<commit_before>/*
 * Creative Commons Copyright 2013 Ursudio <info@ursudio.com>
 * http://www.ursudio.com/
 * Please attribute Ursudio in any production associated with this javascript plugin.
 */

L.TileLayer.WebGLHeatMap = L.Class.extend({

    options: {
        size: 30000, // in meters
        opacity: 1,
		gradientTexture: false,
		alphaRange: 1,
		autoresize: false
    },

    initialize: function (options) {
        this.data = [];
        L.Util.setOptions(this, options);
    },

    onAdd: function (map) {
        this.map = map;
		var mapsize = map.getSize();
		var options = this.options;

		var c = document.createElement("canvas");
		c.id = 'webgl-leaflet';
        c.width = mapsize.x;
        c.height = mapsize.y;
        c.style.opacity = options.opacity;
        c.style.position = 'absolute';

		map.getPanes().overlayPane.appendChild(c);

        this.WebGLHeatMap = createWebGLHeatmap({
			canvas: c,
			gradientTexture: options.gradientTexture,
			alphaRange: [0, options.alphaRange]
		});

        this.canvas = c;

		/* This needs to be fixed somehow. 'moveend' triggers this._plot 3 times for each window resize. */
		map.on("moveend", this._plot, this);

		/* hide layer on zoom, because it doesn't animate zoom */
		map.on("zoomstart", this._hide, this);
		map.on("zoomend", this._show, this);

		if (this.options.autoresize) {
			//a timeout should keep the function from running constantly while resizing
			var timeout;
			var self = this;
			window.onresize = function () {
				timeout = window.setTimeout(function () {
					self.resize();
				}, 250);
			};
		}

        this._plot();
    },

	onRemove: function (map) {
        map.getPanes().overlayPane.removeChild(this.canvas);
        map.off("moveend", this._plot, this);
		map.off("zoomstart", this._hide, this);
		map.off("zoomend", this._show, this);
    },

	_hide : function () {
		this.canvas.style.display = 'none';
	},

	_show : function () {
		this.canvas.style.display = 'block';
	},

	_clear: function () {
		var heatmap = this.WebGLHeatMap;
		heatmap.clear();
		heatmap.display();
	},

	_plot: function () {
		this.active = true;
		var map = this.map;
		var heatmap = this.WebGLHeatMap;
		heatmap.clear();
		L.DomUtil.setPosition(this.canvas, map.latLngToLayerPoint(map.getBounds().getNorthWest()));
        var dataLen = this.data.length;
		if (dataLen) {
            for (var i = 0; i < dataLen; i++) {
				var dataVal = this.data[i],
					latlng = new L.LatLng(dataVal[0], dataVal[1]),
					point = map.latLngToContainerPoint(latlng);
                heatmap.addPoint(
                        Math.floor(point.x),
                        Math.floor(point.y),
                        this._scale(latlng),
						dataVal[2]);
            }
            heatmap.update();
            heatmap.display();
        }
	},

	_scale: function (latlng) {
		// necessary to maintain accurately sized circles
		// to change scale to miles (for example), you will need to convert 40075017 (equatorial circumference of the Earth in metres) to miles
        var lngRadius = (this.options.size / 40075017) * 360 / Math.cos(L.LatLng.DEG_TO_RAD * latlng.lat);
		var latlng2 = new L.LatLng(latlng.lat, latlng.lng - lngRadius);
		var point = this.map.latLngToLayerPoint(latlng);
		var point2 = this.map.latLngToLayerPoint(latlng2);

		return Math.max(Math.round(point.x - point2.x), 1);
    },

	resize: function () {
		//helpful for maps that change sizes
		var mapsize = this.map.getSize();
		this.canvas.width = mapsize.x;
		this.canvas.height = mapsize.y;

		this.WebGLHeatMap.adjustSize();

		this._plot();
	},

    addDataPoint: function (lat, lon, value) {
        this.data.push( [ lat, lon, value / 100 ] );
    },

	setData: function (dataset) {
		// format: [[lat, lon, intensity],...]
		this.data = dataset;
    },

	clearData: function () {
		this.data = [];
	},

	update: function () {
		this._plot();
	}
});

L.TileLayer.webglheatmap = function (options) {
    return new L.TileLayer.WebGLHeatMap(options);
};<commit_msg>added clearTimeout<commit_after>/*
 * Creative Commons Copyright 2013 Ursudio <info@ursudio.com>
 * http://www.ursudio.com/
 * Please attribute Ursudio in any production associated with this javascript plugin.
 */

L.TileLayer.WebGLHeatMap = L.Class.extend({

    options: {
        size: 30000, // in meters
        opacity: 1,
		gradientTexture: false,
		alphaRange: 1,
		autoresize: false
    },

    initialize: function (options) {
        this.data = [];
        L.Util.setOptions(this, options);
    },

    onAdd: function (map) {
        this.map = map;
		var mapsize = map.getSize();
		var options = this.options;

		var c = document.createElement("canvas");
		c.id = 'webgl-leaflet';
        c.width = mapsize.x;
        c.height = mapsize.y;
        c.style.opacity = options.opacity;
        c.style.position = 'absolute';

		map.getPanes().overlayPane.appendChild(c);

        this.WebGLHeatMap = createWebGLHeatmap({
			canvas: c,
			gradientTexture: options.gradientTexture,
			alphaRange: [0, options.alphaRange]
		});

        this.canvas = c;

		/* This needs to be fixed somehow. 'moveend' triggers this._plot 3 times for each window resize. */
		map.on("moveend", this._plot, this);

		/* hide layer on zoom, because it doesn't animate zoom */
		map.on("zoomstart", this._hide, this);
		map.on("zoomend", this._show, this);

		if (this.options.autoresize) {
			//a timeout should keep the function from running constantly while resizing
			var timeout;
			var self = this;
			window.onresize = function () {
				window.clearTimeout(timeout);
				timeout = window.setTimeout(function () {
					self.resize();
				}, 250);
			};
		}

        this._plot();
    },

	onRemove: function (map) {
        map.getPanes().overlayPane.removeChild(this.canvas);
        map.off("moveend", this._plot, this);
		map.off("zoomstart", this._hide, this);
		map.off("zoomend", this._show, this);
    },

	_hide : function () {
		this.canvas.style.display = 'none';
	},

	_show : function () {
		this.canvas.style.display = 'block';
	},

	_clear: function () {
		var heatmap = this.WebGLHeatMap;
		heatmap.clear();
		heatmap.display();
	},

	_plot: function () {
		this.active = true;
		var map = this.map;
		var heatmap = this.WebGLHeatMap;
		heatmap.clear();
		L.DomUtil.setPosition(this.canvas, map.latLngToLayerPoint(map.getBounds().getNorthWest()));
        var dataLen = this.data.length;
		if (dataLen) {
            for (var i = 0; i < dataLen; i++) {
				var dataVal = this.data[i],
					latlng = new L.LatLng(dataVal[0], dataVal[1]),
					point = map.latLngToContainerPoint(latlng);
                heatmap.addPoint(
                        Math.floor(point.x),
                        Math.floor(point.y),
                        this._scale(latlng),
						dataVal[2]);
            }
            heatmap.update();
            heatmap.display();
        }
	},

	_scale: function (latlng) {
		// necessary to maintain accurately sized circles
		// to change scale to miles (for example), you will need to convert 40075017 (equatorial circumference of the Earth in metres) to miles
        var lngRadius = (this.options.size / 40075017) * 360 / Math.cos(L.LatLng.DEG_TO_RAD * latlng.lat);
		var latlng2 = new L.LatLng(latlng.lat, latlng.lng - lngRadius);
		var point = this.map.latLngToLayerPoint(latlng);
		var point2 = this.map.latLngToLayerPoint(latlng2);

		return Math.max(Math.round(point.x - point2.x), 1);
    },

	resize: function () {
		//helpful for maps that change sizes
		var mapsize = this.map.getSize();
		this.canvas.width = mapsize.x;
		this.canvas.height = mapsize.y;

		this.WebGLHeatMap.adjustSize();

		this._plot();
	},

    addDataPoint: function (lat, lon, value) {
        this.data.push( [ lat, lon, value / 100 ] );
    },

	setData: function (dataset) {
		// format: [[lat, lon, intensity],...]
		this.data = dataset;
    },

	clearData: function () {
		this.data = [];
	},

	update: function () {
		this._plot();
	}
});

L.TileLayer.webglheatmap = function (options) {
    return new L.TileLayer.WebGLHeatMap(options);
};<|endoftext|>


<commit_before>var http = require('http');
var util = require('util');
var path = require('path');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var MAX_PORT = 60000;
var curPort = 45000;

function getServer(callback) {
	if (curPort > MAX_PORT) {
		curPort = 40000;
	}

	var server = http.createServer();
	var port = curPort++;
	var next = function() {
		getServer(callback);
	};
	server.on('error', next);
	server.listen(port, function() {
		server.removeListener('error', next);
		callback(server, port);
	});
}

module.exports = function(options, callback) {
	options.getRootCAFile = ca.getRootCAFile;
	options.createCertificate = ca.createCertificate;
	options.storage = new Storage(path.join(options.config.DATA_DIR, '.plugins', options.name));
	if (options.debugMode) {
		var cacheLogs = [];
		console.log = function() {
			var msg = util.format.apply(this, arguments);
			if (cacheLogs) {
				cacheLogs.push(msg);
			} else {
				process.sendData({
					type: 'console.log',
					message: msg
				});
			}
		};
		process.on('data', function(data) {
			if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
				var _cacheLogs = cacheLogs;
				cacheLogs = null;
				_cacheLogs.forEach(function(msg) {
					process.sendData({
						type: 'console.log',
						message: msg
					});
				});
			}
		});
	}

	var execPlugin = require(options.value);
	var port, uiPort, rulesPort, resRulesPort, statusPort, tunnelRulesPort;
	var count = 0;
	var callbackHandler = function() {
		if (--count <= 0) {
			callback(null, {
				port: port,
				uiPort: uiPort,
				rulesPort: rulesPort,
				resRulesPort: resRulesPort,
				statusPort: statusPort,
				tunnelRulesPort: tunnelRulesPort
			});
		}
	};

	var startServer = execPlugin.server || execPlugin;
	if (typeof startServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startServer(server, options);
			port = _port;
			callbackHandler();
		});
	}

	var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
	if (typeof startUIServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startUIServer(server, options);
			uiPort = _port;
			callbackHandler();
		});
	}

	var startRulesServer = execPlugin.rulesServer || execPlugin.reqRulesServer;
	if (typeof startRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startRulesServer(server, options);
			rulesPort = _port;
			callbackHandler();
		});
	}

	var startResRulesServer = execPlugin.resRulesServer;
	if (typeof startResRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startResRulesServer(server, options);
			resRulesPort = _port;
			callbackHandler();
		});
	}

	var startStatusServer = execPlugin.statusServer || execPlugin.stateServer;
	if (typeof startStatusServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startStatusServer(server, options);
			statusPort = _port;
			callbackHandler();
		});
	}

	var startTunnelRulesServer = execPlugin.tunnelRulesServer;
	if (typeof startTunnelRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startTunnelRulesServer(server, options);
			tunnelRulesPort = _port;
			callbackHandler();
		});
	}

	if (!count) {
		callbackHandler();
	}
};

<commit_msg>feat: Add plugin.tunnelServer<commit_after>var http = require('http');
var util = require('util');
var path = require('path');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var MAX_PORT = 60000;
var curPort = 45000;

function getServer(callback) {
	if (curPort > MAX_PORT) {
		curPort = 40000;
	}

	var server = http.createServer();
	var port = curPort++;
	var next = function() {
		getServer(callback);
	};
	server.on('error', next);
	server.listen(port, function() {
		server.removeListener('error', next);
		callback(server, port);
	});
}

module.exports = function(options, callback) {
	options.getRootCAFile = ca.getRootCAFile;
	options.createCertificate = ca.createCertificate;
	options.storage = new Storage(path.join(options.config.DATA_DIR, '.plugins', options.name));
	if (options.debugMode) {
		var cacheLogs = [];
		console.log = function() {
			var msg = util.format.apply(this, arguments);
			if (cacheLogs) {
				cacheLogs.push(msg);
			} else {
				process.sendData({
					type: 'console.log',
					message: msg
				});
			}
		};
		process.on('data', function(data) {
			if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
				var _cacheLogs = cacheLogs;
				cacheLogs = null;
				_cacheLogs.forEach(function(msg) {
					process.sendData({
						type: 'console.log',
						message: msg
					});
				});
			}
		});
	}

	var execPlugin = require(options.value);
	var port, uiPort, rulesPort, resRulesPort, statusPort, tunnelRulesPort, tunnelPort;
	var count = 0;
	var callbackHandler = function() {
		if (--count <= 0) {
			callback(null, {
				port: port,
				uiPort: uiPort,
				rulesPort: rulesPort,
				resRulesPort: resRulesPort,
				statusPort: statusPort,
				tunnelRulesPort: tunnelRulesPort,
				tunnelPort: tunnelPort
			});
		}
	};

	var startServer = execPlugin.server || execPlugin;
	if (typeof startServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startServer(server, options);
			port = _port;
			callbackHandler();
		});
	}

	var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
	if (typeof startUIServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startUIServer(server, options);
			uiPort = _port;
			callbackHandler();
		});
	}

	var startRulesServer = execPlugin.rulesServer || execPlugin.reqRulesServer;
	if (typeof startRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startRulesServer(server, options);
			rulesPort = _port;
			callbackHandler();
		});
	}

	var startResRulesServer = execPlugin.resRulesServer;
	if (typeof startResRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startResRulesServer(server, options);
			resRulesPort = _port;
			callbackHandler();
		});
	}

	var startStatusServer = execPlugin.statusServer || execPlugin.stateServer;
	if (typeof startStatusServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startStatusServer(server, options);
			statusPort = _port;
			callbackHandler();
		});
	}

	var startTunnelRulesServer = execPlugin.tunnelRulesServer;
	if (typeof startTunnelRulesServer == 'function') {
		++count;
		getServer(function(server, _port) {
			startTunnelRulesServer(server, options);
			tunnelRulesPort = _port;
			callbackHandler();
		});
	}

	var startTunnelServer = execPlugin.tunnelServer;
  if (typeof startTunnelServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      startTunnelServer(server, options);
      tunnelPort = _port;
      callbackHandler();
    });
  }

	if (!count) {
		callbackHandler();
	}
};

<|endoftext|>


<commit_before>'use strict';

var Http = require('http');
var Nested = /** @type Nested */ require('./bundle/Nested');
var Next = /** @type Next */ require('fist.util.next/Next');
var Runtime = /** @type Runtime */ require('./track/Runtime');
var Server = /** @type Server */ require('./Server');
var Toobusy = /** @type Toobusy */ require('./util/Toobusy');

var caller = require('./util/caller');

/**
 * @class Framework
 * @extends Server
 * */
var Framework = Server.extend(/** @lends Framework.prototype */ {

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @constructs
     * */
    constructor: function () {
        Framework.Parent.apply(this, arguments);

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Array}
         * */
        this._tasks = [];

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Array}
         * */
        this._pends = [];

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Number}
         * */
        this._pending = 0;

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Toobusy}
         * */
        this._toobusy = new Toobusy({
            maxLag: this.params.busyHWM
        });

        /**
         * @public
         * @memberOf {Framework}
         * @property {Object<Function>}
         * */
        this.renderers = Object.create(null);
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    listen: function () {

        var server = Http.createServer(this.getHandler());

        server.listen.apply(server, arguments);
        //  автоматически запускаю инициализацию
        this.ready();
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    ready: function () {

        var i;
        var l;
        var isError = false;
        var length = this._tasks.length;

        this._pending += 1;

        if ( 1 === this._pending ) {
            this.emit('sys:pending');
        }

        function done (err) {

            if ( 2 > arguments.length ) {
                this.emit('sys:error', err);

                return;
            }

            this._pending -= 1;

            if ( 0 === this._pending ) {
                this.emit('sys:ready');

                while ( this._pends.length ) {
                    this._handle(this._pends.shift());
                }
            }
        }

        function ready (err) {

            if ( 2 > arguments.length ) {
                isError = true;
                done.call(this, err);

                return;
            }

            length -= 1;

            if ( 0 === length ) {
                done.call(this, null, null);
            }
        }

        if ( 0 === length ) {
            done.call(this, null, null);

            return;
        }

        for ( i = 0, l = length; i < l; i += 1 ) {

            if ( isError ) {

                break;
            }

            this._tasks[i](ready, this);
        }
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    plug: function () {
        [].forEach.call(arguments, function (plugin) {
            var next = null;
            var self = this;

            this._tasks.push(function (done, ctxt) {

                if ( null === next ) {
                    next = new Next();
                    plugin.call(self, function () {
                        next.args(arguments);
                    });
                }

                next.done(done, ctxt);
            });

        }, this);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @param {Function} func
     * @param {Runtime} track
     * @param {Bundle} bundle
     * @param {Function} done
     * */
    _call: function (func, track, bundle, done) {

        if ( 'function' === typeof func ) {
            caller.callFunc(func, [track, bundle.errors, bundle.result], done);

            return;
        }

        caller.callRet(func, done);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @returns {Nested}
     * */
    _createBundle: function () {

        return new Nested();
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @returns {Runtime}
     * */
    _createTrack: function (req, res) {

        return new Runtime(this, req, res);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @param {Runtime} track
     * */
    _handle: function (track) {

        if ( this._toobusy.busy() ) {
            track.send(503);

            return;
        }

        if ( 0 === this._pending ) {
            Framework.parent._handle.apply(this, arguments);

            return;
        }

        this._pends.push(track);
    }

});

module.exports = Framework;
<commit_msg>rc-dev: refactor plug/ready<commit_after>'use strict';

var Http = require('http');
var Nested = /** @type Nested */ require('./bundle/Nested');
var Next = /** @type Next */ require('fist.util.next/Next');
var Runtime = /** @type Runtime */ require('./track/Runtime');
var Server = /** @type Server */ require('./Server');
var Toobusy = /** @type Toobusy */ require('./util/Toobusy');

var caller = require('./util/caller');

/**
 * @class Framework
 * @extends Server
 * */
var Framework = Server.extend(/** @lends Framework.prototype */ {

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @constructs
     * */
    constructor: function () {
        Framework.Parent.apply(this, arguments);

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Array}
         * */
        this._tasks = [];

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Array}
         * */
        this._pends = [];

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Number}
         * */
        this._pending = 0;

        /**
         * @protected
         * @memberOf {Framework}
         * @property
         * @type {Toobusy}
         * */
        this._toobusy = new Toobusy({
            maxLag: this.params.busyHWM
        });

        /**
         * @public
         * @memberOf {Framework}
         * @property {Object<Function>}
         * */
        this.renderers = Object.create(null);
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    listen: function () {

        var server = Http.createServer(this.getHandler());

        server.listen.apply(server, arguments);
        //  автоматически запускаю инициализацию
        this.ready();
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    ready: function () {

        var isError = false;
        var length = this._tasks.length;

        this._pending += 1;

        if ( 1 === this._pending ) {
            this.emit('sys:pending');
        }

        function done (err) {

            if ( 2 > arguments.length ) {
                this.emit('sys:error', err);

                return;
            }

            this._pending -= 1;

            if ( 0 === this._pending ) {
                this.emit('sys:ready');

                while ( this._pends.length ) {
                    this._handle(this._pends.shift());
                }
            }
        }

        function ready (err) {

            if ( 2 > arguments.length ) {
                isError = true;
                done.call(this, err);

                return;
            }

            length -= 1;

            if ( 0 === length ) {
                done.call(this, null, null);
            }
        }

        if ( 0 === length ) {
            done.call(this, null, null);

            return;
        }

        while ( this._tasks.length ) {

            if ( isError ) {

                break;
            }

            this._tasks.shift()(ready, this);
        }
    },

    /**
     * @public
     * @memberOf {Framework}
     * @method
     * */
    plug: function () {
        Array.prototype.forEach.call(arguments, function (plugin) {
            var self = this;

            this._tasks.push(function (done, ctxt) {

                var next = new Next();

                plugin.call(self, function () {
                    next.args(arguments);
                });

                next.done(done, ctxt);
            });

        }, this);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @param {Function} func
     * @param {Runtime} track
     * @param {Bundle} bundle
     * @param {Function} done
     * */
    _call: function (func, track, bundle, done) {

        if ( 'function' === typeof func ) {
            caller.callFunc(func, [track, bundle.errors, bundle.result], done);

            return;
        }

        caller.callRet(func, done);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @returns {Nested}
     * */
    _createBundle: function () {

        return new Nested();
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @returns {Runtime}
     * */
    _createTrack: function (req, res) {

        return new Runtime(this, req, res);
    },

    /**
     * @protected
     * @memberOf {Framework}
     * @method
     *
     * @param {Runtime} track
     * */
    _handle: function (track) {

        if ( this._toobusy.busy() ) {
            track.send(503);

            return;
        }

        if ( 0 === this._pending ) {
            Framework.parent._handle.apply(this, arguments);

            return;
        }

        this._pends.push(track);
    }

});

module.exports = Framework;
<|endoftext|>


<commit_before>let path = require('path');
let glob = require('glob');
let webpack = require('webpack');
let Mix = require('laravel-mix').config;
let webpackPlugins = require('laravel-mix').plugins;
let dotenv = require('dotenv')

/*
 |--------------------------------------------------------------------------
 | Load Environment Variables
 |--------------------------------------------------------------------------
 |
 | Load environment variables from .env file. dotenv will never modify
 | any environment variables that have already been set.
 |
 */

dotenv.config({
    path: Mix.Paths.root('.env')
});

/*
 |--------------------------------------------------------------------------
 | Mix Initialization
 |--------------------------------------------------------------------------
 |
 | As our first step, we'll require the project's Laravel Mix file
 | and record the user's requested compilation and build steps.
 | Once those steps have been recorded, we may get to work.
 |
 */

Mix.initialize();

/*
 |--------------------------------------------------------------------------
 | Webpack Context
 |--------------------------------------------------------------------------
 |
 | This prop will determine the appropriate context, when running Webpack.
 | Since you have the option of publishing this webpack.config.js file
 | to your project root, we will dynamically set the path for you.
 |
 */

module.exports.context = Mix.Paths.root();

/*
 |--------------------------------------------------------------------------
 | Webpack Entry
 |--------------------------------------------------------------------------
 |
 | We'll first specify the entry point for Webpack. By default, we'll
 | assume a single bundled file, but you may call Mix.extract()
 | to make a separate bundle specifically for vendor libraries.
 |
 */

module.exports.entry = Mix.entry().get();

/*
 |--------------------------------------------------------------------------
 | Webpack Output
 |--------------------------------------------------------------------------
 |
 | Webpack naturally requires us to specify our desired output path and
 | file name. We'll simply echo what you passed to with Mix.js().
 | Note that, for Mix.version(), we'll properly hash the file.
 |
 */

module.exports.output = Mix.output();

/*
 |--------------------------------------------------------------------------
 | Rules
 |--------------------------------------------------------------------------
 |
 | Webpack rules allow us to register any number of loaders and options.
 | Out of the box, we'll provide a handful to get you up and running
 | as quickly as possible, though feel free to add to this list.
 |
 */

let plugins = [];

if (Mix.options.extractVueStyles) {
    var vueExtractTextPlugin = Mix.vueExtractTextPlugin();

    plugins.push(vueExtractTextPlugin);
}

let rules = [
    {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
            loaders: Mix.options.extractVueStyles ? {
                js: 'babel-loader' + Mix.babelConfig(),
                scss: vueExtractTextPlugin.extract({
                    use: 'css-loader!sass-loader',
                    fallback: 'vue-style-loader'
                }),
                sass: vueExtractTextPlugin.extract({
                    use: 'css-loader!sass-loader?indentedSyntax',
                    fallback: 'vue-style-loader'
                }),
                less: vueExtractTextPlugin.extract({
                    use: 'css-loader!less-loader',
                    fallback: 'vue-style-loader'
                }),
                stylus: vueExtractTextPlugin.extract({
                    use: 'css-loader!stylus-loader?paths[]=node_modules',
                    fallback: 'vue-style-loader'
                }),
                css: vueExtractTextPlugin.extract({
                    use: 'css-loader',
                    fallback: 'vue-style-loader'
                })
            }: {
                js: 'babel-loader' + Mix.babelConfig(),
                scss: 'vue-style-loader!css-loader!sass-loader',
                sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
                less: 'vue-style-loader!css-loader!less-loader',
                stylus: 'vue-style-loader!css-loader!stylus-loader?paths[]=node_modules'
            },

            postcss: Mix.options.postCss,

            preLoaders: Mix.options.vue.preLoaders,

            postLoaders: Mix.options.vue.postLoaders
        }
    },

    {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'babel-loader' + Mix.babelConfig()
    },

    {
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader']
    },

    {
        test: /\.s[ac]ss$/,
        exclude: Mix.preprocessors.map(preprocessor => preprocessor.test()),
        loaders: ['style-loader', 'css-loader', 'sass-loader']
    },

    {
        test: /\.html$/,
        loaders: ['html-loader']
    },

    {
        test: /\.(png|jpe?g|gif)$/,
        loaders: [
            {
                loader: 'file-loader',
                options: {
                    name: path => {
                        if (! /node_modules|bower_components/.test(path)) {
                            return 'images/[name].[ext]?[hash]';
                        }

                        return 'images/vendor/' + path
                            .replace(/\\/g, '/')
                            .replace(
                                /((.*(node_modules|bower_components))|images|image|img|assets)\//g, ''
                            ) + '?[hash]';
                    },
                    publicPath: Mix.options.resourceRoot
                }
            },
            'img-loader'
        ]
    },

    {
        test: /\.(woff2?|ttf|eot|svg|otf)$/,
        loader: 'file-loader',
        options: {
            name: path => {
                if (! /node_modules|bower_components/.test(path)) {
                    return 'fonts/[name].[ext]?[hash]';
                }

                return 'fonts/vendor/' + path
                    .replace(/\\/g, '/')
                    .replace(
                        /((.*(node_modules|bower_components))|fonts|font|assets)\//g, ''
                    ) + '?[hash]';
            },
            publicPath: Mix.options.resourceRoot
        }
    },

    {
        test: /\.(cur|ani)$/,
        loader: 'file-loader',
        options: {
            name: '[name].[ext]?[hash]',
            publicPath: Mix.options.resourceRoot
        }
    }
];

if (Mix.preprocessors) {
    Mix.preprocessors.forEach(preprocessor => {
        rules.push(preprocessor.rules());

        plugins.push(preprocessor.extractPlugin);
    });
}

module.exports.module = { rules };

/*
 |--------------------------------------------------------------------------
 | Resolve
 |--------------------------------------------------------------------------
 |
 | Here, we may set any options/aliases that affect Webpack's resolving
 | of modules. To begin, we will provide the necessary Vue alias to
 | load the Vue common library. You may delete this, if needed.
 |
 */

module.exports.resolve = {
    extensions: ['*', '.js', '.jsx', '.vue'],

    alias: {
        'vue$': 'vue/dist/vue.common.js'
    }
};

/*
 |--------------------------------------------------------------------------
 | Stats
 |--------------------------------------------------------------------------
 |
 | By default, Webpack spits a lot of information out to the terminal,
 | each you time you compile. Let's keep things a bit more minimal
 | and hide a few of those bits and pieces. Adjust as you wish.
 |
 */

module.exports.stats = {
    hash: false,
    version: false,
    timings: false,
    children: false,
    errors: false
};

process.noDeprecation = true;

module.exports.performance = { hints: false };

/*
 |--------------------------------------------------------------------------
 | Devtool
 |--------------------------------------------------------------------------
 |
 | Sourcemaps allow us to access our original source code within the
 | browser, even if we're serving a bundled script or stylesheet.
 | You may activate sourcemaps, by adding Mix.sourceMaps().
 |
 */

module.exports.devtool = Mix.options.sourcemaps;

/*
 |--------------------------------------------------------------------------
 | Webpack Dev Server Configuration
 |--------------------------------------------------------------------------
 |
 | If you want to use that flashy hot module replacement feature, then
 | we've got you covered. Here, we'll set some basic initial config
 | for the Node server. You very likely won't want to edit this.
 |
 */
module.exports.devServer = {
    historyApiFallback: true,
    noInfo: true,
    compress: true,
    quiet: true
};

/*
 |--------------------------------------------------------------------------
 | Plugins
 |--------------------------------------------------------------------------
 |
 | Lastly, we'll register a number of plugins to extend and configure
 | Webpack. To get you started, we've included a handful of useful
 | extensions, for versioning, OS notifications, and much more.
 |
 */

plugins.push(
    new webpack.ProvidePlugin(Mix.autoload || {}),

    new webpackPlugins.FriendlyErrorsWebpackPlugin({ clearConsole: Mix.options.clearConsole }),

    new webpackPlugins.StatsWriterPlugin({
        filename: 'mix-manifest.json',
        transform: Mix.manifest.transform.bind(Mix.manifest),
    }),

    new webpack.LoaderOptionsPlugin({
        minimize: Mix.inProduction,
        options: {
            postcss: Mix.options.postCss,
            context: __dirname,
            output: { path: './' }
        }
    })
);

if (Mix.browserSync) {
    plugins.push(
        new webpackPlugins.BrowserSyncPlugin(
            Object.assign({
                host: 'localhost',
                port: 3000,
                proxy: 'app.dev',
                files: [
                    'app/**/*.php',
                    'resources/views/**/*.php',
                    'public/js/**/*.js',
                    'public/css/**/*.css'
                ]
            }, Mix.browserSync),
            {
                reload: false
            }
        )
    );
}

if (Mix.options.notifications) {
    plugins.push(
        new webpackPlugins.WebpackNotifierPlugin({
            title: 'Laravel Mix',
            alwaysNotify: true,
            contentImage: Mix.Paths.root('node_modules/laravel-mix/icons/laravel.png')
        })
    );
}

if (Mix.copy) {
    Mix.copy.forEach(copy => {
        plugins.push(
            new webpackPlugins.CopyWebpackPlugin([copy])
        );
    });
}

if (Mix.entry().hasExtractions()) {
    plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
            names: Mix.entry().getExtractions(),
            minChunks: Infinity
        })
    );
}

if (Mix.options.versioning) {
    plugins.push(
        new webpack[Mix.inProduction ? 'HashedModuleIdsPlugin': 'NamedModulesPlugin'](),
        new webpackPlugins.WebpackChunkHashPlugin()
    );
}

if (Mix.options.purifyCss) {
    let PurifyCSSPlugin = require('purifycss-webpack');

    // By default, we'll scan all Blade and Vue files in our project.
    let paths = glob.sync(Mix.Paths.root('resources/views/**/*.blade.php')).concat(
        Mix.entry().scripts.reduce((carry, js) => {
            return carry.concat(glob.sync(js.base + '/**/*.vue'));
        }, [])
    );

    plugins.push(new PurifyCSSPlugin(
        Object.assign({ paths }, Mix.options.purifyCss, { minimize: Mix.inProduction })
    ));
}

if (Mix.inProduction && Mix.options.uglify) {
    plugins.push(
        new webpack.optimize.UglifyJsPlugin(Mix.options.uglify)
    );
}

plugins.push(
    new webpack.DefinePlugin(
        Mix.definitions({
            NODE_ENV: Mix.inProduction
                ? 'production'
                : ( process.env.NODE_ENV || 'development' )
        })
    ),

    new webpackPlugins.WebpackOnBuildPlugin(
        stats => global.events.fire('build', stats)
    )
);

if (! Mix.entry().hasScripts()) {
    plugins.push(new webpackPlugins.MockEntryPlugin(Mix.output().path));
}

module.exports.plugins = plugins;

/*
 |--------------------------------------------------------------------------
 | Mix Finalizing
 |--------------------------------------------------------------------------
 |
 | Now that we've declared the entirety of our Webpack configuration, the
 | final step is to scan for any custom configuration in the Mix file.
 | If mix.webpackConfig() is called, we'll merge it in, and build!
 |
 */

if (Mix.webpackConfig) {
    module.exports = require('webpack-merge').smart(
        module.exports, Mix.webpackConfig
    );
}
<commit_msg>Conditionally exclude<commit_after>let path = require('path');
let glob = require('glob');
let webpack = require('webpack');
let Mix = require('laravel-mix').config;
let webpackPlugins = require('laravel-mix').plugins;
let dotenv = require('dotenv')

/*
 |--------------------------------------------------------------------------
 | Load Environment Variables
 |--------------------------------------------------------------------------
 |
 | Load environment variables from .env file. dotenv will never modify
 | any environment variables that have already been set.
 |
 */

dotenv.config({
    path: Mix.Paths.root('.env')
});

/*
 |--------------------------------------------------------------------------
 | Mix Initialization
 |--------------------------------------------------------------------------
 |
 | As our first step, we'll require the project's Laravel Mix file
 | and record the user's requested compilation and build steps.
 | Once those steps have been recorded, we may get to work.
 |
 */

Mix.initialize();

/*
 |--------------------------------------------------------------------------
 | Webpack Context
 |--------------------------------------------------------------------------
 |
 | This prop will determine the appropriate context, when running Webpack.
 | Since you have the option of publishing this webpack.config.js file
 | to your project root, we will dynamically set the path for you.
 |
 */

module.exports.context = Mix.Paths.root();

/*
 |--------------------------------------------------------------------------
 | Webpack Entry
 |--------------------------------------------------------------------------
 |
 | We'll first specify the entry point for Webpack. By default, we'll
 | assume a single bundled file, but you may call Mix.extract()
 | to make a separate bundle specifically for vendor libraries.
 |
 */

module.exports.entry = Mix.entry().get();

/*
 |--------------------------------------------------------------------------
 | Webpack Output
 |--------------------------------------------------------------------------
 |
 | Webpack naturally requires us to specify our desired output path and
 | file name. We'll simply echo what you passed to with Mix.js().
 | Note that, for Mix.version(), we'll properly hash the file.
 |
 */

module.exports.output = Mix.output();

/*
 |--------------------------------------------------------------------------
 | Rules
 |--------------------------------------------------------------------------
 |
 | Webpack rules allow us to register any number of loaders and options.
 | Out of the box, we'll provide a handful to get you up and running
 | as quickly as possible, though feel free to add to this list.
 |
 */

let plugins = [];

if (Mix.options.extractVueStyles) {
    var vueExtractTextPlugin = Mix.vueExtractTextPlugin();

    plugins.push(vueExtractTextPlugin);
}

let rules = [
    {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
            loaders: Mix.options.extractVueStyles ? {
                js: 'babel-loader' + Mix.babelConfig(),
                scss: vueExtractTextPlugin.extract({
                    use: 'css-loader!sass-loader',
                    fallback: 'vue-style-loader'
                }),
                sass: vueExtractTextPlugin.extract({
                    use: 'css-loader!sass-loader?indentedSyntax',
                    fallback: 'vue-style-loader'
                }),
                less: vueExtractTextPlugin.extract({
                    use: 'css-loader!less-loader',
                    fallback: 'vue-style-loader'
                }),
                stylus: vueExtractTextPlugin.extract({
                    use: 'css-loader!stylus-loader?paths[]=node_modules',
                    fallback: 'vue-style-loader'
                }),
                css: vueExtractTextPlugin.extract({
                    use: 'css-loader',
                    fallback: 'vue-style-loader'
                })
            }: {
                js: 'babel-loader' + Mix.babelConfig(),
                scss: 'vue-style-loader!css-loader!sass-loader',
                sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
                less: 'vue-style-loader!css-loader!less-loader',
                stylus: 'vue-style-loader!css-loader!stylus-loader?paths[]=node_modules'
            },

            postcss: Mix.options.postCss,

            preLoaders: Mix.options.vue.preLoaders,

            postLoaders: Mix.options.vue.postLoaders
        }
    },

    {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'babel-loader' + Mix.babelConfig()
    },

    {
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader']
    },

    {
        test: /\.html$/,
        loaders: ['html-loader']
    },

    {
        test: /\.(png|jpe?g|gif)$/,
        loaders: [
            {
                loader: 'file-loader',
                options: {
                    name: path => {
                        if (! /node_modules|bower_components/.test(path)) {
                            return 'images/[name].[ext]?[hash]';
                        }

                        return 'images/vendor/' + path
                            .replace(/\\/g, '/')
                            .replace(
                                /((.*(node_modules|bower_components))|images|image|img|assets)\//g, ''
                            ) + '?[hash]';
                    },
                    publicPath: Mix.options.resourceRoot
                }
            },
            'img-loader'
        ]
    },

    {
        test: /\.(woff2?|ttf|eot|svg|otf)$/,
        loader: 'file-loader',
        options: {
            name: path => {
                if (! /node_modules|bower_components/.test(path)) {
                    return 'fonts/[name].[ext]?[hash]';
                }

                return 'fonts/vendor/' + path
                    .replace(/\\/g, '/')
                    .replace(
                        /((.*(node_modules|bower_components))|fonts|font|assets)\//g, ''
                    ) + '?[hash]';
            },
            publicPath: Mix.options.resourceRoot
        }
    },

    {
        test: /\.(cur|ani)$/,
        loader: 'file-loader',
        options: {
            name: '[name].[ext]?[hash]',
            publicPath: Mix.options.resourceRoot
        }
    }
];

let sassRule = {
    test: /\.s[ac]ss$/,
    loaders: ['style-loader', 'css-loader', 'sass-loader']
};

if (Mix.preprocessors) {
    sassRule.exclude = Mix.preprocessors.map(preprocessor => preprocessor.test());
}

rules.push(sassRule);

if (Mix.preprocessors) {
    Mix.preprocessors.forEach(preprocessor => {
        rules.push(preprocessor.rules());

        plugins.push(preprocessor.extractPlugin);
    });
}

module.exports.module = { rules };

/*
 |--------------------------------------------------------------------------
 | Resolve
 |--------------------------------------------------------------------------
 |
 | Here, we may set any options/aliases that affect Webpack's resolving
 | of modules. To begin, we will provide the necessary Vue alias to
 | load the Vue common library. You may delete this, if needed.
 |
 */

module.exports.resolve = {
    extensions: ['*', '.js', '.jsx', '.vue'],

    alias: {
        'vue$': 'vue/dist/vue.common.js'
    }
};

/*
 |--------------------------------------------------------------------------
 | Stats
 |--------------------------------------------------------------------------
 |
 | By default, Webpack spits a lot of information out to the terminal,
 | each you time you compile. Let's keep things a bit more minimal
 | and hide a few of those bits and pieces. Adjust as you wish.
 |
 */

module.exports.stats = {
    hash: false,
    version: false,
    timings: false,
    children: false,
    errors: false
};

process.noDeprecation = true;

module.exports.performance = { hints: false };

/*
 |--------------------------------------------------------------------------
 | Devtool
 |--------------------------------------------------------------------------
 |
 | Sourcemaps allow us to access our original source code within the
 | browser, even if we're serving a bundled script or stylesheet.
 | You may activate sourcemaps, by adding Mix.sourceMaps().
 |
 */

module.exports.devtool = Mix.options.sourcemaps;

/*
 |--------------------------------------------------------------------------
 | Webpack Dev Server Configuration
 |--------------------------------------------------------------------------
 |
 | If you want to use that flashy hot module replacement feature, then
 | we've got you covered. Here, we'll set some basic initial config
 | for the Node server. You very likely won't want to edit this.
 |
 */
module.exports.devServer = {
    historyApiFallback: true,
    noInfo: true,
    compress: true,
    quiet: true
};

/*
 |--------------------------------------------------------------------------
 | Plugins
 |--------------------------------------------------------------------------
 |
 | Lastly, we'll register a number of plugins to extend and configure
 | Webpack. To get you started, we've included a handful of useful
 | extensions, for versioning, OS notifications, and much more.
 |
 */

plugins.push(
    new webpack.ProvidePlugin(Mix.autoload || {}),

    new webpackPlugins.FriendlyErrorsWebpackPlugin({ clearConsole: Mix.options.clearConsole }),

    new webpackPlugins.StatsWriterPlugin({
        filename: 'mix-manifest.json',
        transform: Mix.manifest.transform.bind(Mix.manifest),
    }),

    new webpack.LoaderOptionsPlugin({
        minimize: Mix.inProduction,
        options: {
            postcss: Mix.options.postCss,
            context: __dirname,
            output: { path: './' }
        }
    })
);

if (Mix.browserSync) {
    plugins.push(
        new webpackPlugins.BrowserSyncPlugin(
            Object.assign({
                host: 'localhost',
                port: 3000,
                proxy: 'app.dev',
                files: [
                    'app/**/*.php',
                    'resources/views/**/*.php',
                    'public/js/**/*.js',
                    'public/css/**/*.css'
                ]
            }, Mix.browserSync),
            {
                reload: false
            }
        )
    );
}

if (Mix.options.notifications) {
    plugins.push(
        new webpackPlugins.WebpackNotifierPlugin({
            title: 'Laravel Mix',
            alwaysNotify: true,
            contentImage: Mix.Paths.root('node_modules/laravel-mix/icons/laravel.png')
        })
    );
}

if (Mix.copy) {
    Mix.copy.forEach(copy => {
        plugins.push(
            new webpackPlugins.CopyWebpackPlugin([copy])
        );
    });
}

if (Mix.entry().hasExtractions()) {
    plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
            names: Mix.entry().getExtractions(),
            minChunks: Infinity
        })
    );
}

if (Mix.options.versioning) {
    plugins.push(
        new webpack[Mix.inProduction ? 'HashedModuleIdsPlugin': 'NamedModulesPlugin'](),
        new webpackPlugins.WebpackChunkHashPlugin()
    );
}

if (Mix.options.purifyCss) {
    let PurifyCSSPlugin = require('purifycss-webpack');

    // By default, we'll scan all Blade and Vue files in our project.
    let paths = glob.sync(Mix.Paths.root('resources/views/**/*.blade.php')).concat(
        Mix.entry().scripts.reduce((carry, js) => {
            return carry.concat(glob.sync(js.base + '/**/*.vue'));
        }, [])
    );

    plugins.push(new PurifyCSSPlugin(
        Object.assign({ paths }, Mix.options.purifyCss, { minimize: Mix.inProduction })
    ));
}

if (Mix.inProduction && Mix.options.uglify) {
    plugins.push(
        new webpack.optimize.UglifyJsPlugin(Mix.options.uglify)
    );
}

plugins.push(
    new webpack.DefinePlugin(
        Mix.definitions({
            NODE_ENV: Mix.inProduction
                ? 'production'
                : ( process.env.NODE_ENV || 'development' )
        })
    ),

    new webpackPlugins.WebpackOnBuildPlugin(
        stats => global.events.fire('build', stats)
    )
);

if (! Mix.entry().hasScripts()) {
    plugins.push(new webpackPlugins.MockEntryPlugin(Mix.output().path));
}

module.exports.plugins = plugins;

/*
 |--------------------------------------------------------------------------
 | Mix Finalizing
 |--------------------------------------------------------------------------
 |
 | Now that we've declared the entirety of our Webpack configuration, the
 | final step is to scan for any custom configuration in the Mix file.
 | If mix.webpackConfig() is called, we'll merge it in, and build!
 |
 */

if (Mix.webpackConfig) {
    module.exports = require('webpack-merge').smart(
        module.exports, Mix.webpackConfig
    );
}
<|endoftext|>


<commit_before>// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (C) 2014-2015  Daiki Ueno <dueno@src.gnome.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const Lang = imports.lang;
const Params = imports.params;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const Pango = imports.gi.Pango;
const PangoCairo = imports.gi.PangoCairo;
const Gc = imports.gi.Gc;
const Main = imports.main;
const Util = imports.util;

const BASELINE_OFFSET = 0.85;
const CELLS_PER_ROW = 5;
const CELL_SIZE = 100;

function getCellSize(fontDescription) {
    if (fontDescription == null
        || fontDescription.get_size() == 0)
        return CELL_SIZE;
    return fontDescription.get_size() * 2 / Pango.SCALE;
}

const CharacterListRow = new Lang.Class({
    Name: 'CharacterListRow',
    Extends: GObject.Object,

    _init: function(params) {
        let filtered = Params.filter(params, { characters: null,
                                               fontDescription: null });
        params = Params.fill(params, {});
        this.parent(params);
        this._characters = filtered.characters;
        this._fontDescription = filtered.fontDescription;
    },

    draw: function(cr, x, y, width, height) {
        let layout = PangoCairo.create_layout(cr);
        layout.set_font_description(this._fontDescription);

        // Draw baseline.
        // FIXME: Pick the baseline color from CSS.
        cr.setSourceRGBA(114.0 / 255.0, 159.0 / 255.0, 207.0 / 255.0, 1.0);
        cr.setLineWidth(0.5);
        cr.moveTo(x, y + BASELINE_OFFSET * height);
        cr.relLineTo(width, 0);
        cr.stroke();
        cr.setSourceRGBA(0.0, 0.0, 0.0, 1.0);

        // Draw characters.  Do centering and attach to the baseline.
        let cellSize = getCellSize(this._fontDescription);
        for (let i in this._characters) {
            layout.set_text(this._characters[i], -1);
            let layoutBaseline = layout.get_baseline() / Pango.SCALE;
            let [logicalRect, inkRect] = layout.get_extents();
            cr.moveTo(x + cellSize * i - logicalRect.x / Pango.SCALE +
                      (cellSize - logicalRect.width / Pango.SCALE) / 2,
                      y + BASELINE_OFFSET * height - layoutBaseline);
            PangoCairo.show_layout(cr, layout);
        }
    },
});

const CharacterListWidget = new Lang.Class({
    Name: 'CharacterListWidget',
    Extends: Gtk.DrawingArea,
    Signals: {
        'character-selected': { param_types: [ GObject.TYPE_STRING ] }
    },
    Properties: {
        'font': GObject.ParamSpec.string(
            'font', '', '',
            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
            'Cantarell 100')
    },

    get font() {
        return this._font;
    },

    set font(v) {
        let fontDescription = Pango.FontDescription.from_string(v);
        if (fontDescription.get_size() == 0)
            fontDescription.set_size(CELL_SIZE);

        fontDescription.set_absolute_size(
            fontDescription.get_size() / 2 * Pango.SCALE);

        if (this._fontDescription &&
            fontDescription.equal(this._fontDescription))
            return;

        this._font = v;
        this._fontDescription = fontDescription;
        if (this._characters) {
            this.setCharacters(this._characters);
            this.show_all();
        }
    },

    _init: function(params) {
        params = Params.fill(params, {});
        this.parent(params);
        this.get_style_context().add_class('character-list');
        this._cellsPerRow = CELLS_PER_ROW;
        this._font = null;
        this._fontDescription = null;
        this._rows = [];
        Main.settings.bind('font', this, 'font', Gio.SettingsBindFlags.DEFAULT);
        this.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
    },

    vfunc_button_press_event: function(event) {
        let allocation = this.get_allocation();
        let cellSize = getCellSize(this._fontDescription);
        let x = Math.floor(event.x / cellSize);
        let y = Math.floor(event.y / cellSize);
        let index = y * this._cellsPerRow + x;
        if (index < this._characters.length)
            this.emit('character-selected', this._characters[index]);
    },

    vfunc_get_preferred_height: function() {
        let [minWidth, natWidth] = this.vfunc_get_preferred_width();
        return this.vfunc_get_preferred_height_for_width(minWidth);
    },

    vfunc_get_preferred_height_for_width: function(width) {
        let height = this._rows.length * getCellSize(this._fontDescription);
        return [height, height];
    },

    vfunc_get_preferred_width: function() {
        return this.vfunc_get_preferred_width_for_height(0);
    },

    vfunc_get_preferred_width_for_height: function(height) {
        let width = this._cellsPerRow * getCellSize(this._fontDescription);
        return [width, width];
    },

    vfunc_size_allocate: function(allocation) {
        this.parent(allocation);

        let cellSize = getCellSize(this._fontDescription);
        let cellsPerRow = Math.floor(allocation.width / cellSize);
        if (cellsPerRow != this._cellsPerRow) {
            // Reflow if the number of cells per row has changed.
            this._cellsPerRow = cellsPerRow;
            this.setCharacters(this._characters);
        }
    },

    _createCharacterListRow: function(characters) {
        let row = new CharacterListRow({
            characters: characters,
            fontDescription: this._fontDescription
        });
        return row;
    },

    setCharacters: function(characters) {
        this._rows = [];
        this._characters = characters;

        if (characters.length == 0)
            return;

        let start = 0, stop = 1;
        for (; stop <= characters.length; stop++) {
            if (stop % this._cellsPerRow == 0) {
                let rowCharacters = characters.slice(start, stop);
                let row = this._createCharacterListRow(rowCharacters);
                this._rows.push(row);
                start = stop;
            }
        }
        if (start != stop - 1) {
            let rowCharacters = characters.slice(start, stop);
            let row = this._createCharacterListRow(rowCharacters);
            this._rows.push(row);
        }

        this.queue_draw();
    },

    vfunc_draw: function(cr) {
        // Clear the canvas.
        // FIXME: Pick the background color from CSS.
        cr.setSourceRGBA(1, 1, 1, 1);
        cr.paint();
        cr.setSourceRGBA(0, 0, 0, 1);

        // Use device coordinates directly, since PangoCairo doesn't
        // work well with scaled matrix:
        // https://bugzilla.gnome.org/show_bug.cgi?id=700592
        let allocation = this.get_allocation();

        let cellSize = getCellSize(this._fontDescription);
        for (let index in this._rows) {
            this._rows[index].draw(cr, 0, index * cellSize,
                                   allocation.width, cellSize);
        }
    }
});
<commit_msg>characterList: Optimize rendering when scrolling<commit_after>// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (C) 2014-2015  Daiki Ueno <dueno@src.gnome.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const Lang = imports.lang;
const Params = imports.params;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const Pango = imports.gi.Pango;
const PangoCairo = imports.gi.PangoCairo;
const Gc = imports.gi.Gc;
const Main = imports.main;
const Util = imports.util;

const BASELINE_OFFSET = 0.85;
const CELLS_PER_ROW = 5;
const CELL_SIZE = 100;

function getCellSize(fontDescription) {
    if (fontDescription == null
        || fontDescription.get_size() == 0)
        return CELL_SIZE;
    return fontDescription.get_size() * 2 / Pango.SCALE;
}

const CharacterListRow = new Lang.Class({
    Name: 'CharacterListRow',
    Extends: GObject.Object,

    _init: function(params) {
        let filtered = Params.filter(params, { characters: null,
                                               fontDescription: null });
        params = Params.fill(params, {});
        this.parent(params);
        this._characters = filtered.characters;
        this._fontDescription = filtered.fontDescription;
    },

    draw: function(cr, x, y, width, height) {
        let layout = PangoCairo.create_layout(cr);
        layout.set_font_description(this._fontDescription);

        // Draw baseline.
        // FIXME: Pick the baseline color from CSS.
        cr.setSourceRGBA(114.0 / 255.0, 159.0 / 255.0, 207.0 / 255.0, 1.0);
        cr.setLineWidth(0.5);
        cr.moveTo(x, y + BASELINE_OFFSET * height);
        cr.relLineTo(width, 0);
        cr.stroke();
        cr.setSourceRGBA(0.0, 0.0, 0.0, 1.0);

        // Draw characters.  Do centering and attach to the baseline.
        let cellSize = getCellSize(this._fontDescription);
        for (let i in this._characters) {
            layout.set_text(this._characters[i], -1);
            let layoutBaseline = layout.get_baseline() / Pango.SCALE;
            let [logicalRect, inkRect] = layout.get_extents();
            cr.moveTo(x + cellSize * i - logicalRect.x / Pango.SCALE +
                      (cellSize - logicalRect.width / Pango.SCALE) / 2,
                      y + BASELINE_OFFSET * height - layoutBaseline);
            PangoCairo.show_layout(cr, layout);
        }
    },
});

const CharacterListWidget = new Lang.Class({
    Name: 'CharacterListWidget',
    Extends: Gtk.DrawingArea,
    Signals: {
        'character-selected': { param_types: [ GObject.TYPE_STRING ] }
    },
    Properties: {
        'font': GObject.ParamSpec.string(
            'font', '', '',
            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
            'Cantarell 100')
    },

    get font() {
        return this._font;
    },

    set font(v) {
        let fontDescription = Pango.FontDescription.from_string(v);
        if (fontDescription.get_size() == 0)
            fontDescription.set_size(CELL_SIZE);

        fontDescription.set_absolute_size(
            fontDescription.get_size() / 2 * Pango.SCALE);

        if (this._fontDescription &&
            fontDescription.equal(this._fontDescription))
            return;

        this._font = v;
        this._fontDescription = fontDescription;
        if (this._characters) {
            this.setCharacters(this._characters);
            this.show_all();
        }
    },

    _init: function(params) {
        params = Params.fill(params, {});
        this.parent(params);
        this.get_style_context().add_class('character-list');
        this._cellsPerRow = CELLS_PER_ROW;
        this._font = null;
        this._fontDescription = null;
        this._rows = [];
        Main.settings.bind('font', this, 'font', Gio.SettingsBindFlags.DEFAULT);
        this.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
    },

    vfunc_button_press_event: function(event) {
        let allocation = this.get_allocation();
        let cellSize = getCellSize(this._fontDescription);
        let x = Math.floor(event.x / cellSize);
        let y = Math.floor(event.y / cellSize);
        let index = y * this._cellsPerRow + x;
        if (index < this._characters.length)
            this.emit('character-selected', this._characters[index]);
    },

    vfunc_get_preferred_height: function() {
        let [minWidth, natWidth] = this.vfunc_get_preferred_width();
        return this.vfunc_get_preferred_height_for_width(minWidth);
    },

    vfunc_get_preferred_height_for_width: function(width) {
        let height = this._rows.length * getCellSize(this._fontDescription);
        return [height, height];
    },

    vfunc_get_preferred_width: function() {
        return this.vfunc_get_preferred_width_for_height(0);
    },

    vfunc_get_preferred_width_for_height: function(height) {
        let width = this._cellsPerRow * getCellSize(this._fontDescription);
        return [width, width];
    },

    vfunc_size_allocate: function(allocation) {
        this.parent(allocation);

        let cellSize = getCellSize(this._fontDescription);
        let cellsPerRow = Math.floor(allocation.width / cellSize);
        if (cellsPerRow != this._cellsPerRow) {
            // Reflow if the number of cells per row has changed.
            this._cellsPerRow = cellsPerRow;
            this.setCharacters(this._characters);
        }
    },

    _createCharacterListRow: function(characters) {
        let row = new CharacterListRow({
            characters: characters,
            fontDescription: this._fontDescription
        });
        return row;
    },

    setCharacters: function(characters) {
        this._rows = [];
        this._characters = characters;

        if (characters.length == 0)
            return;

        let start = 0, stop = 1;
        for (; stop <= characters.length; stop++) {
            if (stop % this._cellsPerRow == 0) {
                let rowCharacters = characters.slice(start, stop);
                let row = this._createCharacterListRow(rowCharacters);
                this._rows.push(row);
                start = stop;
            }
        }
        if (start != stop - 1) {
            let rowCharacters = characters.slice(start, stop);
            let row = this._createCharacterListRow(rowCharacters);
            this._rows.push(row);
        }

        this.queue_draw();
    },

    vfunc_draw: function(cr) {
        // Clear the canvas.
        // FIXME: Pick the background color from CSS.
        cr.setSourceRGBA(1, 1, 1, 1);
        cr.paint();
        cr.setSourceRGBA(0, 0, 0, 1);

        // Use device coordinates directly, since PangoCairo doesn't
        // work well with scaled matrix:
        // https://bugzilla.gnome.org/show_bug.cgi?id=700592
        let allocation = this.get_allocation();

        // Redraw rows within the clipped region.
        let [x1, y1, x2, y2] = cr.clipExtents();
        let cellSize = getCellSize(this._fontDescription);
        let start = Math.floor(y1 / cellSize);
        let end = Math.ceil(y2 / cellSize);
        for (let index = start; index < end; index++) {
            this._rows[index].draw(cr, 0, index * cellSize,
                                   allocation.width, cellSize);
        }
    }
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Jefferson Lee
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA

function MusicKeyboard() {

    var keyboard = document.getElementById("keyboard");
    var keyboardHolder = document.getElementById("keyboardHolder");
    var firstOctave = document.getElementById("firstOctave");
    var firstNote = document.getElementById("firstNote");
    var secondOctave = document.getElementById("secondOctave");
    var secondNote = document.getElementById("secondNote");
    var whiteKeys = document.getElementById("white");
    var blackKeys = document.getElementById("black");

    var whiteNoteEnums = ['C','D','E','F','G','A','B'];
    var blackNoteEnums = ['C♯', 'D♯', 'SKIP', 'F♯', 'G♯', 'A♯', 'SKIP'];

    var selected = [];

    //configure defaults
    changeKeys();

    function changeKeys() {
        whiteKeys.innerHTML = "";
        blackKeys.innerHTML = "";
        var note1 = firstNote.value;
        var note2 = secondNote.value;
        var oct1 = firstOctave.value;
        var oct2 = secondOctave.value;
        //sanity checks
        //missing values
        if(note1 == "" || note2 == "" || oct1 == "" || oct2 == "") {
            return;
        }
        //2nd octave < 1st octave
        if(oct2 < oct1) {
            var tmp = oct1;
            oct1 = oct2;
            oct2 = tmp;
        }
        //2nd key comes before 1st key on same octave
        if(oct1 == oct2 && whiteNoteEnums.indexOf(note1) > whiteNoteEnums.indexOf(note2)) {
            var tmp = note1;
            note1 = note2;
            note2 = tmp;
        }
        //reflect sanity changes
        firstNote.value = note1;
        secondNote.value = note2;
        firstOctave.value = oct1;
        secondOctave.value = oct2;

        //first key -> end of first octave
        for(var j = whiteNoteEnums.indexOf(note1); j < whiteNoteEnums.length; j++) {
            whiteKeys.innerHTML += ""+whiteNoteEnums[j]+oct1+"	";
        }
        for(var j = whiteNoteEnums.indexOf(note1); j < blackNoteEnums.length; j++) {
            if(blackNoteEnums[j] != 'SKIP') {
                blackKeys.innerHTML += ""+blackNoteEnums[j]+oct1+"	";
            }
            else {
                blackKeys.innerHTML += "	";
            }
        }
        //2nd octave -> second to last octave
        for(var i = parseInt(oct1)+1; i <= oct2-1; i++) {
            for(var j = 0; j < whiteNoteEnums.length; j++) {
                whiteKeys.innerHTML += ""+whiteNoteEnums[j]+i+"	";
            }
            for(var j = 0; j < blackNoteEnums.length; j++) {
                if(blackNoteEnums[j] != 'SKIP') {
                    blackKeys.innerHTML += ""+blackNoteEnums[j]+i+"	";
                }
                else {
                blackKeys.innerHTML += "	";
                }
            }
        }
        //last octave -> last key
        for(var j = 0; j < whiteNoteEnums.indexOf(note2)+1; j++) {
            whiteKeys.innerHTML += ""+whiteNoteEnums[j]+oct2+"	";
        }
        for(var j = 0; j < whiteNoteEnums.indexOf(note2); j++) {
            if(blackNoteEnums[j] != 'SKIP') {
                blackKeys.innerHTML += ""+blackNoteEnums[j]+oct2+"	";
            }
            else {
                blackKeys.innerHTML += "	";
            }
        }
        //assign the IDs (for clearing)
        for(var i = 0; i < whiteKeys.children.length; i++) {
            whiteKeys.children[i].id = whiteKeys.children[i].textContent;
        }
        for(var i = 0; i < blackKeys.children.length; i++) {
            blackKeys.children[i].id = blackKeys.children[i].textContent;
        }
        console.log(note1+oct1+"-"+note2+oct2);
    }

    keyboard.addEventListener("mousedown", function (e) {
        var target = e.target;
        if(target.tagName == "TD") {
            if((target.style.backgroundColor != "lightblue") && (target.style.backgroundColor != "pink")) {
                selected.push(target.textContent);
                if(target.parentNode == whiteKeys) {
                    target.style.backgroundColor = "lightblue";
                }
                else {
                    target.style.backgroundColor = "pink";
                }
            }
            else if(target.style.backgroundColor == "lightblue" || target.style.backgroundColor == "pink")  {
                selected.splice(selected.indexOf(target.textContent), 1);
                if(target.parentNode == whiteKeys) {
                    target.style.backgroundColor = "white";
                }
                else {
                    target.style.backgroundColor = "black";
                }
            }
            handleKeyboard(target.textContent);
        }
    });

    function deselect () {
        for(var i = 0; i < selected.length; i++) {
            var tmp = document.getElementById(selected[i]);
            if (tmp.parentElement == whiteKeys) {
                tmp.style.backgroundColor = "white";
            }
            else {
                tmp.style.backgroundColor = "black";
            }
        }
        selected = [];
    }

    var keyboardShown = true;

    function toggleKeyboard() {
        if(keyboardShown) {
            keyboardHolder.style.display = 'none';
        }
        else {
            keyboardHolder.style.display = 'inline';
        }

        keyboardShown = !keyboardShown;
    }

    function handleKeyboard (key) {
        //Tone can't do special sharps, need # isntead of ♯
        console.log('IIIInside handleKeyboard function... value of key ' +key);
        var noSharp = key;
        if(key[1] == "♯") {
            noSharp = key[0]+"#"+key[2];
        }

        synth.triggerAttackRelease(noSharp, "8n");
    }

    function handleKeyboardPitches (pitches) {
        console.log("generating keyboard pitches for: " + pitches);
        //copy and pasted from matrix.saveMatrix (), the original had too many array dimensions so the forloop has been removed
        var noteConversion = {'C': 'do', 'D': 're', 'E': 'mi', 'F': 'fa', 'G': 'sol', 'A': 'la', 'B': 'ti', 'R': 'rest'};
        var newStack = [[0, ["action", {"collapsed":false}], 100, 100, [null, 1, null, null]], [1, ["text", {"value":"chunk"}], 0, 0, [0]]];
        var endOfStackIdx = 0;
        for (var i = 0; i < pitches.length; i++) {
        // We want all of the notes in a column.
        // console.log(this.notesToPlay[i]);
            var note = pitches[i].slice(0);

        // Add the Note block and its value
            var idx = newStack.length;
            newStack.push([idx, 'note', 0, 0, [endOfStackIdx, idx + 1, idx + 2, null]]);
            var n = newStack[idx][4].length;
            if (i == 0) {  // the action block
                newStack[endOfStackIdx][4][n - 2] = idx;
            }
            else { // the previous note block
                newStack[endOfStackIdx][4][n - 1] = idx;
            }
            var endOfStackIdx = idx;
            newStack.push([idx + 1, ['number', {'value': "4"}], 0, 0, [idx]]);
            // Add the pitch blocks to the Note block
            var  notePitch = note.substring(0,note.length-1);  //i.e. D or D# not D#1
            var thisBlock = idx + 2;

            // We need to point to the previous note or pitch block.
            var previousBlock = idx;  // Note block

            // The last connection in last pitch block is null.
            var lastConnection = null;

            newStack.push([thisBlock, 'pitch', 0, 0, [previousBlock, thisBlock + 1, thisBlock + 2, lastConnection]]);
            if(['♯', '♭'].indexOf(notePitch[1]) != -1) {
                newStack.push([thisBlock + 1, ['solfege', {'value': noteConversion[note[0]] + note[1]}], 0, 0, [thisBlock]]);
                newStack.push([thisBlock + 2, ['number', {'value': note[note.length-1]}], 0, 0, [thisBlock]]);
            }
            else {
                newStack.push([thisBlock + 1, ['solfege', {'value': noteConversion[notePitch[0]]}], 0, 0, [thisBlock]]);
                newStack.push([thisBlock + 2, ['number', {'value': note[note.length-1]}], 0, 0, [thisBlock]]);
            }
        }
        console.log(newStack);
        blocks.logo.blocks.loadNewBlocks(newStack);
    }

       //not sure if there's a synth already in the program
       var synth = new Tone.Synth().toMaster();
}
     <commit_msg>color on keyPress<commit_after>// Copyright (c) 2015 Jefferson Lee
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA

function MusicKeyboard() {

    var keyboard = document.getElementById("keyboard");
    var keyboardHolder = document.getElementById("keyboardHolder");
    var firstOctave = document.getElementById("firstOctave");
    var firstNote = document.getElementById("firstNote");
    var secondOctave = document.getElementById("secondOctave");
    var secondNote = document.getElementById("secondNote");
    var whiteKeys = document.getElementById("white");
    var blackKeys = document.getElementById("black");

    var whiteNoteEnums = ['C','D','E','F','G','A','B'];
    var blackNoteEnums = ['C♯', 'D♯', 'SKIP', 'F♯', 'G♯', 'A♯', 'SKIP'];

    var selected = [];

    //configure defaults
    changeKeys();

    function changeKeys() {
        whiteKeys.innerHTML = "";
        blackKeys.innerHTML = "";
        var note1 = firstNote.value;
        var note2 = secondNote.value;
        var oct1 = firstOctave.value;
        var oct2 = secondOctave.value;
        //sanity checks
        //missing values
        if(note1 == "" || note2 == "" || oct1 == "" || oct2 == "") {
            return;
        }
        //2nd octave < 1st octave
        if(oct2 < oct1) {
            var tmp = oct1;
            oct1 = oct2;
            oct2 = tmp;
        }
        //2nd key comes before 1st key on same octave
        if(oct1 == oct2 && whiteNoteEnums.indexOf(note1) > whiteNoteEnums.indexOf(note2)) {
            var tmp = note1;
            note1 = note2;
            note2 = tmp;
        }
        //reflect sanity changes
        firstNote.value = note1;
        secondNote.value = note2;
        firstOctave.value = oct1;
        secondOctave.value = oct2;

        //first key -> end of first octave
        for(var j = whiteNoteEnums.indexOf(note1); j < whiteNoteEnums.length; j++) {
            whiteKeys.innerHTML += ""+whiteNoteEnums[j]+oct1+"	";
        }
        for(var j = whiteNoteEnums.indexOf(note1); j < blackNoteEnums.length; j++) {
            if(blackNoteEnums[j] != 'SKIP') {
                blackKeys.innerHTML += ""+blackNoteEnums[j]+oct1+"	";
            }
            else {
                blackKeys.innerHTML += "	";
            }
        }
        //2nd octave -> second to last octave
        for(var i = parseInt(oct1)+1; i <= oct2-1; i++) {
            for(var j = 0; j < whiteNoteEnums.length; j++) {
                whiteKeys.innerHTML += ""+whiteNoteEnums[j]+i+"	";
            }
            for(var j = 0; j < blackNoteEnums.length; j++) {
                if(blackNoteEnums[j] != 'SKIP') {
                    blackKeys.innerHTML += ""+blackNoteEnums[j]+i+"	";
                }
                else {
                blackKeys.innerHTML += "	";
                }
            }
        }
        //last octave -> last key
        for(var j = 0; j < whiteNoteEnums.indexOf(note2)+1; j++) {
            whiteKeys.innerHTML += ""+whiteNoteEnums[j]+oct2+"	";
        }
        for(var j = 0; j < whiteNoteEnums.indexOf(note2); j++) {
            if(blackNoteEnums[j] != 'SKIP') {
                blackKeys.innerHTML += ""+blackNoteEnums[j]+oct2+"	";
            }
            else {
                blackKeys.innerHTML += "	";
            }
        }
        //assign the IDs (for clearing)
        for(var i = 0; i < whiteKeys.children.length; i++) {
            whiteKeys.children[i].id = whiteKeys.children[i].textContent;
        }
        for(var i = 0; i < blackKeys.children.length; i++) {
            blackKeys.children[i].id = blackKeys.children[i].textContent;
        }
        console.log(note1+oct1+"-"+note2+oct2);
    }

    keyboard.addEventListener("mousedown", function (e) {
        var target = e.target;
        if(target.tagName == "TD") {
            if((target.style.backgroundColor != "lightgrey") && (target.style.backgroundColor != "rgb(72,72,72)")) {
                selected.push(target.textContent);
                if(target.parentNode == whiteKeys) {
                    target.style.backgroundColor = "lightgrey";
                }
                else {
                    target.style.backgroundColor = "rgb(72,72,72)";
                }
            }
            handleKeyboard(target.textContent);
        }
    });

    keyboard.addEventListener("mouseup", function (f) {
        var target = f.target;
        if(target.tagName == "TD") {
                if(target.parentNode == whiteKeys) {
                    target.style.backgroundColor = "white";
                }
                else {
                    target.style.backgroundColor = "black";
                }
        }

    });

    function deselect () {
        for(var i = 0; i < selected.length; i++) {
            var tmp = document.getElementById(selected[i]);
            if (tmp.parentElement == whiteKeys) {
                tmp.style.backgroundColor = "white";
            }
            else {
                tmp.style.backgroundColor = "black";
            }
        }
        selected = [];
    }

    var keyboardShown = true;

    function toggleKeyboard() {
        if(keyboardShown) {
            keyboardHolder.style.display = 'none';
        }
        else {
            keyboardHolder.style.display = 'inline';
        }

        keyboardShown = !keyboardShown;
    }

    function handleKeyboard (key) {
        //Tone can't do special sharps, need # isntead of ♯
        console.log('IIIInside handleKeyboard function... value of key ' +key);
        var noSharp = key;
        if(key[1] == "♯") {
            noSharp = key[0]+"#"+key[2];
        }

        synth.triggerAttackRelease(noSharp, "8n");
    }

    function handleKeyboardPitches (pitches) {
        console.log("generating keyboard pitches for: " + pitches);
        //copy and pasted from matrix.saveMatrix (), the original had too many array dimensions so the forloop has been removed
        var noteConversion = {'C': 'do', 'D': 're', 'E': 'mi', 'F': 'fa', 'G': 'sol', 'A': 'la', 'B': 'ti', 'R': 'rest'};
        var newStack = [[0, ["action", {"collapsed":false}], 100, 100, [null, 1, null, null]], [1, ["text", {"value":"chunk"}], 0, 0, [0]]];
        var endOfStackIdx = 0;
        for (var i = 0; i < pitches.length; i++) {
        // We want all of the notes in a column.
        // console.log(this.notesToPlay[i]);
            var note = pitches[i].slice(0);

        // Add the Note block and its value
            var idx = newStack.length;
            newStack.push([idx, 'note', 0, 0, [endOfStackIdx, idx + 1, idx + 2, null]]);
            var n = newStack[idx][4].length;
            if (i == 0) {  // the action block
                newStack[endOfStackIdx][4][n - 2] = idx;
            }
            else { // the previous note block
                newStack[endOfStackIdx][4][n - 1] = idx;
            }
            var endOfStackIdx = idx;
            newStack.push([idx + 1, ['number', {'value': "4"}], 0, 0, [idx]]);
            // Add the pitch blocks to the Note block
            var  notePitch = note.substring(0,note.length-1);  //i.e. D or D# not D#1
            var thisBlock = idx + 2;

            // We need to point to the previous note or pitch block.
            var previousBlock = idx;  // Note block

            // The last connection in last pitch block is null.
            var lastConnection = null;

            newStack.push([thisBlock, 'pitch', 0, 0, [previousBlock, thisBlock + 1, thisBlock + 2, lastConnection]]);
            if(['♯', '♭'].indexOf(notePitch[1]) != -1) {
                newStack.push([thisBlock + 1, ['solfege', {'value': noteConversion[note[0]] + note[1]}], 0, 0, [thisBlock]]);
                newStack.push([thisBlock + 2, ['number', {'value': note[note.length-1]}], 0, 0, [thisBlock]]);
            }
            else {
                newStack.push([thisBlock + 1, ['solfege', {'value': noteConversion[notePitch[0]]}], 0, 0, [thisBlock]]);
                newStack.push([thisBlock + 2, ['number', {'value': note[note.length-1]}], 0, 0, [thisBlock]]);
            }
        }
        console.log(newStack);
        blocks.logo.blocks.loadNewBlocks(newStack);
    }

       //not sure if there's a synth already in the program
       var synth = new Tone.Synth().toMaster();
}
     <|endoftext|>


<commit_before>/* global CustomEvent */

require('./vr-register-element');

var requestInterval = require('request-interval');
var THREE = require('../lib/three');

var VRObject = require('./core/vr-object');

module.exports = document.registerElement(
  'vr-cursor',
  {
    prototype: Object.create(
      VRObject.prototype, {
        createdCallback: {
          value: function () {
            var material = this.getMaterial();
            var geometry = this.getGeometry();
            this.object3D = new THREE.Mesh(geometry, material);
            this.raycaster = new THREE.Raycaster();
            this.attachEventListeners();
            this.pollForHoverIntersections();
            this.load();
          }
        },

        attachEventListeners: {
          value: function () {
            document.addEventListener('mousedown', this.onMouseDown.bind(this), false);
            this.addEventListener('click', this.handleClick.bind(this));
          }
        },

        pollForHoverIntersections: {
          value: function () {
            this.intersectedEls = {};
            requestInterval(100, this.handleMouseEnter.bind(this));
          }
        },

        onMouseDown: {
          value: function () {
            this.click();
          }
        },

        attributeChangedCallback: {
          value: function () {
            var material = this.getMaterial();
            var geometry = this.getGeometry();
            this.object3D.geometry = geometry;
            this.object3D.material = material;
          }
        },

        getGeometry: {
          value: function () {
            var radius = this.getAttribute('radius', 10);
            var geometryId = this.getAttribute('geometry');
            var geometryEl = geometryId ? document.querySelector('#' + geometryId) : undefined;
            return (geometryEl && geometryEl.geometry) || new THREE.SphereGeometry(radius, 64, 40);
          }
        },

        getMaterial: {
          value: function () {
            var materialId = this.getAttribute('material');
            var materialEl = materialId ? document.querySelector('#' + materialId) : undefined;
            return (materialEl && materialEl.material) || new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide});
          }
        },

        intersect: {
          value: function (objects) {
            var raycaster = this.raycaster;
            var cursor = this.object3D;
            var cursorPosition = cursor.position.clone();
            cursor.localToWorld(cursorPosition);
            var parent = this.parentNode.object3D;
            var parentPosition = parent.position.clone();
            parent.localToWorld(parentPosition);
            var direction = cursorPosition.sub(parentPosition).normalize();
            raycaster.set(parentPosition, direction);
            return raycaster.intersectObjects(objects, true);
          }
        },

        handleClick: {
          value: function () {
            var scene = this.sceneEl.object3D;
            var intersectedObjs = this.intersect(scene.children);
            intersectedObjs.forEach(function (obj) {
              obj.object.el.click();
            });
          }
        },

        handleMouseEnter: {
          value: function () {
            // Eventually we can support all types of custom mouse events à la jQuery's mouse events:
            // https://api.jquery.com/category/events/mouse-events/
            var scene = this.sceneEl.object3D;
            var intersectedObjs = this.intersect(scene.children);
            intersectedObjs.forEach(this.handleIntersection.bind(this));
            Object.keys(this.intersectedEls).forEach(this.emitMouseEvents.bind(this));
          }
        },

        handleIntersection: {
          value: function (obj) {
            var el = obj.object.el;
            var id = obj.object.id;
            this.intersectedEls[id] = this.intersectedEls[id] || {el: el, firedHover: false};
            this.intersectedEls[id].justIntersected = true;
          }
        },

        emitMouseEvents: {
          value: function (id) {
            var obj = this.intersectedEls[id];
            var el = obj.el;

            if (obj.justIntersected) {
              if (!obj.firedHover) {
                el.dispatchEvent(new CustomEvent('mouseenter'));
                obj.firedHover = true;
              }
              obj.justIntersected = false;
            } else {
              el.dispatchEvent(new CustomEvent('mouseleave'));
              delete this.intersectedEls[id];
            }
          }
        }
      })
  }
);
<commit_msg>emit click/hover events for first intersection Fixes #174<commit_after>/* global CustomEvent */

require('./vr-register-element');

var requestInterval = require('request-interval');
var THREE = require('../lib/three');

var VRObject = require('./core/vr-object');

module.exports = document.registerElement(
  'vr-cursor',
  {
    prototype: Object.create(
      VRObject.prototype, {
        createdCallback: {
          value: function () {
            var material = this.getMaterial();
            var geometry = this.getGeometry();
            geometry.computeBoundingBox();
            var bb = geometry.boundingBox;
            this.cursorBias = bb.max.z - bb.min.z + 0.001;
            this.object3D = new THREE.Mesh(geometry, material);
            this.raycaster = new THREE.Raycaster();
            this.intersectedEls = {};
            this.attachEventListeners();
            this.pollForHoverIntersections();
            this.load();
          }
        },

        attachEventListeners: {
          value: function () {
            document.addEventListener('mousedown', this.onMouseDown.bind(this), false);
            this.addEventListener('click', this.handleClick.bind(this));
          }
        },

        pollForHoverIntersections: {
          value: function () {
            requestInterval(100, this.handleMouseEnter.bind(this));
          }
        },

        onMouseDown: {
          value: function () {
            this.click();
          }
        },

        attributeChangedCallback: {
          value: function () {
            var material = this.getMaterial();
            var geometry = this.getGeometry();
            this.object3D.geometry = geometry;
            this.object3D.material = material;
          }
        },

        getGeometry: {
          value: function () {
            var radius = this.getAttribute('radius', 10);
            var geometryId = this.getAttribute('geometry');
            var geometryEl = geometryId ? document.querySelector('#' + geometryId) : undefined;
            return (geometryEl && geometryEl.geometry) || new THREE.SphereGeometry(radius, 64, 40);
          }
        },

        getMaterial: {
          value: function () {
            var materialId = this.getAttribute('material');
            var materialEl = materialId ? document.querySelector('#' + materialId) : undefined;
            return (materialEl && materialEl.material) || new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide});
          }
        },

        intersect: {
          value: function (objects) {
            var raycaster = this.raycaster;
            var cursor = this.object3D;
            var cursorPosition = cursor.position.clone();
            cursor.localToWorld(cursorPosition);
            var parent = this.parentNode.object3D;
            var parentPosition = parent.position.clone();
            parent.localToWorld(parentPosition);
            var direction = cursorPosition.sub(parentPosition).normalize();
            // Offset the ray to start past the cursor, so its geometry is
            // not intersected.
            parentPosition.add(cursorPosition);
            parentPosition.sub(new THREE.Vector3(0, 0, this.cursorBias));
            raycaster.set(parentPosition, direction);
            return raycaster.intersectObjects(objects, true);
          }
        },

        handleClick: {
          value: function () {
            var scene = this.sceneEl.object3D;
            var intersectedObjs = this.intersect(scene.children);
            if (intersectedObjs.length) {
              intersectedObjs[0].object.el.click();
            }
          }
        },

        handleMouseEnter: {
          value: function () {
            // Eventually we can support all types of custom mouse events à la jQuery's mouse events:
            // https://api.jquery.com/category/events/mouse-events/
            var scene = this.sceneEl.object3D;
            var intersectedObjs = this.intersect(scene.children);
            if (intersectedObjs.length) {
              this.handleIntersection(intersectedObjs[0]);
            }
            Object.keys(this.intersectedEls).forEach(this.emitMouseEvents.bind(this));
          }
        },

        handleIntersection: {
          value: function (obj) {
            var el = obj.object.el;
            var id = obj.object.id;
            this.intersectedEls[id] = this.intersectedEls[id] || {el: el, firedHover: false};
            this.intersectedEls[id].justIntersected = true;
          }
        },

        emitMouseEvents: {
          value: function (id) {
            var obj = this.intersectedEls[id];
            var el = obj.el;

            if (obj.justIntersected) {
              if (!obj.firedHover) {
                el.dispatchEvent(new CustomEvent('mouseenter'));
                obj.firedHover = true;
              }
              obj.justIntersected = false;
            } else {
              el.dispatchEvent(new CustomEvent('mouseleave'));
              delete this.intersectedEls[id];
            }
          }
        }
      })
  }
);
<|endoftext|>