<commit_before>function TiledCanvas (canvas, settings) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');

    this.leftTopX = 0;
    this.leftTopY = 0;
    this.zoom = 1; // 2 = two times zoomed in

    this.affecting = [[0, 0], [0, 0]];
    this.chunks = {};
    // this.chunks[chunkX][chunkY] is a context or 'empty'

    this.settings = this.normalizeDefaults(settings, this.defaultSettings);
    this.contextQueue = [];
    this.context = this.createContext();
    this.lastClear = Date.now();
}

TiledCanvas.prototype.MIN_INACTIVE_UNLOAD_TIME = 10 * 1000;

TiledCanvas.prototype.defaultSettings = {
    chunkSize: 1024,                      // The size of the chunks in pixels
    fadeTime: 500,                       // Fade time for the loading animation
    maxLoadedChunks: 100                 // We'll try never loading more than this amount of chunks if possible
};

TiledCanvas.prototype.cloneObject = function (obj) {
	var clone = {};
	for (var k in obj) {
		if (typeof obj[k] === "object" && !(obj[k] instanceof Array)) {
			clone[k] = this.cloneObject(obj[k]);
		} else {
			clone[k] = obj[k]
		}
	}
	return clone;
};

TiledCanvas.prototype.normalizeDefaults = function normalizeDefaults (target, defaults) {
	target = target || {};
	var normalized = this.cloneObject(target);
	for (var k in defaults) {
		if (typeof defaults[k] === "object" && !(defaults[k] instanceof Array)) {
			normalized[k] = this.normalizeDefaults(target[k] || {}, defaults[k]);
		} else {
			normalized[k] = target[k] || defaults[k];
		}
	}
	return normalized;
};

// Function that schedules one redraw, if you call this twice
// within the same frame, or twice before a redraw is done, only one redraw
// will actually be executed
TiledCanvas.prototype.redrawOnce = function redrawOnce () {
    if (!this._redrawTimeout)
        this._redrawTimeout = requestAnimationFrame(this.redraw.bind(this, false));
};

TiledCanvas.prototype.redraw = function redraw (noclear) {
	cancelAnimationFrame(this._redrawTimeout);
	delete this._redrawTimeout;

    if (!noclear) {
        this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

        this.ctx.save();
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);
        this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
        this.ctx.restore();
    }
	
    var startChunkX = Math.floor(this.leftTopX / this.settings.chunkSize),
        endChunkX   = Math.ceil((this.leftTopX + this.canvas.width / this.zoom) / this.settings.chunkSize),
        startChunkY = Math.floor(this.leftTopY / this.settings.chunkSize),
        endChunkY   = Math.ceil((this.leftTopY + this.canvas.height / this.zoom) / this.settings.chunkSize);
    
    for (var chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
        for (var chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
            this.drawChunk(chunkX, chunkY);
        }
    }
};

TiledCanvas.prototype.drawChunk = function drawChunk (chunkX, chunkY) {
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY]) {
        if (this.chunks[chunkX][chunkY] == "empty") return;
	
	this.chunks[chunkX][chunkY].lastDrawn = Date.now();

        this.ctx.drawImage(this.chunks[chunkX][chunkY].canvas, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);

        // If this chunk got recently added we want a fade effect
        if (this.chunks[chunkX][chunkY].addedTime) {
            var deltaAdded = Date.now() - this.chunks[chunkX][chunkY].addedTime;
            this.ctx.globalAlpha = Math.max(0, 1 - deltaAdded / this.settings.fadeTime);
 
            if (deltaAdded > this.settings.fadeTime)
                delete this.chunks[chunkX][chunkY].addedTime;

            // Force a redraw to avoid optimization of not drawing
            this.redrawOnce();

            // If we have a loading image we should fade from that instead of transparency
            if (this.loadingImage) {
                var originalwidth = this.settings.chunkSize * this.zoom;
                var width = originalwidth * this.ctx.globalAlpha;
                this.ctx.drawImage(this.loadingImage,
                    ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom + (originalwidth - width) / 2,
                    ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom + (originalwidth - width) / 2,
                    width,
                    width);
            }

            this.ctx.globalAlpha = 1;
        }
    } else if(typeof this.requestUserChunk == "function") {
        this.requestChunk(chunkX, chunkY);
        if (this.loadingImage) {
            this.ctx.drawImage(this.loadingImage, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);
        }
    }
};

TiledCanvas.prototype.goto = function goto (x, y) {
    this.leftTopX = x;
    this.leftTopY = y;
    this.redrawOnce();
};

TiledCanvas.prototype.relativeZoom = function relativeZoom (zoom) {
    this.zoom *= zoom;
    this.redrawOnce();
};

TiledCanvas.prototype.absoluteZoom = function absoluteZoom (zoom) {
    this.zoom = zoom;
    this.redrawOnce();
};

TiledCanvas.prototype.execute = function execute () {
    this.executeNoRedraw();
    this.redrawOnce();
};<commit_msg>Add a function to draw specific regions to a canvas<commit_after>function TiledCanvas (canvas, settings) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');

    this.leftTopX = 0;
    this.leftTopY = 0;
    this.zoom = 1; // 2 = two times zoomed in

    this.affecting = [[0, 0], [0, 0]];
    this.chunks = {};
    // this.chunks[chunkX][chunkY] is a context or 'empty'

    this.settings = this.normalizeDefaults(settings, this.defaultSettings);
    this.contextQueue = [];
    this.context = this.createContext();
    this.lastClear = Date.now();
}

TiledCanvas.prototype.MIN_INACTIVE_UNLOAD_TIME = 10 * 1000;

TiledCanvas.prototype.defaultSettings = {
    chunkSize: 1024,                      // The size of the chunks in pixels
    fadeTime: 500,                       // Fade time for the loading animation
    maxLoadedChunks: 100                 // We'll try never loading more than this amount of chunks if possible
};

TiledCanvas.prototype.cloneObject = function (obj) {
	var clone = {};
	for (var k in obj) {
		if (typeof obj[k] === "object" && !(obj[k] instanceof Array)) {
			clone[k] = this.cloneObject(obj[k]);
		} else {
			clone[k] = obj[k]
		}
	}
	return clone;
};

TiledCanvas.prototype.normalizeDefaults = function normalizeDefaults (target, defaults) {
	target = target || {};
	var normalized = this.cloneObject(target);
	for (var k in defaults) {
		if (typeof defaults[k] === "object" && !(defaults[k] instanceof Array)) {
			normalized[k] = this.normalizeDefaults(target[k] || {}, defaults[k]);
		} else {
			normalized[k] = target[k] || defaults[k];
		}
	}
	return normalized;
};

// Function that schedules one redraw, if you call this twice
// within the same frame, or twice before a redraw is done, only one redraw
// will actually be executed
TiledCanvas.prototype.redrawOnce = function redrawOnce () {
    if (!this._redrawTimeout)
        this._redrawTimeout = requestAnimationFrame(this.redraw.bind(this, false));
};

TiledCanvas.prototype.redraw = function redraw (noclear) {
	cancelAnimationFrame(this._redrawTimeout);
	delete this._redrawTimeout;

    if (!noclear) {
        this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

        this.ctx.save();
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);
        this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
        this.ctx.restore();
    }
	
    var startChunkX = Math.floor(this.leftTopX / this.settings.chunkSize),
        endChunkX   = Math.ceil((this.leftTopX + this.canvas.width / this.zoom) / this.settings.chunkSize),
        startChunkY = Math.floor(this.leftTopY / this.settings.chunkSize),
        endChunkY   = Math.ceil((this.leftTopY + this.canvas.height / this.zoom) / this.settings.chunkSize);
    
    for (var chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
        for (var chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
            this.drawChunk(chunkX, chunkY);
        }
    }
};

TiledCanvas.prototype.drawChunk = function drawChunk (chunkX, chunkY) {
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY]) {
        if (this.chunks[chunkX][chunkY] == "empty") return;
	
	this.chunks[chunkX][chunkY].lastDrawn = Date.now();

        this.ctx.drawImage(this.chunks[chunkX][chunkY].canvas, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);

        // If this chunk got recently added we want a fade effect
        if (this.chunks[chunkX][chunkY].addedTime) {
            var deltaAdded = Date.now() - this.chunks[chunkX][chunkY].addedTime;
            this.ctx.globalAlpha = Math.max(0, 1 - deltaAdded / this.settings.fadeTime);
 
            if (deltaAdded > this.settings.fadeTime)
                delete this.chunks[chunkX][chunkY].addedTime;

            // Force a redraw to avoid optimization of not drawing
            this.redrawOnce();

            // If we have a loading image we should fade from that instead of transparency
            if (this.loadingImage) {
                var originalwidth = this.settings.chunkSize * this.zoom;
                var width = originalwidth * this.ctx.globalAlpha;
                this.ctx.drawImage(this.loadingImage,
                    ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom + (originalwidth - width) / 2,
                    ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom + (originalwidth - width) / 2,
                    width,
                    width);
            }

            this.ctx.globalAlpha = 1;
        }
    } else if(typeof this.requestUserChunk == "function") {
        this.requestChunk(chunkX, chunkY);
        if (this.loadingImage) {
            this.ctx.drawImage(this.loadingImage, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);
        }
    }
};

TiledCanvas.prototype.drawToCanvas = function drawToCanvas (canvas, from, to) {		
	var minX = Math.min(from[0], to[0]),
	    maxX = Math.max(from[0], to[0]),
	    minY = Math.min(from[1], to[1]),
	    maxY = Math.max(from[1], to[1]);

	var width = maxX minX,
	    height = maxY minY;	

	var startChunkX = Math.floor(minX / this.settings.chunkSize),
	    endChunkX   = Math.ceil((minX + width) / this.settings.chunkSize),
	    startChunkY = Math.floor(minY / this.settings.chunkSize),
	    endChunkY   = Math.ceil((maxY + height) / this.settings.chunkSize);	

	var ctx = canvas.getContext("2d");

	for (var chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
		for (var chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
			if (this.chunks[chunkX] && this.chunks[chunkX][chunkY] && this.chunks[chunkX][chunkY] !== "empty") {
				ctx.drawImage(
					this.chunks[chunkX][chunkY].canvas,
					chunkX * this.settings.chunkSize minX,
					chunkY * this.settings.chunkSize minY
				);
			}
		}
	}
};

TiledCanvas.prototype.goto = function goto (x, y) {
    this.leftTopX = x;
    this.leftTopY = y;
    this.redrawOnce();
};

TiledCanvas.prototype.relativeZoom = function relativeZoom (zoom) {
    this.zoom *= zoom;
    this.redrawOnce();
};

TiledCanvas.prototype.absoluteZoom = function absoluteZoom (zoom) {
    this.zoom = zoom;
    this.redrawOnce();
};

TiledCanvas.prototype.execute = function execute () {
    this.executeNoRedraw();
    this.redrawOnce();
};
{'fraction_non_alphanumeric': 0.13537262704136616, 'fraction_numerical': 0.0073779325209317745, 'mean_word_length': 2.81651376146789, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '54408', 'n_tokens_mistral': 3956, 'n_tokens_neox': 3770, 'n_words': 840}