<commit_before>'use strict';
var fs = require('fs');
var angular = require('camunda-bpm-sdk-js/vendor/angular');
var Viewer = require('dmn-js/lib/NavigatedViewer');
var Modeler = require('dmn-js/lib/Modeler');
var changeDmnNamespace = require('../../util/change-dmn-namespace');

var template = fs.readFileSync(__dirname + '/cam-widget-dmn-viewer.html', 'utf8');

module.exports = ['$window', function($window) {

  return {
    scope: {
      xml: '=',
      table: '@',
      width: '=',
      height: '=',
      control: '=?',
      editMode: '=',
      showDetails: '=',
      disableNavigation: '=',
      enableDrdNavigation: '=',
      onLoad: '&',
      onClick: '&',
      onDblClick: '&'
    },
    template: template,
    link: function($scope, $element) {
      var canvas;

      $scope.isDrd = false;

        // --- CONTROL FUNCTIONS ---
      $scope.control = $scope.control || {};

      $scope.control.getViewer = function() {
        return viewer;
      };

      $scope.control.getElement = function(elementId) {
        return viewer.get('elementRegistry').get(elementId);
      };

      $scope.loaded = false;
      $scope.control.isLoaded = function() {
        return $scope.loaded;
      };

      $scope.control.highlightRow = function(elementId, className) {
        var tableViewer = viewer.table;
        var elementRegistry = tableViewer.get('elementRegistry');

        tableViewer.get('eventBus').on('row.render', function(event) {
          if (event.data.id === elementId) {
            angular.element(event.gfx).addClass(className);
          }
        });

        tableViewer.get('graphicsFactory').update('row', elementRegistry.get(elementId), elementRegistry.getGraphics(elementId));
      };

      $scope.control.highlightElement = function(id) {
        if (canvas) {
          canvas.addMarker(id, 'highlight');

          $element.find('[data-element-id="' + id + '"]>.djs-outline').attr({
            rx: '14px',
            ry: '14px'
          });
        }
      };

      $scope.control.clearAllElementsHighlight = function() {
        if (canvas) {
          var children = canvas.getRootElement().children;

          children.forEach(function(element) {
            var id = element.id;

            if (canvas.hasMarker(id, 'highlight')) {
              canvas.removeMarker(id, 'highlight');
            }
          });
        }
      };

      $scope.control.clearElementHighlight = function(id) {
        if (canvas) {
          canvas.removeMarker(id, 'highlight');
        }
      };

      $scope.control.isElementHighlighted = function(id) {
        if (canvas) {
          return canvas.hasMarker(id, 'highlight');
        }
      };

      $scope.control.getElements = function() {
        if (canvas) {
          return canvas.getRootElement().children;
        }
      };

      $scope.control.createBadge = function(id, config) {
        if (canvas) {
          addOverlay(id, config)
        }
      };

      function addOverlay(id, config) {
        var overlays = viewer.get('overlays');

        var overlayId = overlays.add(id, {
          position: config.position || {
            bottom: 10,
            right: 10
          },
          show: {
            minZoom: -Infinity,
            maxZoom: +Infinity
          },
          html: config.html
        });

        return overlayId;
      }

      var DmnViewer;

      if(!$scope.editMode) {
        DmnViewer = Viewer;
      } else {
        DmnViewer = Modeler;
      }

      var container = $element[0].querySelector('.table-holder');
      var viewer = new DmnViewer({
        container: container,
        width: $scope.width,
        height: $scope.height,
        hideDetails: !$scope.showDetails,
        disableDrdInteraction: !$scope.enableDrdNavigation
      });

      var xml = null;

      $scope.$watch('xml', function(newValue) {
        if (newValue) {
          xml = newValue;
          renderTable();
        }
      });

      viewer.on('element.click', function(e) {
        $scope.$apply(function() {
          $scope.onClick({element: e.element, $event: e.originalEvent});
        });
      });

      viewer.on('element.dblclick', function(e) {
        $scope.$apply(function() {
          $scope.onDblClick({element: e.element, $event: e.originalEvent});
        });
      });

      viewer.on('view.switch', function(e) {
        $scope.$apply(function() {
          $scope.isDrd = e.fromTable;
        });
      });

      $scope.zoomIn = function() {
        viewer.get('zoomScroll').zoom(1, {
          x: $element[0].offsetWidth / 2,
          y: $element[0].offsetHeight / 2
        });
      };

      $scope.zoomOut = function() {
        viewer.get('zoomScroll').zoom(-1, {
          x: $element[0].offsetWidth / 2,
          y: $element[0].offsetHeight / 2
        });
      };

      $scope.resetZoom = function() {
        if (canvas) {
          canvas.zoom('fit-viewport', 'auto');
        }
      };

      $window.addEventListener('resize', $scope.resetZoom);

      $scope.$on('destroy', function() {
        $window.removeEventListener('resize', $scope.resetZoom);
      });

      function renderTable() {
        if (xml) {
          var correctedXML = changeDmnNamespace(xml);

          $scope.loaded = false;

          viewer.importXML(correctedXML, function(err) {
            $scope.isDrd = viewer.getDecisions().length > 1 && !$scope.table && !$scope.editMode;

            if ($scope.isDrd) {
              canvas = viewer.get('canvas');
              canvas.zoom('fit-viewport', 'auto');
            }

            if ($scope.table) {
              var decisions = viewer.getDecisions();
              var isIndex = /^[0-9]+$/.test($scope.table);

              $scope.table = isIndex ? +$scope.table : $scope.table;

              decisions.forEach(function(decision, index) {
                if (isDecisionSelected(decision, index)) {
                  viewer.showDecision(decision);
                }
              });
            }

            $scope.$apply(function() {
              if(err) {
                $scope.error = err;
                return;
              }
              $scope.loaded = true;
              $scope.onLoad();
            });
          });
        }
      }

      function isDecisionSelected(decision, index) {
        if (angular.isString($scope.table) && decision.id === $scope.table || decision.name === $scope.table) {
          return true;
        }

        return $scope.table === index;
      }
    }
  };
}];
<commit_msg>fix(linting): add missing semicolon<commit_after>'use strict';
var fs = require('fs');
var angular = require('camunda-bpm-sdk-js/vendor/angular');
var Viewer = require('dmn-js/lib/NavigatedViewer');
var Modeler = require('dmn-js/lib/Modeler');
var changeDmnNamespace = require('../../util/change-dmn-namespace');

var template = fs.readFileSync(__dirname + '/cam-widget-dmn-viewer.html', 'utf8');

module.exports = ['$window', function($window) {

  return {
    scope: {
      xml: '=',
      table: '@',
      width: '=',
      height: '=',
      control: '=?',
      editMode: '=',
      showDetails: '=',
      disableNavigation: '=',
      enableDrdNavigation: '=',
      onLoad: '&',
      onClick: '&',
      onDblClick: '&'
    },
    template: template,
    link: function($scope, $element) {
      var canvas;

      $scope.isDrd = false;

        // --- CONTROL FUNCTIONS ---
      $scope.control = $scope.control || {};

      $scope.control.getViewer = function() {
        return viewer;
      };

      $scope.control.getElement = function(elementId) {
        return viewer.get('elementRegistry').get(elementId);
      };

      $scope.loaded = false;
      $scope.control.isLoaded = function() {
        return $scope.loaded;
      };

      $scope.control.highlightRow = function(elementId, className) {
        var tableViewer = viewer.table;
        var elementRegistry = tableViewer.get('elementRegistry');

        tableViewer.get('eventBus').on('row.render', function(event) {
          if (event.data.id === elementId) {
            angular.element(event.gfx).addClass(className);
          }
        });

        tableViewer.get('graphicsFactory').update('row', elementRegistry.get(elementId), elementRegistry.getGraphics(elementId));
      };

      $scope.control.highlightElement = function(id) {
        if (canvas) {
          canvas.addMarker(id, 'highlight');

          $element.find('[data-element-id="' + id + '"]>.djs-outline').attr({
            rx: '14px',
            ry: '14px'
          });
        }
      };

      $scope.control.clearAllElementsHighlight = function() {
        if (canvas) {
          var children = canvas.getRootElement().children;

          children.forEach(function(element) {
            var id = element.id;

            if (canvas.hasMarker(id, 'highlight')) {
              canvas.removeMarker(id, 'highlight');
            }
          });
        }
      };

      $scope.control.clearElementHighlight = function(id) {
        if (canvas) {
          canvas.removeMarker(id, 'highlight');
        }
      };

      $scope.control.isElementHighlighted = function(id) {
        if (canvas) {
          return canvas.hasMarker(id, 'highlight');
        }
      };

      $scope.control.getElements = function() {
        if (canvas) {
          return canvas.getRootElement().children;
        }
      };

      $scope.control.createBadge = function(id, config) {
        if (canvas) {
          addOverlay(id, config);
        }
      };

      function addOverlay(id, config) {
        var overlays = viewer.get('overlays');

        var overlayId = overlays.add(id, {
          position: config.position || {
            bottom: 10,
            right: 10
          },
          show: {
            minZoom: -Infinity,
            maxZoom: +Infinity
          },
          html: config.html
        });

        return overlayId;
      }

      var DmnViewer;

      if(!$scope.editMode) {
        DmnViewer = Viewer;
      } else {
        DmnViewer = Modeler;
      }

      var container = $element[0].querySelector('.table-holder');
      var viewer = new DmnViewer({
        container: container,
        width: $scope.width,
        height: $scope.height,
        hideDetails: !$scope.showDetails,
        disableDrdInteraction: !$scope.enableDrdNavigation
      });

      var xml = null;

      $scope.$watch('xml', function(newValue) {
        if (newValue) {
          xml = newValue;
          renderTable();
        }
      });

      viewer.on('element.click', function(e) {
        $scope.$apply(function() {
          $scope.onClick({element: e.element, $event: e.originalEvent});
        });
      });

      viewer.on('element.dblclick', function(e) {
        $scope.$apply(function() {
          $scope.onDblClick({element: e.element, $event: e.originalEvent});
        });
      });

      viewer.on('view.switch', function(e) {
        $scope.$apply(function() {
          $scope.isDrd = e.fromTable;
        });
      });

      $scope.zoomIn = function() {
        viewer.get('zoomScroll').zoom(1, {
          x: $element[0].offsetWidth / 2,
          y: $element[0].offsetHeight / 2
        });
      };

      $scope.zoomOut = function() {
        viewer.get('zoomScroll').zoom(-1, {
          x: $element[0].offsetWidth / 2,
          y: $element[0].offsetHeight / 2
        });
      };

      $scope.resetZoom = function() {
        if (canvas) {
          canvas.zoom('fit-viewport', 'auto');
        }
      };

      $window.addEventListener('resize', $scope.resetZoom);

      $scope.$on('destroy', function() {
        $window.removeEventListener('resize', $scope.resetZoom);
      });

      function renderTable() {
        if (xml) {
          var correctedXML = changeDmnNamespace(xml);

          $scope.loaded = false;

          viewer.importXML(correctedXML, function(err) {
            $scope.isDrd = viewer.getDecisions().length > 1 && !$scope.table && !$scope.editMode;

            if ($scope.isDrd) {
              canvas = viewer.get('canvas');
              canvas.zoom('fit-viewport', 'auto');
            }

            if ($scope.table) {
              var decisions = viewer.getDecisions();
              var isIndex = /^[0-9]+$/.test($scope.table);

              $scope.table = isIndex ? +$scope.table : $scope.table;

              decisions.forEach(function(decision, index) {
                if (isDecisionSelected(decision, index)) {
                  viewer.showDecision(decision);
                }
              });
            }

            $scope.$apply(function() {
              if(err) {
                $scope.error = err;
                return;
              }
              $scope.loaded = true;
              $scope.onLoad();
            });
          });
        }
      }

      function isDecisionSelected(decision, index) {
        if (angular.isString($scope.table) && decision.id === $scope.table || decision.name === $scope.table) {
          return true;
        }

        return $scope.table === index;
      }
    }
  };
}];
<|endoftext|>


<commit_before>/* global Plotly */
'use strict';

angular.module('metadatamanagementApp').directive('diagram',
  function($window, $timeout) {
    var link = function(scope, element) {
      scope.isNotLoaded = true;
      scope.isNotAvailable = false;
      var data = [{
        marker: {
          color: '#006AB2',
          opacity: 0.7
        }}];
      var drawDiagram = function() {
        $timeout(function() {
          scope.isNotLoaded = false;
          Plotly.newPlot('diagram', data);
        }, 1000);
      };
      function createString(object) {
        if (object === null) {
          return '';
        }
        return String(object);
      }
      function truncate(str, length) {
        str = createString(str);
        return str.length > length ? str.slice(0, length) + '...' : str;
      }
      if ((scope.type === 'nominal') || (scope.type === 'ordinal')) {
        data[0].x = [];
        data[0].text = [];
        data[0].y = [];
        data[0].type = 'bar';
        if (scope.statistics.validResponses) {
          scope.statistics.validResponses.forEach(function(obj) {
            var tempValue =
              truncate(' - ' + (obj.label[scope.language] || obj.value) + ' - ',
              11);
            data[0].x.push(tempValue);
            data[0].text.push((obj.label[scope.language] || obj.value));
            data[0].y.push(obj.absoluteFrequency);
          });
        } else {
          scope.isNotAvailable = true;
        }
      }
      if ((scope.type === 'kontinuierlich') || (scope.type === 'continous')) {
        /* should be discussed....
        data[0].x = [];
        data[0].type = 'histogram';
        */
        scope.isNotAvailable = true;
      }
      angular.element($window).on('resize', function() {
        drawDiagram();
      });
      element.on('$destroy', function() {
        angular.element($window).off('resize');
      });
      drawDiagram();
    };
    return {
      restrict: 'E',
      templateUrl: 'scripts/variablemanagement/directives/diagram.html.tmpl',
      link: link,
      scope: {
        statistics: '=',
        language: '=',
        type: '='
      }
    };
  });
<commit_msg>#672 dispalyed diagram only when container existed<commit_after>/* global Plotly */
'use strict';

angular.module('metadatamanagementApp').directive('diagram',
  function($window, $timeout) {
    var link = function(scope, element) {
      scope.isNotLoaded = true;
      scope.isNotAvailable = false;
      var data = [{
        marker: {
          color: '#006AB2',
          opacity: 0.7
        }}];
      var drawDiagram = function() {
        $timeout(function() {
          scope.isNotLoaded = false;
          if (angular.element('diagram').length > 0) {
            Plotly.newPlot('diagram', data);
          }
        }, 1000);
      };
      function createString(object) {
        if (object === null) {
          return '';
        }
        return String(object);
      }
      function truncate(str, length) {
        str = createString(str);
        return str.length > length ? str.slice(0, length) + '...' : str;
      }
      if ((scope.type === 'nominal') || (scope.type === 'ordinal')) {
        data[0].x = [];
        data[0].text = [];
        data[0].y = [];
        data[0].type = 'bar';
        if (scope.statistics.validResponses) {
          scope.statistics.validResponses.forEach(function(obj) {
            var tempValue =
              truncate(' - ' + (obj.label[scope.language] || obj.value) + ' - ',
              11);
            data[0].x.push(tempValue);
            data[0].text.push((obj.label[scope.language] || obj.value));
            data[0].y.push(obj.absoluteFrequency);
          });
        } else {
          scope.isNotAvailable = true;
        }
      }
      if ((scope.type === 'kontinuierlich') || (scope.type === 'continous')) {
        /* should be discussed....
        data[0].x = [];
        data[0].type = 'histogram';
        */
        scope.isNotAvailable = true;
      }
      angular.element($window).on('resize', function() {
        drawDiagram();
      });
      element.on('$destroy', function() {
        angular.element($window).off('resize');
      });
      drawDiagram();
    };
    return {
      restrict: 'E',
      templateUrl: 'scripts/variablemanagement/directives/diagram.html.tmpl',
      link: link,
      scope: {
        statistics: '=',
        language: '=',
        type: '='
      }
    };
  });
<|endoftext|>


<commit_before>/**
 * @package     hubzero-cms
 * @file        core/assets/js/hubcore.js
 * @copyright   Copyright 2005-2015 HUBzero Foundation, LLC.
 * @license     http://opensource.org/licenses/MIT MIT
 */

// Only define the Hubzero namespace if not defined
if (typeof(Hubzero) === 'undefined') {
	var Hubzero = {};
}

/**
 * Session based api initialization
 */
Hubzero.initApi = function ( callback )
{
	// Get session token for oauth calls
	$.ajax({
		url      : '/api/developer/oauth/token',
		data     : 'grant_type=session',
		dataType : 'json',
		type     : 'POST',
		cache    : 'false',
		success  : function ( data, textStatus, jqXHR )
		{
			var token = data.access_token;

			// Set defaults for ajax calls
			$.ajaxSetup({
				headers : {
					'Authorization' : 'Bearer ' + token
				}
			});

			if ($.type(callback) === 'function')
			{
				callback();
			}
		}
	});
};<commit_msg>[ASSETS] refresh access token periodically, based on expiration time<commit_after>/**
 * @package     hubzero-cms
 * @file        core/assets/js/hubcore.js
 * @copyright   Copyright 2005-2015 HUBzero Foundation, LLC.
 * @license     http://opensource.org/licenses/MIT MIT
 */

// Only define the Hubzero namespace if not defined
if (typeof(Hubzero) === 'undefined') {
	var Hubzero = {};
}

/**
 * Session based api initialization
 */
Hubzero.initApi = function ( callback )
{
	// Get session token for oauth calls
	$.ajax({
		url      : '/api/developer/oauth/token',
		data     : 'grant_type=session',
		dataType : 'json',
		type     : 'POST',
		cache    : 'false',
		success  : function ( data, textStatus, jqXHR )
		{
			var token = data.access_token;

			// Set defaults for ajax calls
			$.ajaxSetup({
				headers : {
					'Authorization' : 'Bearer ' + token
				}
			});

			if ($.type(callback) === 'function')
			{
				callback();
			}

			// Check again when the token expires
			setTimeout(Hubzero.initApi, data.expires_in*1000);
		}
	});
};<|endoftext|>


<commit_before>try {
    require('@jymfony/util');
} catch (e) {
    require('../../../util');
}

const { normalize } = require('path');
let Compiler;
let Parser;
let AST;

const DescriptorStorage = require('./DescriptorStorage');
const ManagedProxy = require('./Proxy/ManagedProxy');
const isNyc = !! global.__coverage__;

const Generator = require('./Parser/SourceMap/Generator');
const StackHandler = require('./Parser/SourceMap/StackHandler');
const Storage = function () {};
Storage.prototype = {};

let codeCache = new Storage();
let _cache = new Storage();

let { resolve } = require;
if (__jymfony.version_compare(process.versions.v8, '10.0.0', '<')) {
    resolve = (id, { paths }) => {
        if (id.startsWith('.')) {
            return require.resolve(id, { paths });
        }

        return require.resolve(id, { paths: [ paths[0] + '/node_modules' ] });
    };
}

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 * because the buffer-to-string conversion in `fs.readFileSync()`
 * translates it to FEFF, the UTF-16 BOM.
 */
function stripBOM(content) {
    if (0xFEFF === content.charCodeAt(0)) {
        content = content.slice(1);
    }
    return content;
}

// From node source:/lib/internal/modules/cjs/helpers.js
const builtinLibs = [
    'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto',
    'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net',
    'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl',
    'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util',
    'v8', 'vm', 'worker_threads', 'zlib',
];

/**
 * Patching-replacement for "require" function in Autoloader component.
 *
 * @internal
 * @memberOf Jymfony.Component.Autoloader
 */
class ClassLoader {
    /**
     * Constructor
     *
     * @param {Jymfony.Component.Autoloader.Finder} finder
     * @param {path} path
     * @param {vm} vm
     */
    constructor(finder, path, vm) {
        /**
         * @type {Jymfony.Component.Autoloader.Finder}
         *
         * @private
         */
        this._finder = finder;

        /**
         * @type {path}
         *
         * @private
         */
        this._path = path;

        /**
         * @type {vm}
         *
         * @private
         */
        this._vm = vm;

        /**
         * @type {Jymfony.Component.Autoloader.DescriptorStorage}
         *
         * @private
         */
        this._descriptorStorage = new DescriptorStorage(this);

        if (undefined === Compiler) {
            Compiler = require('./Parser/Compiler');
            Parser = require('./Parser/Parser');
            AST = require('./Parser/AST');
        }
    }

    /**
     * Clears the code/compiler cache.
     */
    static clearCache(prefix = null) {
        if (null === prefix) {
            codeCache = new Storage();
            _cache = new Storage();
        } else {
            prefix = new RegExp('^' + __jymfony.regex_quote(normalize(prefix)));
            for (const fn of Object.keys(_cache)) {
                if (! fn.match(prefix)) {
                    continue;
                }

                delete _cache[fn];
                delete codeCache[fn];
            }
        }
    }

    /**
     * Loads a class.
     *
     * @param {string} fn
     * @param {*} self
     *
     * @returns {*}
     */
    loadClass(fn, self) {
        const exports = this.loadFile(fn, self);

        return exports.__esModule ? exports.default : exports;
    }

    /**
     * Loads a file and returns the file exports.
     *
     * @param {string} fn
     * @param {*} self
     * @param {*} exports
     *
     * @returns {*}
     */
    loadFile(fn, self, exports = {}) {
        fn = this._path.resolve(fn);
        if (_cache[fn]) {
            return _cache[fn];
        }

        return _cache[fn] = this._doLoadFile(fn, self, exports);
    }

    /**
     * Gets a file code.
     *
     * @param {string} fn
     *
     * @returns {{code: string, program: Jymfony.Component.Autoloader.Parser.AST.Program}}
     */
    getCode(fn) {
        if (codeCache[fn]) {
            const cached = codeCache[fn];
            Object.assign(this._descriptorStorage._storage, cached.decorators);

            return cached;
        }

        let code = stripBOM(this._finder.load(fn)), program = null;
        const sourceMapGenerator = new Generator({ file: fn });
        const descriptorStorage = this._descriptorStorage;
        const decorators = {};

        try {
            this._descriptorStorage = this._descriptorStorage.setFile(fn);
            const parser = new Parser(this._descriptorStorage);
            const compiler = new Compiler(sourceMapGenerator);

            program = parser.parse(code);
            program.prepare();

            const p = new AST.Program(program.location);
            p.add(new AST.ParenthesizedExpression(null,
                new AST.FunctionExpression(null, new AST.BlockStatement(null, [
                    new AST.StringLiteral(null, '\'use strict\''),
                    ...program.body,
                ]), null, [
                    new AST.Identifier(null, 'exports'),
                    new AST.Identifier(null, 'require'),
                    new AST.Identifier(null, 'module'),
                    new AST.Identifier(null, '__filename'),
                    new AST.Identifier(null, '__dirname'),
                    new AST.Identifier(null, '__self'),
                ])
            ));

            code = compiler.compile(p);
            StackHandler.registerSourceMap(fn, sourceMapGenerator.toJSON().mappings);
        } catch (err) {
            // Compiler have failed. Code is unpatched, but can be included.

            if (! (err instanceof SyntaxError)) {
                throw err;
            } else {
                console.warn('Syntax error while parsing ' + fn + ': ' + err.message);
            }
        } finally {
            Object.assign(decorators, this._descriptorStorage._storage);
            this._descriptorStorage = descriptorStorage;
        }

        return codeCache[fn] = {
            code,
            program,
            decorators,
        };
    }

    /**
     * Internal file loader.
     *
     * @param {string} fn
     * @param {*} self
     * @param {*} exports
     *
     * @returns {*}
     *
     * @private
     */
    _doLoadFile(fn, self, exports) {
        const module = this._getModuleObject(fn, exports);
        const dirname = module.paths[0];
        const opts = isNyc ? fn : {
            filename: fn,
            produceCachedData: false,
        };

        const req = id => {
            if (builtinLibs.includes(id)) {
                return require(id);
            }

            id = resolve(id, { paths: [ dirname ] });
            if (! id.endsWith('.js')) {
                return require(id);
            }

            return this.loadFile(id, null);
        };

        _cache[fn] = module.exports;
        req.proxy = id => {
            if (builtinLibs.includes(id)) {
                return require(id);
            }

            id = require.resolve(id, { paths: [ dirname ] });
            if (_cache[id]) {
                return _cache[id];
            }

            const code = this.getCode(id);
            const exports = {};

            return _cache[id] = new ManagedProxy(exports, proxy => {
                proxy.initializer = null;
                proxy.target = exports;

                const module = this._getModuleObject(id, exports);
                const dirname = module.paths[0];

                this._vm.runInThisContext(code.code, opts)(module.exports, req, module, id, dirname, null);
                proxy.target = module.exports;

                return null;
            });
        };

        req.optional = (id, asObject = false) => {
            try {
                return req(id);
            } catch (e) {
                return asObject ? {} : undefined;
            }
        };

        this._vm.runInThisContext(this.getCode(fn).code, opts)(module.exports, req, module, fn, dirname, self);
        require.cache[fn] = module;

        return _cache[fn] = module.exports;
    }

    _getModuleObject(fn, exports) {
        return {
            children: [],
            exports,
            filename: fn,
            id: fn,
            loaded: false,
            parent: module,
            paths: [ this._path.dirname(fn) ],
            require: require,
        };
    }
}

module.exports = ClassLoader;
<commit_msg>[Autoloader] add require.resolve to custom require function<commit_after>try {
    require('@jymfony/util');
} catch (e) {
    require('../../../util');
}

const { normalize } = require('path');
let Compiler;
let Parser;
let AST;

const DescriptorStorage = require('./DescriptorStorage');
const ManagedProxy = require('./Proxy/ManagedProxy');
const isNyc = !! global.__coverage__;

const Generator = require('./Parser/SourceMap/Generator');
const StackHandler = require('./Parser/SourceMap/StackHandler');
const Storage = function () {};
Storage.prototype = {};

let codeCache = new Storage();
let _cache = new Storage();

let { resolve } = require;
if (__jymfony.version_compare(process.versions.v8, '10.0.0', '<')) {
    resolve = (id, { paths }) => {
        if (id.startsWith('.')) {
            return require.resolve(id, { paths });
        }

        return require.resolve(id, { paths: [ paths[0] + '/node_modules' ] });
    };
}

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 * because the buffer-to-string conversion in `fs.readFileSync()`
 * translates it to FEFF, the UTF-16 BOM.
 */
function stripBOM(content) {
    if (0xFEFF === content.charCodeAt(0)) {
        content = content.slice(1);
    }
    return content;
}

// From node source:/lib/internal/modules/cjs/helpers.js
const builtinLibs = [
    'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto',
    'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net',
    'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl',
    'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util',
    'v8', 'vm', 'worker_threads', 'zlib',
];

/**
 * Patching-replacement for "require" function in Autoloader component.
 *
 * @internal
 * @memberOf Jymfony.Component.Autoloader
 */
class ClassLoader {
    /**
     * Constructor
     *
     * @param {Jymfony.Component.Autoloader.Finder} finder
     * @param {path} path
     * @param {vm} vm
     */
    constructor(finder, path, vm) {
        /**
         * @type {Jymfony.Component.Autoloader.Finder}
         *
         * @private
         */
        this._finder = finder;

        /**
         * @type {path}
         *
         * @private
         */
        this._path = path;

        /**
         * @type {vm}
         *
         * @private
         */
        this._vm = vm;

        /**
         * @type {Jymfony.Component.Autoloader.DescriptorStorage}
         *
         * @private
         */
        this._descriptorStorage = new DescriptorStorage(this);

        if (undefined === Compiler) {
            Compiler = require('./Parser/Compiler');
            Parser = require('./Parser/Parser');
            AST = require('./Parser/AST');
        }
    }

    /**
     * Clears the code/compiler cache.
     */
    static clearCache(prefix = null) {
        if (null === prefix) {
            codeCache = new Storage();
            _cache = new Storage();
        } else {
            prefix = new RegExp('^' + __jymfony.regex_quote(normalize(prefix)));
            for (const fn of Object.keys(_cache)) {
                if (! fn.match(prefix)) {
                    continue;
                }

                delete _cache[fn];
                delete codeCache[fn];
            }
        }
    }

    /**
     * Loads a class.
     *
     * @param {string} fn
     * @param {*} self
     *
     * @returns {*}
     */
    loadClass(fn, self) {
        const exports = this.loadFile(fn, self);

        return exports.__esModule ? exports.default : exports;
    }

    /**
     * Loads a file and returns the file exports.
     *
     * @param {string} fn
     * @param {*} self
     * @param {*} exports
     *
     * @returns {*}
     */
    loadFile(fn, self, exports = {}) {
        fn = this._path.resolve(fn);
        if (_cache[fn]) {
            return _cache[fn];
        }

        return _cache[fn] = this._doLoadFile(fn, self, exports);
    }

    /**
     * Gets a file code.
     *
     * @param {string} fn
     *
     * @returns {{code: string, program: Jymfony.Component.Autoloader.Parser.AST.Program}}
     */
    getCode(fn) {
        if (codeCache[fn]) {
            const cached = codeCache[fn];
            Object.assign(this._descriptorStorage._storage, cached.decorators);

            return cached;
        }

        let code = stripBOM(this._finder.load(fn)), program = null;
        const sourceMapGenerator = new Generator({ file: fn });
        const descriptorStorage = this._descriptorStorage;
        const decorators = {};

        try {
            this._descriptorStorage = this._descriptorStorage.setFile(fn);
            const parser = new Parser(this._descriptorStorage);
            const compiler = new Compiler(sourceMapGenerator);

            program = parser.parse(code);
            program.prepare();

            const p = new AST.Program(program.location);
            p.add(new AST.ParenthesizedExpression(null,
                new AST.FunctionExpression(null, new AST.BlockStatement(null, [
                    new AST.StringLiteral(null, '\'use strict\''),
                    ...program.body,
                ]), null, [
                    new AST.Identifier(null, 'exports'),
                    new AST.Identifier(null, 'require'),
                    new AST.Identifier(null, 'module'),
                    new AST.Identifier(null, '__filename'),
                    new AST.Identifier(null, '__dirname'),
                    new AST.Identifier(null, '__self'),
                ])
            ));

            code = compiler.compile(p);
            StackHandler.registerSourceMap(fn, sourceMapGenerator.toJSON().mappings);
        } catch (err) {
            // Compiler have failed. Code is unpatched, but can be included.

            if (! (err instanceof SyntaxError)) {
                throw err;
            } else {
                console.warn('Syntax error while parsing ' + fn + ': ' + err.message);
            }
        } finally {
            Object.assign(decorators, this._descriptorStorage._storage);
            this._descriptorStorage = descriptorStorage;
        }

        return codeCache[fn] = {
            code,
            program,
            decorators,
        };
    }

    /**
     * Internal file loader.
     *
     * @param {string} fn
     * @param {*} self
     * @param {*} exports
     *
     * @returns {*}
     *
     * @private
     */
    _doLoadFile(fn, self, exports) {
        const module = this._getModuleObject(fn, exports);
        const dirname = module.paths[0];
        const opts = isNyc ? fn : {
            filename: fn,
            produceCachedData: false,
        };

        const req = id => {
            if (builtinLibs.includes(id)) {
                return require(id);
            }

            id = resolve(id, { paths: [ dirname ] });
            if (! id.endsWith('.js')) {
                return require(id);
            }

            return this.loadFile(id, null);
        };

        _cache[fn] = module.exports;
        req.proxy = id => {
            if (builtinLibs.includes(id)) {
                return require(id);
            }

            id = resolve(id, { paths: [ dirname ] });
            if (_cache[id]) {
                return _cache[id];
            }

            const code = this.getCode(id);
            const exports = {};

            return _cache[id] = new ManagedProxy(exports, proxy => {
                proxy.initializer = null;
                proxy.target = exports;

                const module = this._getModuleObject(id, exports);
                const dirname = module.paths[0];

                this._vm.runInThisContext(code.code, opts)(module.exports, req, module, id, dirname, null);
                proxy.target = module.exports;

                return null;
            });
        };

        req.optional = (id, asObject = false) => {
            try {
                return req(id);
            } catch (e) {
                return asObject ? {} : undefined;
            }
        };

        req.resolve = (id, options = {}) => {
            return resolve(id, { paths: [ dirname ], ...options });
        };

        this._vm.runInThisContext(this.getCode(fn).code, opts)(module.exports, req, module, fn, dirname, self);
        require.cache[fn] = module;

        return _cache[fn] = module.exports;
    }

    _getModuleObject(fn, exports) {
        return {
            children: [],
            exports,
            filename: fn,
            id: fn,
            loaded: false,
            parent: module,
            paths: [ this._path.dirname(fn) ],
            require: require,
        };
    }
}

module.exports = ClassLoader;
<|endoftext|>


<commit_before>/**
 * @file HuePicker component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Event from '../_vendors/Event';
import Dom from '../_vendors/Dom';
import Valid from '../_vendors/Valid';
import Color from '../_vendors/Color';

export default class HuePicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            offset: 0,
            value: props.value
        };

        this.activated = false;

        this.getOffset = ::this.getOffset;
        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseMoveHandler = ::this.mouseMoveHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;

    }

    getOffset(value = this.props.value) {

        if (!Valid.isRGB(value)) {
            return 0;
        }

        return this.huePickerEl.offsetWidth * Color.hue2PerCent(value);

    }

    mouseDownHandler(e) {

        this.activated = true;

        this.changeHandler(e.clientX - Dom.getOffset(this.huePickerEl).left);

    }

    mouseMoveHandler(e) {
        if (this.activated) {
            this.changeHandler(e.clientX - Dom.getOffset(this.huePickerEl).left);
        }
    }

    mouseUpHandler(e) {
        this.activated = false;
    }

    changeHandler(offsetX) {

        const width = this.huePickerEl.offsetWidth,
            offset = Valid.range(offsetX, 0, width),
            perCent = offset / width,
            value = Color.perCent2Hue(perCent);

        this.setState({
            offset,
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {

        this.huePickerEl = this.refs.huePicker;

        this.setState({
            offset: this.getOffset()
        });

        Event.addEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.addEvent(document, 'mouseup', this.mouseUpHandler);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value,
                offset: this.getOffset(nextProps.value)
            });
        }
    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.removeEvent(document, 'mouseup', this.mouseUpHandler);
    }

    render() {

        const {className, style} = this.props,
            {offset} = this.state,

            wrapperClassName = (className ? ' ' + className : ''),

            pointerStyle = {
                transform: `translateX(${offset}px)`
            };

        return (

        );

    }
};

HuePicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * hue rgb value.
     */
    value: PropTypes.arrayOf(PropTypes.number),

    onChange: PropTypes.func

};

HuePicker.defaultProps = {

    className: null,
    style: null,

    value: [255, 0, 0]

};<commit_msg>update HuePicker<commit_after>/**
 * @file HuePicker component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Event from '../_vendors/Event';
import Dom from '../_vendors/Dom';
import Valid from '../_vendors/Valid';

export default class HuePicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value
        };

        this.activated = false;

        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseMoveHandler = ::this.mouseMoveHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;

    }

    mouseDownHandler(e) {
        this.activated = true;
        this.changeHandler(e.clientX - Dom.getOffset(this.huePickerEl).left);
    }

    mouseMoveHandler(e) {
        if (this.activated) {
            this.changeHandler(e.clientX - Dom.getOffset(this.huePickerEl).left);
        }
    }

    mouseUpHandler() {
        this.activated = false;
    }

    changeHandler(offsetX) {

        const width = this.huePickerEl.offsetWidth,
            offset = Valid.range(offsetX, 0, width),
            perCent = offset / width,
            value = perCent * 360;

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {

        this.huePickerEl = this.refs.huePicker;

        Event.addEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.addEvent(document, 'mouseup', this.mouseUpHandler);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.removeEvent(document, 'mouseup', this.mouseUpHandler);
    }

    render() {

        const {className, style} = this.props,
            {value} = this.state,

            wrapperClassName = (className ? ' ' + className : ''),

            pointerStyle = {
                left: `${value / 360 * 100}%`
            };

        return (

        );

    }
};

HuePicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * hue value (deg).
     */
    value: PropTypes.number,

    onChange: PropTypes.func

};

HuePicker.defaultProps = {

    className: null,
    style: null,

    value: 0

};<|endoftext|>


<commit_before>L.Control.SlideMenu = L.Control.extend({
    options: {
        position: 'topleft',
        menuposition: 'topleft', // topleft,topright,bottomleft,bottomright
        width: '300px',
        height: '100%',
        direction: 'horizontal', // vertical or horizontal
        changeperc: '10',
        delay: '10',
        icon: 'fa-bars'
    },

    initialize: function(innerHTML, options){
        L.Util.setOptions(this, options);
        this._innerHTML = innerHTML;
        this._isLeftPosition = this.options.menuposition == 'topleft' ||
            this.options.menuposition == 'bottomleft' ? true : false;
        this._isTopPosition = this.options.menuposition == 'topleft' ||
            this.options.menuposition == 'topright' ? true : false;
        this._isHorizontal = this.options.direction == 'horizontal' ? true : false;
    },

    onAdd: function(map){
        this._container = L.DomUtil.create('div', 'leaflet-control-slidemenu leaflet-bar leaflet-control');
        var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', this._container);
        link.title = 'Menu';
        L.DomUtil.create('span', 'fa ' + this.options.icon, link);

        this._menu = L.DomUtil.create('div', 'leaflet-menu', map._container);

        this._menu.style.width = this.options.width;
        this._menu.style.height = this.options.height;

        if(this._isHorizontal){
            var frominit = -(parseInt(this.options.width, 10));
            if(this._isLeftPosition){
                this._menu.style.left = '-' + this.options.width;
            }
            else{
                this._menu.style.right = '-' + this.options.width;
            }

            if(this._isTopPosition){
                this._menu.style.top = '0px';
            }
            else{
                this._menu.style.bottom = '0px';
            }
        }
        else{
            var frominit = -(parseInt(this.options.height, 10));
            if(this._isLeftPosition){
                this._menu.style.left = '0px';
            }
            else{
                this._menu.style.right = '0px';
            }

            if(this._isTopPosition){
                this._menu.style.top = '-' + this.options.height;
            }
            else{
                this._menu.style.bottom = '-' + this.options.height;
            }
        }

        var closeButton = L.DomUtil.create('button', 'leaflet-menu-close-button fa', this._menu);

        if(this._isHorizontal){
            if(this._isLeftPosition){
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-left');
            }
            else{
                closeButton.style.float = 'left';
                L.DomUtil.addClass(closeButton, 'fa-chevron-right');
            }
        }
        else{
            if(this._isTopPosition){
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-up');
            }
            else{
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-down');
            }
        }

        this._contents = L.DomUtil.create('div', 'leaflet-menu-contents', this._menu);
        this._contents.innerHTML = this._innerHTML;
        this._contents.style.clear = 'both';

        if(this._isHorizontal){
            var ispx = this.options.width.slice(-1) == 'x' ? true : false;
            var unit = parseInt(this.options.width, 10) * parseInt(this.options.changeperc, 10) / 100;
        }
        else{
            var ispx = this.options.height.slice(-1) == 'x' ? true : false;
            var unit = parseInt(this.options.height, 10) * parseInt(this.options.changeperc, 10) / 100;
        }

        L.DomEvent.disableClickPropagation(this._menu);
        L.DomEvent
            .on(link, 'click', L.DomEvent.stopPropagation)
            .on(link, 'click', function(){
                // Open
                this._animate(this._menu, frominit, 0, true, ispx, unit);
            }, this)
            .on(closeButton, 'click', L.DomEvent.stopPropagation)
            .on(closeButton, 'click', function(){
                // Close
                this._animate(this._menu, 0, frominit, false, ispx, unit);
            }, this);
        L.DomEvent.on(this._menu, 'mouseover', function(){
            map.scrollWheelZoom.disable();
        });
        L.DomEvent.on(this._menu, 'mouseout', function(){
            map.scrollWheelZoom.enable();
        });

        return this._container;
    },

    onRemove: function(map){
        //Remove sliding menu from DOM
        map._container.removeChild(this._menu);
        delete this._menu;
    },

    setContents: function(innerHTML){
        this._innerHTML = innerHTML;
        this._contents.innerHTML = this._innerHTML;
    },

    _animate: function(menu, from, to, isOpen, ispx, unit){
        if(this._isHorizontal){
            if(this._isLeftPosition){
                menu.style.left = from + (ispx ? 'px' : '%');
            }
            else{
                menu.style.right = from + (ispx ? 'px' : '%');
            }
        }
        else{
            if(this._isTopPosition){
                menu.style.top = from + (ispx ? 'px' : '%');
            }
            else{
                menu.style.bottom = from + (ispx ? 'px' : '%');
            }
        }

        if(from != to){
            setTimeout(function(slideMenu){
                var value = isOpen ? from + unit : from - unit;
                slideMenu._animate(slideMenu._menu, value, to, isOpen, ispx, unit);
            }, parseInt(this.options.delay), this);
        }
        else{
            return;
        }
    }
});

L.control.slideMenu = function(innerHTML, options) {
    return new L.Control.SlideMenu(innerHTML, options);
};<commit_msg>Se añaden los metodos de show() y hide(), para poder interactuar con el componente.<commit_after>L.Control.SlideMenu = L.Control.extend({
    options: {
        position: 'topleft',
        menuposition: 'topleft', // topleft,topright,bottomleft,bottomright
        width: '300px',
        height: '100%',
        direction: 'horizontal', // vertical or horizontal
        changeperc: '10',
        delay: '10',
        icon: 'fa-bars',
        hidden: false
    },

    initialize: function(innerHTML, options){
        L.Util.setOptions(this, options);
        this._innerHTML = innerHTML;
        this._isLeftPosition = this.options.menuposition == 'topleft' ||
            this.options.menuposition == 'bottomleft' ? true : false;
        this._isTopPosition = this.options.menuposition == 'topleft' ||
            this.options.menuposition == 'topright' ? true : false;
        this._isHorizontal = this.options.direction == 'horizontal' ? true : false;
    },

    onAdd: function(map){
        this._container = L.DomUtil.create('div', 'leaflet-control-slidemenu leaflet-bar leaflet-control');
        var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', this._container);
        link.title = 'Menu';
        L.DomUtil.create('span', 'fa ' + this.options.icon, link);

        this._menu = L.DomUtil.create('div', 'leaflet-menu', map._container);

        this._menu.style.width = this.options.width;
        this._menu.style.height = this.options.height;

        if(this._isHorizontal){
            var frominit = -(parseInt(this.options.width, 10));
            if(this._isLeftPosition){
                this._menu.style.left = '-' + this.options.width;
            }
            else{
                this._menu.style.right = '-' + this.options.width;
            }

            if(this._isTopPosition){
                this._menu.style.top = '0px';
            }
            else{
                this._menu.style.bottom = '0px';
            }
        }
        else{
            var frominit = -(parseInt(this.options.height, 10));
            if(this._isLeftPosition){
                this._menu.style.left = '0px';
            }
            else{
                this._menu.style.right = '0px';
            }

            if(this._isTopPosition){
                this._menu.style.top = '-' + this.options.height;
            }
            else{
                this._menu.style.bottom = '-' + this.options.height;
            }
        }

        var closeButton = L.DomUtil.create('button', 'leaflet-menu-close-button fa', this._menu);

        if(this._isHorizontal){
            if(this._isLeftPosition){
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-left');
            }
            else{
                closeButton.style.float = 'left';
                L.DomUtil.addClass(closeButton, 'fa-chevron-right');
            }
        }
        else{
            if(this._isTopPosition){
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-up');
            }
            else{
                closeButton.style.float = 'right';
                L.DomUtil.addClass(closeButton, 'fa-chevron-down');
            }
        }

        this._contents = L.DomUtil.create('div', 'leaflet-menu-contents', this._menu);
        this._contents.innerHTML = this._innerHTML;
        this._contents.style.clear = 'both';

        if(this._isHorizontal){
            var ispx = this.options.width.slice(-1) == 'x' ? true : false;
            var unit = parseInt(this.options.width, 10) * parseInt(this.options.changeperc, 10) / 100;
        }
        else{
            var ispx = this.options.height.slice(-1) == 'x' ? true : false;
            var unit = parseInt(this.options.height, 10) * parseInt(this.options.changeperc, 10) / 100;
        }

        L.DomEvent.disableClickPropagation(this._menu);
        L.DomEvent
            .on(link, 'click', L.DomEvent.stopPropagation)
            .on(link, 'click', function(){
                // Open
                this._animate(this._menu, frominit, 0, true, ispx, unit);
            }, this)
            .on(closeButton, 'click', L.DomEvent.stopPropagation)
            .on(closeButton, 'click', function(){
                // Close
                this._animate(this._menu, 0, frominit, false, ispx, unit);
            }, this);
        L.DomEvent.on(this._menu, 'mouseover', function(){
            map.scrollWheelZoom.disable();
        });
        L.DomEvent.on(this._menu, 'mouseout', function(){
            map.scrollWheelZoom.enable();
        });

        if(this.options.hidden){
            this.hide();
        }

        return this._container;
    },

    onRemove: function(map){
        //Remove sliding menu from DOM
        map._container.removeChild(this._menu);
        delete this._menu;
    },

    setContents: function(innerHTML){
        this._innerHTML = innerHTML;
        this._contents.innerHTML = this._innerHTML;
    },

    _animate: function(menu, from, to, isOpen, ispx, unit){
        if(this._isHorizontal){
            if(this._isLeftPosition){
                menu.style.left = from + (ispx ? 'px' : '%');
            }
            else{
                menu.style.right = from + (ispx ? 'px' : '%');
            }
        }
        else{
            if(this._isTopPosition){
                menu.style.top = from + (ispx ? 'px' : '%');
            }
            else{
                menu.style.bottom = from + (ispx ? 'px' : '%');
            }
        }

        if(from != to){
            setTimeout(function(slideMenu){
                var value = isOpen ? from + unit : from - unit;
                slideMenu._animate(slideMenu._menu, value, to, isOpen, ispx, unit);
            }, parseInt(this.options.delay), this);
        }
        else{
            return;
        }
    },

    hide: function () {
        this._container.style.display = 'none';
    },

    show: function () {
        this._container.style.display = 'inherit';
    }
});

L.control.slideMenu = function(innerHTML, options) {
    return new L.Control.SlideMenu(innerHTML, options);
};<|endoftext|>


<commit_before>Halftone.LuminanceCompressor = function(){

  this.options = {
    luminancePrecision: 100
  };

};

Halftone.LuminanceCompressor.prototype = {

    getDifferenceMatrix: function (oldMatrix, newMatrix){

        var differenceMatrix = {},
            currentPixelIndex = 0;

        for(var r = 0; r < newMatrix.matrix.length; r++){

            var row = newMatrix.matrix[r];

            for(var c = 0; c < row.length; c++){

              var newPixelLuminance = Halftone.Util.getRgbLuminance(row[c], this.options.luminancePrecision);

              if(oldMatrix && oldMatrix.matrix && oldMatrix.matrix[r] && oldMatrix.matrix[r][c]){

                // if there is an old matrix to compare to, find the matching pixel
                var oldPixelLuminance = Halftone.Util.getRgbLuminance(oldMatrix.matrix[r][c], this.options.luminancePrecision);

                if(oldPixelLuminance == newPixelLuminance){

                  // if the pixel color hasn't changed enough, don't change it
                  currentPixelIndex++;

                  continue;

                }

              }

              // either old matrix doesn't exist or pixel color has shifted beyond deltaE tolerance

              if(!differenceMatrix[newPixelLuminance]){
                differenceMatrix[newPixelLuminance] = [];
              }

              // current pixel color is significantly different previous column
              differenceMatrix[newPixelLuminance].push(currentPixelIndex);

              currentPixelIndex++;

            }

        }

        return { metadata: newMatrix.metadata, matrix: differenceMatrix };

    }

};
<commit_msg>tweaking similarity calculation<commit_after>Halftone.LuminanceCompressor = function(){

  this.options = {
    precision: 100,
    similarity: 0.025
  };

};

Halftone.LuminanceCompressor.prototype = {

    getDifferenceMatrix: function (oldMatrix, newMatrix){

        var differenceMatrix = {},
            currentPixelIndex = 0,
            numPixelsChanged = 0;

        for(var r = 0; r < newMatrix.matrix.length; r++){

            var row = newMatrix.matrix[r];

            for(var c = 0; c < row.length; c++){

              var newPixelLuminance = Halftone.Util.getRgbLuminance(row[c], this.options.precision);

              if(oldMatrix && oldMatrix.matrix && oldMatrix.matrix[r] && oldMatrix.matrix[r][c]){

                // if there is an old matrix to compare to, find the matching pixel
                var oldPixelLuminance = Halftone.Util.getRgbLuminance(oldMatrix.matrix[r][c], this.options.precision);

                if(Math.abs(oldPixelLuminance - newPixelLuminance) < this.options.similarity){

                  // if the pixel color hasn't changed enough, don't change it
                  currentPixelIndex++;

                  continue;

                }

              }

              // either old matrix doesn't exist or pixel color has shifted beyond deltaE tolerance

              if(!differenceMatrix[newPixelLuminance]){
                differenceMatrix[newPixelLuminance] = [];
              }

              // current pixel color is significantly different previous column
              differenceMatrix[newPixelLuminance].push(currentPixelIndex);

              currentPixelIndex++;
              numPixelsChanged++;

            }

        }

        newMatrix.metadata.numPixelsChanged = numPixelsChanged;

        return { metadata: newMatrix.metadata, matrix: differenceMatrix };

    }

};
<|endoftext|>


<commit_before>import * as list from "../../util/list";
import * as record from "../../util/record";
import * as timer from "../../util/timer";
import * as async from "../../util/async";
import * as console from "../../util/console";
import { chrome } from "../../common/globals";
import { async_chrome, throw_error, callback } from "../common/util";
import { assert, crash } from "../../util/assert";

const chrome_get_all = () =>
  async_chrome((f) => {
    chrome["storage"]["local"]["get"](null, callback(f));
  });

const chrome_remove = (keys) =>
  async_chrome((f) => {
    chrome["storage"]["local"]["remove"](keys, callback(f));
  });

const chrome_set = (values) =>
  async_chrome((f) => {
    chrome["storage"]["local"]["set"](values, callback(f));
  });

const duration = timer.make();

export const init = async.after(chrome_get_all(), (db) => {
  const OUTSIDE = 0;
  const INSIDE  = 1;
  const FAILED  = 2;

  const _transaction = {
    _state: OUTSIDE,
    _keys: record.make(),
    _delay: null,
    _timer: null,
    _touched: false
  };

  const _touch = (key) => {
    record.include(_transaction._keys, key, true);
    _transaction._touched = true;
  };

  const _commit = () => {
    if (_transaction._touched && _transaction._timer === null) {
      const duration = timer.make();

      const remove = list.make();
      const update = record.make();

      let removed = false;
      let updated = false;

      record.each(_transaction._keys, (key) => {
        if (record.has(db, key)) {
          record.insert(update, key, record.get(db, key));
          updated = true;

        } else {
          list.push(remove, key);
          removed = true;
        }
      });

      if (removed) {
        const duration = timer.make();

        async.run(chrome_remove(remove), () => {
          timer.done(duration);

          console.debug("commit: remove (" +
                        timer.diff(duration) +
                        "ms)");
        });
      }

      if (updated) {
        const duration = timer.make();

        async.run(chrome_set(update), () => {
          timer.done(duration);

          console.debug("commit: write (" +
                        timer.diff(duration) +
                        "ms)");
        });
      }

      _transaction._keys = record.make();
      _transaction._touched = false;

      _transaction._timer = setTimeout(() => {
        _transaction._timer = null;

        _commit();
      }, 1000);

      timer.done(duration);

      console.debug("commit: serialize (" +
                    timer.diff(duration) +
                    "ms)");
    }
  };

  const delay = (ms) => {
    if (_transaction._delay === null) {
      if (_transaction._timer !== null) {
        clearTimeout(_transaction._timer);
      }

      _transaction._delay = ms;

      _transaction._timer = setTimeout(() => {
        _transaction._delay = null;
        _transaction._timer = null;

        _commit();
      }, ms);

    } else {
      assert(ms === _transaction._delay);
    }
  };

  const transaction = (f) => {
    if (_transaction._state === FAILED) {
      crash(new Error("Transaction failed"));

    } else if (_transaction._state === OUTSIDE) {
      _transaction._state = INSIDE;

      try {
        f();

      } catch (e) {
        _transaction._state = FAILED;
        throw e;
      }

      _commit();
      _transaction._state = OUTSIDE;

    } else {
      crash(new Error("Transaction is already running"));
    }
  };

  const check_transaction = () => {
    if (_transaction._state === FAILED) {
      crash(new Error("Cannot write: transaction failed"));

    } else if (_transaction._state === OUTSIDE) {
      crash(new Error("Cannot write: must be inside of a transaction"));
    }
  };

  // TODO maybe run this inside a transaction, so that it cannot be nested inside another transaction ?
  const modify = (key, f) => {
    check_transaction();

    record.modify(db, key, f);

    _touch(key);
  };

  // TODO is this correct ?
  const write = (key, f) => {
    check_transaction();

    f(get(key));

    _touch(key);
  };

  const include = (key, value) => {
    record.include(db, key, value);
  };

  const get = (key) =>
    record.get(db, key);

  const get_all = () =>
    db;

  // TODO test this
  // TODO how should this interact with `include` ?
  const set_all = (new_db) => {
    transaction(() => {
      // TODO is this inefficient ?
      record.each(db, (key) => {
        if (!record.has(new_db, key)) {
          // TODO this removes a key while looping over the object, does that cause any issues ?
          record.remove(db, key);
          _touch(key);
        }
      });

      // TODO is this inefficient ?
      record.each(new_db, (key, value) => {
        record.assign(db, key, value);
        _touch(key);
      });
    });
  };

  timer.done(duration);
  console.info("db: initialized (" + timer.diff(duration) + "ms)", db);

  return async.done({ modify, write, include, get, get_all, set_all, delay,
                      transaction });
});
<commit_msg>Changing the way transactions are committed<commit_after>import * as list from "../../util/list";
import * as record from "../../util/record";
import * as timer from "../../util/timer";
import * as async from "../../util/async";
import * as console from "../../util/console";
import { chrome } from "../../common/globals";
import { async_chrome, throw_error, callback } from "../common/util";
import { assert, crash } from "../../util/assert";

const chrome_get_all = () =>
  async_chrome((f) => {
    chrome["storage"]["local"]["get"](null, callback(f));
  });

const chrome_remove = (keys) =>
  async_chrome((f) => {
    chrome["storage"]["local"]["remove"](keys, callback(f));
  });

const chrome_set = (values) =>
  async_chrome((f) => {
    chrome["storage"]["local"]["set"](values, callback(f));
  });

const duration = timer.make();

export const init = async.after(chrome_get_all(), (db) => {
  const OUTSIDE = 0;
  const INSIDE  = 1;
  const FAILED  = 2;

  const _transaction = {
    _state: OUTSIDE,
    _keys: record.make(),
    _delay: null,
    _timer: null,
    _touched: false
  };

  const _touch = (key) => {
    record.include(_transaction._keys, key, true);
    _transaction._touched = true;
  };

  const _commit = () => {
    if (_transaction._touched) {
      const duration = timer.make();

      const remove = list.make();
      const update = record.make();

      let removed = false;
      let updated = false;

      const touched = list.make();

      record.each(_transaction._keys, (key) => {
        list.push(touched, key);

        if (record.has(db, key)) {
          record.insert(update, key, record.get(db, key));
          updated = true;

        } else {
          list.push(remove, key);
          removed = true;
        }
      });

      if (removed) {
        const duration = timer.make();

        async.run(chrome_remove(remove), () => {
          timer.done(duration);

          console.debug("commit: remove (" +
                        timer.diff(duration) +
                        "ms)");
        });
      }

      if (updated) {
        const duration = timer.make();

        async.run(chrome_set(update), () => {
          timer.done(duration);

          console.debug("commit: write (" +
                        timer.diff(duration) +
                        "ms)");
        });
      }

      _transaction._keys = record.make();
      _transaction._touched = false;

      timer.done(duration);

      console.debug("commit: serialize (" +
                    timer.diff(duration) +
                    "ms)\n    " + list.join(touched, "\n    "));
    }
  };

  const delay = (ms) => {
    if (_transaction._delay === null) {
      if (_transaction._timer !== null) {
        clearTimeout(_transaction._timer);
      }

      _transaction._delay = ms;

      _transaction._timer = setTimeout(() => {
        _transaction._delay = null;
        _transaction._timer = null;

        _commit();
      }, ms);

    } else {
      assert(ms === _transaction._delay);
    }
  };

  const transaction = (f) => {
    if (_transaction._state === FAILED) {
      crash(new Error("Transaction failed"));

    } else if (_transaction._state === OUTSIDE) {
      _transaction._state = INSIDE;

      try {
        f();

      } catch (e) {
        _transaction._state = FAILED;
        throw e;
      }

      if (_transaction._timer === null) {
        _transaction._timer = setTimeout(() => {
          _transaction._timer = null;

          _commit();
        }, 1000);
      }

      _transaction._state = OUTSIDE;

    } else {
      crash(new Error("Transaction is already running"));
    }
  };

  const check_transaction = () => {
    if (_transaction._state === FAILED) {
      crash(new Error("Cannot write: transaction failed"));

    } else if (_transaction._state === OUTSIDE) {
      crash(new Error("Cannot write: must be inside of a transaction"));
    }
  };

  // TODO maybe run this inside a transaction, so that it cannot be nested inside another transaction ?
  const modify = (key, f) => {
    check_transaction();

    record.modify(db, key, f);

    _touch(key);
  };

  // TODO is this correct ?
  const write = (key, f) => {
    check_transaction();

    f(get(key));

    _touch(key);
  };

  const include = (key, value) => {
    record.include(db, key, value);
  };

  const get = (key) =>
    record.get(db, key);

  const get_all = () =>
    db;

  // TODO test this
  // TODO how should this interact with `include` ?
  const set_all = (new_db) => {
    transaction(() => {
      // TODO is this inefficient ?
      record.each(db, (key) => {
        if (!record.has(new_db, key)) {
          // TODO this removes a key while looping over the object, does that cause any issues ?
          record.remove(db, key);
          _touch(key);
        }
      });

      // TODO is this inefficient ?
      record.each(new_db, (key, value) => {
        record.assign(db, key, value);
        _touch(key);
      });
    });
  };

  timer.done(duration);
  console.info("db: initialized (" + timer.diff(duration) + "ms)", db);

  return async.done({ modify, write, include, get, get_all, set_all, delay,
                      transaction });
});
<|endoftext|>


<commit_before>
var createClass = require('./functions/createClass');
var arrayIndexOf = require('./functions/arrayIndexOf');

var EventStream = createClass({
	statics: {
		END: {}
	}
},
/** @lends EventStream.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function()
	{
		this.subscribers = [];
		this.oneSubscribers = [];
		this.endSubscribers = [];
	},

	/**
	 * Binds event handler.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	on: function(fn)
	{
		if(arrayIndexOf(this.subscribers, fn) === -1) this.subscribers.push(fn);
		return this;
	},

	onEnd: function(fn)
	{
		if(arrayIndexOf(this.endSubscribers, fn) === -1) this.endSubscribers.push(fn);
		return this;
	},

	/**
	 * Binds event handler that will be executed only once.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	one: function(eventType, fn)
	{
		this.oneSubscribers.push(fn);
		return this.on(fn);
	},

	/**
	 * Unbinds event handler.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	off: function(fn)
	{
		var i = arrayIndexOf(this.subscribers, fn);
		if(i !== -1) this.subscribers.splice(i, 1);

		i = arrayIndexOf(this.oneSubscribers, fn);
		if(i !== -1) this.oneSubscribers.splice(i, 1);

		i = arrayIndexOf(this.endSubscribers, fn);
		if(i !== -1) this.endSubscribers.splice(i, 1);

		return this;
	},

	offAll: function()
	{
		this.subscribers = [];
		this.oneSubscribers = [];
		this.endSubscribers = [];
		return this;
	},

	/**
	 * Triggers an event.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {mixed} eventData Arbitrary data that will be passed to the event handlers as an argument
	 */
	trigger: function(eventData)
	{
		var i, l;

		if(eventData === EventStream.END)
		{
			for(i = 0, l = this.endSubscribers.length; i < l; i++)
			{
				if(typeof this.endSubscribers[i] === 'function') this.endSubscribers[i].call(null);
			}
			return this.offAll();
		}

		for(i = 0, l = this.subscribers.length; i < l; i++)
		{
			if(typeof this.subscribers[i] === 'function') this.subscribers[i].call(null, eventData);
		}

		// Remove "one" subscribers:
		for(i = 0, l = this.oneSubscribers.length; i < l; i++)
		{
			this.off(this.oneSubscribers[i]);
		}

		return this;
	},

	triggerLater: function(eventData, delay)
	{
		var that = this;
		if(delay === undefined) delay = 0;
		setTimeout(function()
		{
			that.trigger(eventData);
		}, delay);
		return this;
	},

	map: function(fn)
	{
		var mes = new EventStream();

		this.on(function(event){
			mes.trigger(fn.call(null, event));
		});

		return mes;
	},

	reduce: function(fn, initialValue)
	{
		var es = new EventStream();
		var value;

		if(typeof fn !== 'function')
		{
			throw new TypeError( fn + ' is not a function' );
		}

		if(arguments.length >= 2)
		{
			value = arguments[1];
		}

		this.on(function(event){
			value = fn.call(null, value, event);
		});

		this.onEnd(function(){
			es.trigger(value).end();
		});

		return es;
	},

	flatMap: function(fn)
	{
		var mes = new EventStream();

		var observe = function(event)
		{
			var res = fn.call(null, event);

			if(res instanceof EventStream)
			{
				res.on(function(event2)
				{
					mes.trigger(event2);
				});
			}
			else
			{
				mes.trigger(res);
			}
		};

		this.on(observe);

		return mes;
	},

	filter: function(fn)
	{
		var fes = new EventStream();

		this.on(function(event){
			if(fn.call(null, event)) fes.trigger(event);
		});

		return fes;
	},

	merge: function(es)
	{
		var mes = new EventStream();

		this.on(mes.f('trigger'));
		es.on(mes.f('trigger'));

		return mes;
	},

	end: function()
	{
		this.trigger(EventStream.END);
	},

	endLater: function(delay)
	{
		var that = this;
		if(delay === undefined) delay = 0;
		setTimeout(function()
		{
			that.trigger(EventStream.END);
		}, delay);
		return this;
	}
});

module.exports = EventStream;

<commit_msg>fix(EventStream): proper propagation of stream ending in map, filter, megre and flatMap methods<commit_after>
var createClass = require('./functions/createClass');
var arrayIndexOf = require('./functions/arrayIndexOf');

var EventStream = createClass({
	statics: {
		END: {}
	}
},
/** @lends EventStream.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function()
	{
		this.subscribers = [];
		this.oneSubscribers = [];
		this.endSubscribers = [];
	},

	/**
	 * Binds event handler.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	on: function(fn)
	{
		if(arrayIndexOf(this.subscribers, fn) === -1) this.subscribers.push(fn);
		return this;
	},

	onEnd: function(fn)
	{
		if(arrayIndexOf(this.endSubscribers, fn) === -1) this.endSubscribers.push(fn);
		return this;
	},

	/**
	 * Binds event handler that will be executed only once.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	one: function(eventType, fn)
	{
		this.oneSubscribers.push(fn);
		return this.on(fn);
	},

	/**
	 * Unbinds event handler.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {function} fn Event handler
	 */
	off: function(fn)
	{
		var i = arrayIndexOf(this.subscribers, fn);
		if(i !== -1) this.subscribers.splice(i, 1);

		i = arrayIndexOf(this.oneSubscribers, fn);
		if(i !== -1) this.oneSubscribers.splice(i, 1);

		i = arrayIndexOf(this.endSubscribers, fn);
		if(i !== -1) this.endSubscribers.splice(i, 1);

		return this;
	},

	offAll: function()
	{
		this.subscribers = [];
		this.oneSubscribers = [];
		this.endSubscribers = [];
		return this;
	},

	/**
	 * Triggers an event.
	 *
	 * @param {string|Array} eventType Event name(s)
	 * @param {mixed} eventData Arbitrary data that will be passed to the event handlers as an argument
	 */
	trigger: function(eventData)
	{
		var i, l;

		if(eventData === EventStream.END)
		{
			for(i = 0, l = this.endSubscribers.length; i < l; i++)
			{
				if(typeof this.endSubscribers[i] === 'function') this.endSubscribers[i].call(null);
			}
			return this.offAll();
		}

		for(i = 0, l = this.subscribers.length; i < l; i++)
		{
			if(typeof this.subscribers[i] === 'function') this.subscribers[i].call(null, eventData);
		}

		// Remove "one" subscribers:
		for(i = 0, l = this.oneSubscribers.length; i < l; i++)
		{
			this.off(this.oneSubscribers[i]);
		}

		return this;
	},

	triggerLater: function(eventData, delay)
	{
		var that = this;
		if(delay === undefined) delay = 0;
		setTimeout(function()
		{
			that.trigger(eventData);
		}, delay);
		return this;
	},

	map: function(fn)
	{
		var mes = new EventStream();

		this.on(function(event){
			mes.trigger(fn.call(null, event));
		});

		this.onEnd(function(){
			mes.end();
		});

		return mes;
	},

	reduce: function(fn, initialValue)
	{
		var es = new EventStream();
		var value;

		if(typeof fn !== 'function')
		{
			throw new TypeError( fn + ' is not a function' );
		}

		if(arguments.length >= 2)
		{
			value = arguments[1];
		}

		this.on(function(event){
			value = fn.call(null, value, event);
		});

		this.onEnd(function(){
			es.trigger(value).end();
		});

		return es;
	},

	flatMap: function(fn)
	{
		var mes = new EventStream();
		var activeStreams = 0;

		var observe = function(event)
		{
			var res = fn.call(null, event);

			if(res instanceof EventStream)
			{
				activeStreams++;
				res.on(function(event2)
				{
					mes.trigger(event2);
				});

				res.onEnd(function()
				{
					activeStreams--;
					if(activeStreams === 0) mes.end();
				});
			}
			else
			{
				mes.trigger(res);
			}
		};

		this.on(observe);
		this.onEnd(function()
		{
			if(activeStreams === 0) mes.end();
		});

		return mes;
	},

	filter: function(fn)
	{
		var fes = new EventStream();

		this.on(function(event){
			if(fn.call(null, event)) fes.trigger(event);
		});

		this.onEnd(function(){
			fes.end();
		});

		return fes;
	},

	merge: function(es)
	{
		var mes = new EventStream();
		var endCount = 2;

		function endHandler()
		{
			endCount--;
			if(endCount === 0) mes.end();
		}

		this.on(mes.f('trigger'));
		es.on(mes.f('trigger'));

		this.onEnd(endHandler);
		es.onEnd(endHandler);

		return mes;
	},

	end: function()
	{
		this.trigger(EventStream.END);
	},

	endLater: function(delay)
	{
		var that = this;
		if(delay === undefined) delay = 0;
		setTimeout(function()
		{
			that.trigger(EventStream.END);
		}, delay);
		return this;
	}
});

module.exports = EventStream;

<|endoftext|>


<commit_before>/** Class to store event callbacks*/
export class Event {
    constructor(name) {
        this.name = name;
        this.callbacks = [];
    }

    /**
     * Adds a new callback
     * @param {function} callback The callback to add
     */
    registerCallback(callback) {
        this.callbacks.push(callback);
    }
}

/** Class to event registering, dispatching and eventListeners */
export class Reactor {
    constructor() {
        this.events = {};
    }

    /**
     * Adds a new event
     * @param {string} eventName The new event to add
     */
    registerEvent(eventName) {
        this.events[eventName] = new Event(eventName);
    }

    /**
     * Triggers and event's callbacks
     * @param {string} eventName The event to trigger
     * @param {Object} eventArgs Arguments to be passed to the event callbacks
     */
    dispatchEvent(eventName, eventArgs) {
        for (var i = 0; i < this.events[eventName].callbacks.length; i++) {
            var callback = this.events[eventName].callbacks[i];
            callback(eventArgs);
        }
    }

    /**
     * Adds a new event callback to an event
     * @param {string}   eventName The event to add a callback to
     * @param {function} callback  The callback
     */
    addEventListener(eventName, callback) {
        this.events[eventName].registerCallback(callback);
    }
}

/** Provides useful tab functions */
export class TabTools {
    /**
     * Opens a new tab
     * @param {string} url The url of the tab to open
     * @param {function} callback Function to be called when the tab is opened
     * @param {boolean} active Whether the new tab should be switched to and made the active tab
     */
    static new(url, callback, active) {
        chrome.tabs.create({
            active: active,
            url: url
        }, callback);
    }
    /**
     * Closes a tab
     * @param {object} tab The tab to close
     */
    static close(tab) {
        chrome.tabs.remove(tab.id);
    }

    /**
     * Executes callback function on the active tab in current window
     * @param {function} callback Function to call when the tab is found.
     *                            The tab is passed as an argument to this callback
     */
    static onActive(callback) {
        chrome.tabs.query({
            active: true,
            currentWindow: true
        }, (tabs) => {
            var activetab = tabs[0];
            callback(activetab);
        });
    }

    /**
     * Closes the active tab
     */
    static closeActive() {TabTools.onActive(TabTools.close)};

    /**
     * Searches for a tab with the given url
     * @param {string} url
     * @param {function} callback
     * @param {boolean} [multiple=false]
     */
    static getByUrl(url, callback, multiple=false) {
        chrome.tabs.query({
                url: url
            },
            (tabs) => {
                if (multiple) {
                    callback(tabs);
                } else {
                    callback(tabs[0]);
                }
            }
        );
    }

    /**
     * Checks if a tabs url matches a given url pattern
     * @param {object}   tab         The tab to check
     * @param {string}   urlPattern  The url pattern
     * @param {function} callback    Function to be called when the query is complete.
     *                               Whether the tab matches or not is passed to this callback
     */
    static tabMatchesUrl(tab, urlPattern, callback) {
        TabTools.getByUrl(urlPattern, (query_tabs) => {
            var foundMatch = false;
            for (var i = 0; i < query_tabs.length; i++) {
                if (tab.id == query_tabs[i].id) {
                    callback(true);
                    foundMatch = true;
                    break;
                }
            }
            if (!foundMatch) {
                callback(false);
            }
        }, true);
    }
}

/** Used to track tabs specified in the manifest. Sends events like onTabLoad and onTabUnload */
export class TabTracker {
    /**
     * @param {string[]} options.urlPatterns The url patterns of the tabs to be tracked
     * @param {number[]} options.tabIds      The IDs of the tabs to be tracked
     */
    constructor(options) {
        this.tabs = {};

        this.urlPatterns = [];
        this.tabIds = [];

        if (typeof options.urlPatterns !== 'undefined') this.urlPatterns = options.urlPatterns;
        if (typeof options.tabIds !== 'undefined') this.tabIds = options.tabIds;

        // Event handler
        this.events = new Reactor();

        this.events.registerEvent('onTabLoad');
        this.events.registerEvent('onTabUnload');

        chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
            // Check if this is one of the tabs we should be tracking
            this._queryTrackTab(sender.tab, (isTrackTab) => {
                if (isTrackTab) {
                    if (request.from == 'contentScript' && request.type == 'loadEvent') {
                        if (request.loadState == 'load') {
                            this.addTab(sender.tab);
                        }
                        else if (request.loadState == 'unload') {
                            this.removeTab(sender.tab);
                        }
                    }
                }
            })
        });
    }

    /**
     * Used by _queryTrackTab to iteratively check if a tab matches the list of contentScript urls
     * @private
     */
    _queryTrackTabLoop(tab, index, isTrackTab, callback) {
        if (index < this.urlPatterns.length) {
            var url = this.urlPatterns[index];
            TabTools.tabMatchesUrl(tab, url, (matches) => {
                if (matches) {
                    callback(true);
                }
                else {
                    this._queryTrackTabLoop(tab, index+1, matches, callback);
                }
            });
        }
        else {
            callback(isTrackTab);
        }
    }

    /**
     * Checks if a tab is one that we wish to track
     * @private
     * @param {object}   tab      The tab to query
     * @param {function} callback Function called when query is complete.
     *                            Whether this is a tab we wish to track is passed to this callback as an argument
     */
    _queryTrackTab(tab, callback) {
        if (typeof tab !== 'undefined' && typeof tab.url !== 'undefined' && this.urlPatterns.length > 0) {
            this._queryTrackTabLoop(tab, 0, false, callback);
        }
        else if (typeof tab !== 'undefined' && typeof tab.id !== 'undefined' && this.tabIds.length > 0) {
            for (let id of this.tabIds) {
                if (tab.id == id) {
                    callback(true);
                    break;
                }
            }
            callback(false);
        }
        else {
            callback(false);
        }
    }

    /** Returns the number of tracks being tracked */
    get numTabs() {
        return Object.keys(this.tabs).length;
    }

    /**
     * Adds a new tab to track and fires the onTabLoad event
     * @param {object} tab The tab to track
     */
    addTab(tab) {
        this.tabs[tab.id] = tab;
        this.events.dispatchEvent('onTabLoad', this.tabs[tab.id]);
    }

    /**
     * Removes a tab being tracked and fires the onTabUnloadEvent
     * @param {object} tab The tab to remove
     */
    removeTab(tab) {
        delete this.tabs[tab.id];
        this.events.dispatchEvent('onTabUnload', tab.id);
    }
}

/** Core extensions class. Handles notifications and saving tabs */
export class Core {
    constructor() {
        /**
         * The core extension actions
         */
        this.coreActions = {};
    }

    /**
     * Sends a basic notification
     * @private
     * @param {object} data The notification data
     */
    _notify(data) {
        data.type = 'basic';
        data.iconUrl = chrome.runtime.getManifest().icons['128'];
        data.title = 'Quantum Router';
        data.isClickable = false;
        chrome.notifications.create('', data);
    }

    /**
     * Creates a chrome notification
     * @param {string} msg  The notification message
     * @param {('error'|'normal')} [type=normal] The type of notification. Valid types are: error
     */
    notify(msg, type='normal') {
        if (type == 'error') {
            this._notify({'message': 'Error: ' + msg});
        } else {
            this._notify({'message': msg});
        }
    }

    /**
     * An alias for Core.coreActions
     */
    get ca() {
        return this.coreActions;
    }
}

export let core = new Core();

/**
 * Base class for modules
 * A module is a class that handles a specific task. For example, sending and receiving WhatsApp messages
 */
export class Module {
    constructor(name) {
        this.name = name;
    }

    /**
     * Sends a message to a tab and attaches which module it was from
     * @param {number} tabId The ID of the tab to send a message to
     * @param {object} data  The message data
     */
    sendTabMessage(tabId, data, callback) {
        data.from = this.name;
        console.log("Sent message to tabId : "+tabId);
        console.log(data);
        chrome.tabs.sendMessage(tabId, data, callback);
    }
}
<commit_msg>Tidy up and remove old code<commit_after>/** Class to store event callbacks*/
export class Event {
    constructor(name) {
        this.name = name;
        this.callbacks = [];
    }

    /**
     * @param {function} callback The callback to add
     */
    registerCallback(callback) {
        this.callbacks.push(callback);
    }
}

/** Class to handle event registering, dispatching and eventListeners */
export class Reactor {
    constructor() {
        this.events = {};
    }

    /**
     * @param {string} eventName The new event to add
     */
    registerEvent(eventName) {
        this.events[eventName] = new Event(eventName);
    }

    /**
     * @param {string} eventName The event to trigger
     * @param {Object} eventArgs Arguments to be passed to the event callbacks
     */
    dispatchEvent(eventName, eventArgs) {
        for (var i = 0; i < this.events[eventName].callbacks.length; i++) {
            var callback = this.events[eventName].callbacks[i];
            callback(eventArgs);
        }
    }

    /**
     * @param {string}   eventName The event to add a callback to
     * @param {function} callback  The callback
     */
    addEventListener(eventName, callback) {
        this.events[eventName].registerCallback(callback);
    }
}

export class TabTools {
    /**
     * @param {string} url The url of the tab to open
     * @param {function} callback Function to be called when the tab is opened
     * @param {boolean} active Whether the new tab should be switched to and made the active tab
     */
    static new(url, callback, active) {
        chrome.tabs.create({
            active: active,
            url: url
        }, callback);
    }
    /**
     * @param {object} tab The tab to close
     */
    static close(tab) {
        chrome.tabs.remove(tab.id);
    }

    /**
     * Executes callback function on the active tab in current window
     * @param {function} callback Function to call when the tab is found.
     *                            The tab is passed as an argument to this callback
     */
    static onActive(callback) {
        chrome.tabs.query({
            active: true,
            currentWindow: true
        }, (tabs) => {
            var activetab = tabs[0];
            callback(activetab);
        });
    }

    static closeActive() {TabTools.onActive(TabTools.close)};

    /**
     * @param {string} url
     * @param {function} callback
     * @param {boolean} [multiple=false]
     */
    static getByUrl(url, callback, multiple=false) {
        chrome.tabs.query({
                url: url
            },
            (tabs) => {
                if (multiple) {
                    callback(tabs);
                } else {
                    callback(tabs[0]);
                }
            }
        );
    }

    /**
     * @param {object}   tab         The tab to check
     * @param {string}   urlPattern  The url pattern
     * @param {function} callback    Function to be called when the query is complete.
     *                               Whether the tab matches or not is passed to this callback
     */
    static tabMatchesUrl(tab, urlPattern, callback) {
        TabTools.getByUrl(urlPattern, (query_tabs) => {
            var foundMatch = false;
            for (var i = 0; i < query_tabs.length; i++) {
                if (tab.id == query_tabs[i].id) {
                    callback(true);
                    foundMatch = true;
                    break;
                }
            }
            if (!foundMatch) {
                callback(false);
            }
        }, true);
    }
}

/** Used to track tabs specified in the manifest. Sends events like onTabLoad and onTabUnload */
export class TabTracker {
    /**
     * @param {string[]} options.urlPatterns The url patterns of the tabs to be tracked
     * @param {number[]} options.tabIds      The IDs of the tabs to be tracked
     */
    constructor(options) {
        this.tabs = {};

        this.urlPatterns = [];
        this.tabIds = [];

        if (typeof options.urlPatterns !== 'undefined') this.urlPatterns = options.urlPatterns;
        if (typeof options.tabIds !== 'undefined') this.tabIds = options.tabIds;

        this.events = new Reactor();
        this.events.registerEvent('onTabLoad');
        this.events.registerEvent('onTabUnload');

        chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
            // Check if this is one of the tabs we should be tracking
            this._queryTrackTab(sender.tab, (isTrackTab) => {
                if (isTrackTab && request.from == 'contentScript' && request.type == 'loadEvent') {
                    if (request.loadState == 'load') {
                        this.addTab(sender.tab);
                    } else if (request.loadState == 'unload') {
                        this.removeTab(sender.tab);
                    }
                }
            })
        });
    }

    /**
     * Used by _queryTrackTab to iteratively check if a tab matches the list of contentScript urls
     * @private
     */
    _queryTrackTabLoop(tab, index, isTrackTab, callback) {
        if (index < this.urlPatterns.length) {
            var url = this.urlPatterns[index];
            TabTools.tabMatchesUrl(tab, url, (matches) => {
                if (matches) {
                    callback(true);
                } else {
                    this._queryTrackTabLoop(tab, index+1, matches, callback);
                }
            });
        } else {
            callback(isTrackTab);
        }
    }

    /**
     * Checks if a tab is one that we wish to track
     * @private
     * @param {object}   tab      The tab to query
     * @param {function} callback Function called when query is complete.
     *                            Whether this is a tab we wish to track is passed to this callback as an argument
     */
    _queryTrackTab(tab, callback) {
        if (typeof tab !== 'undefined' && typeof tab.url !== 'undefined' && this.urlPatterns.length > 0) {
            this._queryTrackTabLoop(tab, 0, false, callback);
        } else if (typeof tab !== 'undefined' && typeof tab.id !== 'undefined' && this.tabIds.length > 0) {
            for (let id of this.tabIds) {
                if (tab.id == id) {
                    callback(true);
                    break;
                }
            }
            callback(false);
        } else {
            callback(false);
        }
    }

    /** Returns the number of tracks being tracked */
    get numTabs() {
        return Object.keys(this.tabs).length;
    }

    /**
     * Adds a new tab to track and fires the onTabLoad event
     * @param {object} tab The tab to track
     */
    addTab(tab) {
        this.tabs[tab.id] = tab;
        this.events.dispatchEvent('onTabLoad', this.tabs[tab.id]);
    }

    /**
     * Removes a tab being tracked and fires the onTabUnloadEvent
     * @param {object} tab The tab to remove
     */
    removeTab(tab) {
        delete this.tabs[tab.id];
        this.events.dispatchEvent('onTabUnload', tab.id);
    }
}

/** Core extensions class. Handles notifications and saving tabs */
export class Notifier {
    /**
     * @private
     * @param {object} data The notification data
     */
    static _notify(data) {
        data.type = 'basic';
        data.iconUrl = chrome.runtime.getManifest().icons['128'];
        data.title = 'Quantum Router';
        data.isClickable = false;
        chrome.notifications.create('', data);
    }

    /**
     * Creates a chrome notification
     * @param {string} msg  The notification message
     * @param {('error'|'normal')} [type=normal] The type of notification.
     */
    static notify(msg, type='normal') {
        if (type == 'error') {
            this._notify({'message': 'Error: ' + msg});
        } else {
            this._notify({'message': msg});
        }
    }
}
<|endoftext|>