<commit_before>/*global require*/
/*global console*/
/*global module*/

var fs = require('fs');
var PNG = require('node-png').PNG;

var PngRounder = function()
{
    //the radius of the corners of the rounded rect. this is multiplied by the
    //min(width,height) of the image, so a value of 0.5 would create a circle.
    //generally, you want values above 0.5, since that creates a rounded rect.
    //values <= 0.5 create smaller and smaller circles
    this.radiusFactor = 0.45;

    //how far in we bring the edge (crop). this is also multiplied by the same
    //dimension value as the radiusFactor. 0.5 would crop the entire image.
    //remember this is cropped from both sides of the image, so total amount cropped
    //is 2 * this value.
    this.edgeSizeFactor = 0.1;
};

//rounds all the PNGs found in the array of search paths. if the search paths
//have any folders, those are expanded (but not recursively).
//exportPath is the folder where the new PNGs will be placed.
PngRounder.prototype.roundPngs = function( sourcePaths, exportPath )
{
    var files = this._findFileList( sourcePaths );

    var fileIndex;
    for ( fileIndex = 0; fileIndex < files.length; fileIndex++ )
    {
        this._roundPng( files[ fileIndex ], exportPath );
    }
};

//takes an array of source paths and expands any that are folders
PngRounder.prototype._findFileList = function( sourcePaths )
{
    var files = [];

    var pathIndex;
    for ( pathIndex = 0; pathIndex < sourcePaths.length; pathIndex++ )
    {
        var path = sourcePaths[ pathIndex ];

        if ( fs.lstatSync( path ).isDirectory() )
        {
            var dirContents = fs.readdirSync( path );
            var subFileIndex;
            for ( subFileIndex = 0; subFileIndex < dirContents.length; subFileIndex++ )
            {
                var subFile = dirContents[ subFileIndex ];
                if ( subFile.substring( subFile.lastIndexOf( "." ) + 1 ).toLowerCase() === "png" )
                {
                    files.push( path + "/" + subFile );
                }
            }
        }
        else
        {
            files.push( path );
        }
    }

    return files;
};

//rounds a single PNG found at path, and puts the new one at exportPath/filename.png
PngRounder.prototype._roundPng = function( path, exportPath )
{
    var radiusFactor = this.radiusFactor;
    var edgeSizeFactor = this.edgeSizeFactor;
    var fileName = path.substring( path.lastIndexOf( "/" ) + 1 );

    fs.createReadStream( path ).pipe( new PNG({filterType: 4}) ).on('parsed', function()
    {
        var center = { x: Math.floor( this.width / 2 ), y: Math.floor( this.height / 2 ) };
        var radius = radiusFactor * Math.min( this.width, this.height );

        var edgeSize = { width: edgeSizeFactor * this.width, height: edgeSizeFactor * this.height };
        var imageRect = { minX: edgeSize.width,
                          minY: edgeSize.height,
                          maxX: this.width  - edgeSize.width,
                          maxY: this.height - edgeSize.height };

        var x;
        var y;
        for ( y = 0; y < this.height; y++ )
        {
            for ( x = 0; x < this.width; x++ )
            {
                var idx = (this.width * y + x) << 2;
                var transparentPixel = false;

                if ( x < imageRect.minX || y < imageRect.minY || x > imageRect.maxX || y > imageRect.maxY )
                {
                    transparentPixel = true;
                }
                else
                {
                    var diff = { x: x - center.x, y: y - center.y };
                    var dist = Math.sqrt( diff.x * diff.x + diff.y * diff.y );

                    if ( dist > radius )
                    {
                        transparentPixel = true;
                    }
                }

                if ( transparentPixel )
                {
                    //this is the alpha value (values are packed in 4s: RGBA in the data array)
                    this.data[idx+3] = 0;
                }
            }
        }

        this.pack().pipe( fs.createWriteStream( exportPath + "/" + fileName ) );
    });
};

module.exports = new PngRounder();<commit_msg>Looks like a fix to transparency. Was on my machine for years.<commit_after>/*global require*/
/*global console*/
/*global module*/

var fs = require('fs');
var PNG = require('node-png').PNG;

var PngRounder = function()
{
    //the radius of the corners of the rounded rect. this is multiplied by the
    //min(width,height) of the image, so a value of 0.5 would create a circle.
    //generally, you want values above 0.5, since that creates a rounded rect.
    //values <= 0.5 create smaller and smaller circles
    //this.radiusFactor = 0.45;

    //how far in we bring the edge (crop). this is also multiplied by the same
    //dimension value as the radiusFactor. 0.5 would crop the entire image.
    //remember this is cropped from both sides of the image, so total amount cropped
    //is 2 * this value.
    //this.edgeSizeFactor = 0.1;

    this.radiusFactor = 1.0;
    this.edgeSizeFactor = 0.0;

    //the total distance in RGB values from white we can go when we turn excess pixels transparent
    this.transparencyRange = 40;
};

//rounds all the PNGs found in the array of search paths. if the search paths
//have any folders, those are expanded (but not recursively).
//exportPath is the folder where the new PNGs will be placed.
PngRounder.prototype.roundPngs = function( sourcePaths, exportPath )
{
    var files = this._findFileList( sourcePaths );

    var fileIndex;
    for ( fileIndex = 0; fileIndex < files.length; fileIndex++ )
    {
        this._roundPng( files[ fileIndex ], exportPath );
    }
};

//takes an array of source paths and expands any that are folders
PngRounder.prototype._findFileList = function( sourcePaths )
{
    var files = [];

    var pathIndex;
    for ( pathIndex = 0; pathIndex < sourcePaths.length; pathIndex++ )
    {
        var path = sourcePaths[ pathIndex ];

        if ( fs.lstatSync( path ).isDirectory() )
        {
            var dirContents = fs.readdirSync( path );
            var subFileIndex;
            for ( subFileIndex = 0; subFileIndex < dirContents.length; subFileIndex++ )
            {
                var subFile = dirContents[ subFileIndex ];
                if ( subFile.substring( subFile.lastIndexOf( "." ) + 1 ).toLowerCase() === "png" )
                {
                    files.push( path + "/" + subFile );
                }
            }
        }
        else
        {
            files.push( path );
        }
    }

    return files;
};

//rounds a single PNG found at path, and puts the new one at exportPath/filename.png
PngRounder.prototype._roundPng = function( path, exportPath )
{
    var radiusFactor = this.radiusFactor;
    var edgeSizeFactor = this.edgeSizeFactor;
    var fileName = path.substring( path.lastIndexOf( "/" ) + 1 );
    var transparencyRange = this.transparencyRange;

    fs.createReadStream( path ).pipe( new PNG({filterType: 4}) ).on('parsed', function()
    {
        var center = { x: Math.floor( this.width / 2 ), y: Math.floor( this.height / 2 ) };
        var radius = radiusFactor * Math.min( this.width, this.height );

        var edgeSize = { width: edgeSizeFactor * this.width, height: edgeSizeFactor * this.height };
        var imageRect = { minX: edgeSize.width,
                          minY: edgeSize.height,
                          maxX: this.width  - edgeSize.width,
                          maxY: this.height - edgeSize.height };

        var x;
        var y;
        for ( y = 0; y < this.height; y++ )
        {
            for ( x = 0; x < this.width; x++ )
            {
                var idx = (this.width * y + x) << 2;
                var transparentPixel = false;

                if ( x < imageRect.minX || y < imageRect.minY || x > imageRect.maxX || y > imageRect.maxY )
                {
                    transparentPixel = true;
                }
                else if ( 255 - this.data[idx] + 255 - this.data[idx+1] + 255 - this.data[idx+2] <= transparencyRange )
                {
                    transparentPixel = true;
                }
                else
                {
                    var diff = { x: x - center.x, y: y - center.y };
                    var dist = Math.sqrt( diff.x * diff.x + diff.y * diff.y );

                    if ( dist > radius )
                    {
                        transparentPixel = true;
                    }
                }

                if ( transparentPixel )
                {
                    //this is the alpha value (values are packed in 4s: RGBA in the data array)
                    this.data[idx+3] = 0;
                }
            }
        }

        this.pack().pipe( fs.createWriteStream( exportPath + "/" + fileName ) );
    });
};

module.exports = new PngRounder();<|endoftext|>


<commit_before>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

"use strict";

/**
 * Base class for a DOM based view. Uses debounced rendering using RequestAnimationFrame
 * for optimal performance. Supports easy DOM event managment for view content.
 */
core.Class("core.view.Dom",
{
  include : [core.view.Abstract],
  implement : [core.view.IView],

  construct: function(presenter, root)
  {
    core.view.Abstract.call(this, presenter);

    this.__renderRequestBound = core.Function.bind(this.__renderRequest, this);

    if (root != null)
    {
      if (jasy.Env.isSet("debug")) {
        core.Assert.isType(root, "Node", "Invalid root node!");
      }

      this.setRoot(root);
    }
  },

  properties :
  {
    /** Instance of compiled template to produce final data / text for output e.g. HTML, JSON, ... */
    template :
    {
      type: core.template.Template,
      nullable : true,
      apply : function() {
        this.render();
      }
    },

    /** The root element to render into */
    root :
    {
      type: "Node",
      nullable : true,
      apply : function(value, old) {
        this.render();
      }
    }
  },

  events :
  {
    /** Fired whenever the view has been rendered */
    render : core.event.Simple

  },

  members :
  {
    /*
    ======================================================
      DEBOUNCED RENDER LOGIC
    ======================================================
    */

    // Interface implementation
    render : function()
    {
      if (this.__renderScheduled != null) {
        return;
      }

      this.__renderScheduled = core.effect.AnimationFrame.request(this.__renderRequestBound);
    },

    /**
     * This method checks for whether all required data is available and triggers
     * the rendering as soon as this happens. Until than it regularly re-evaluates
     * whether this case happens.
     */
    __renderRequest : function()
    {
      if (this.__loaded && this._shouldRender())
      {
        this.__render();
        this.__renderScheduled = null;
      }
      else
      {
        this.__renderScheduled = core.effect.AnimationFrame.request(this.__renderRequestBound);
      }
    },

    /**
     * Internal render method which is called in buffered mode
     * so that only one rendering happens per frame.
     */
    __render : function()
    {
      var presenter = this.getPresenter();
      var elem = this.getRoot();
      var template = this.getTemplate();

      if (!presenter || !elem || !template) {
        return;
      }

      var partials = this.getPartials();
      var labels = this.getLabels();

      this._beforeRender();
      elem.innerHTML = template.render(presenter, partials, labels);
      this._afterRender();

      // Let others know
      this.fireEvent("render");
    },

    _beforeRender : function() {
      // placeholder
    },

    _afterRender : function() {
      // placeholder
    },

    _shouldRender : function() {
      return true;
    },

    __loaded : false,

    markAsLoaded : function()
    {
      this.log("All assets loaded!");
      this.__loaded = true;
      this.render();
    },

    markAsFailed : function() {
      this.error("Unable to load required assets. Please retry!");
    },

    /*
    ======================================================
      PUBLIC API
    ======================================================
    */

    // Interface implementation
    show : function(approach)
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "block";
      this.fireEvent("show");
    },

    // Interface implementation
    hide : function(approach)
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "none";
      this.fireEvent("hide");
    },

    /**
     * {Object} Returns the event parent which is used to bubble view events, to
     */
    getEventParent : function() {
      return this.getPresenter();
    },

    /*
    ======================================================
      REMOTE ASSET LOADING
    ======================================================
    */

    /**
     * {core.event.Promise} Loads and registers the given partial from
     * a local @assetId {String}. Returns a promise for easy management.
     *
     * The name of the partial is auto extracted as the file name part
     * of the @assetId.
     */
    loadPartial : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      // Auto extract partial name from file name
      // Convention over configuration FTW
      var lastSlash = assetId.lastIndexOf("/");
      var lastDot = assetId.lastIndexOf(".");
      var name = assetId.slice(lastSlash+1, lastDot);

      core.io.Text.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load partial: " + uri);
          } else {
            this.log("Loaded partial: " + uri);
          }
        }

        // Enable stripping (to remove white spaces from formatting)
        var template = core.template.Compiler.compile(data.text, this.getLabels());
        this.addPartial(name, template);

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    },

    /**
     * {core.event.Promise} Loads and registers the given template from
     * a local @assetId {String}. Returns a promise for easy management.
     */
    loadTemplate : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      core.io.Text.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load template: " + uri);
          } else {
            this.log("Loaded template: " + uri);
          }
        }

        // Enable stripping (to remove white spaces from formatting)
        var template = core.template.Compiler.compile(data.text, this.getLabels());
        this.setTemplate(template);

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    },

    /**
     * {core.event.Promise} Loads and injects the given stylesheet from
     * a local @assetId {String}. Returns a promise for easy management.
     */
    loadStyleSheet : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      core.io.StyleSheet.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load stylesheet: " + uri);
          } else {
            this.log("Loaded stylesheet: " + uri);
          }
        }

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    }
  }
});
<commit_msg>Don't hide error message.<commit_after>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

"use strict";

/**
 * Base class for a DOM based view. Uses debounced rendering using RequestAnimationFrame
 * for optimal performance. Supports easy DOM event managment for view content.
 */
core.Class("core.view.Dom",
{
  include : [core.view.Abstract],
  implement : [core.view.IView],

  construct: function(presenter, root)
  {
    core.view.Abstract.call(this, presenter);

    this.__renderRequestBound = core.Function.bind(this.__renderRequest, this);

    if (root != null)
    {
      if (jasy.Env.isSet("debug")) {
        core.Assert.isType(root, "Node", "Invalid root node!");
      }

      this.setRoot(root);
    }
  },

  properties :
  {
    /** Instance of compiled template to produce final data / text for output e.g. HTML, JSON, ... */
    template :
    {
      type: core.template.Template,
      nullable : true,
      apply : function() {
        this.render();
      }
    },

    /** The root element to render into */
    root :
    {
      type: "Node",
      nullable : true,
      apply : function(value, old) {
        this.render();
      }
    }
  },

  events :
  {
    /** Fired whenever the view has been rendered */
    render : core.event.Simple

  },

  members :
  {
    /*
    ======================================================
      DEBOUNCED RENDER LOGIC
    ======================================================
    */

    // Interface implementation
    render : function()
    {
      if (this.__renderScheduled != null) {
        return;
      }

      this.__renderScheduled = core.effect.AnimationFrame.request(this.__renderRequestBound);
    },

    /**
     * This method checks for whether all required data is available and triggers
     * the rendering as soon as this happens. Until than it regularly re-evaluates
     * whether this case happens.
     */
    __renderRequest : function()
    {
      if (this.__loaded && this._shouldRender())
      {
        this.__render();
        this.__renderScheduled = null;
      }
      else
      {
        this.__renderScheduled = core.effect.AnimationFrame.request(this.__renderRequestBound);
      }
    },

    /**
     * Internal render method which is called in buffered mode
     * so that only one rendering happens per frame.
     */
    __render : function()
    {
      var presenter = this.getPresenter();
      var elem = this.getRoot();
      var template = this.getTemplate();

      if (!presenter || !elem || !template) {
        return;
      }

      var partials = this.getPartials();
      var labels = this.getLabels();

      this._beforeRender();
      elem.innerHTML = template.render(presenter, partials, labels);
      this._afterRender();

      // Let others know
      this.fireEvent("render");
    },

    _beforeRender : function() {
      // placeholder
    },

    _afterRender : function() {
      // placeholder
    },

    _shouldRender : function() {
      return true;
    },

    __loaded : false,

    markAsLoaded : function()
    {
      this.log("All assets loaded!");
      this.__loaded = true;
      this.render();
    },

    markAsFailed : function(msg) {
      this.error("Unable to load required assets: " + msg);
    },

    /*
    ======================================================
      PUBLIC API
    ======================================================
    */

    // Interface implementation
    show : function(approach)
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "block";
      this.fireEvent("show");
    },

    // Interface implementation
    hide : function(approach)
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "none";
      this.fireEvent("hide");
    },

    /**
     * {Object} Returns the event parent which is used to bubble view events, to
     */
    getEventParent : function() {
      return this.getPresenter();
    },

    /*
    ======================================================
      REMOTE ASSET LOADING
    ======================================================
    */

    /**
     * {core.event.Promise} Loads and registers the given partial from
     * a local @assetId {String}. Returns a promise for easy management.
     *
     * The name of the partial is auto extracted as the file name part
     * of the @assetId.
     */
    loadPartial : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      // Auto extract partial name from file name
      // Convention over configuration FTW
      var lastSlash = assetId.lastIndexOf("/");
      var lastDot = assetId.lastIndexOf(".");
      var name = assetId.slice(lastSlash+1, lastDot);

      core.io.Text.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load partial: " + uri);
          } else {
            this.log("Loaded partial: " + uri);
          }
        }

        // Enable stripping (to remove white spaces from formatting)
        var template = core.template.Compiler.compile(data.text, this.getLabels());
        this.addPartial(name, template);

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    },

    /**
     * {core.event.Promise} Loads and registers the given template from
     * a local @assetId {String}. Returns a promise for easy management.
     */
    loadTemplate : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      core.io.Text.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load template: " + uri);
          } else {
            this.log("Loaded template: " + uri);
          }
        }

        // Enable stripping (to remove white spaces from formatting)
        var template = core.template.Compiler.compile(data.text, this.getLabels());
        this.setTemplate(template);

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    },

    /**
     * {core.event.Promise} Loads and injects the given stylesheet from
     * a local @assetId {String}. Returns a promise for easy management.
     */
    loadStyleSheet : function(assetId)
    {
      var promise = core.event.Promise.obtain();

      core.io.StyleSheet.load(jasy.Asset.toUri(assetId), function(uri, errornous, data)
      {
        if (jasy.Env.isSet("debug"))
        {
          if (errornous) {
            this.error("Failed to load stylesheet: " + uri);
          } else {
            this.log("Loaded stylesheet: " + uri);
          }
        }

        errornous ? promise.reject("io") : promise.fulfill(data);
        promise.release();
      }, this);

      return promise;
    }
  }
});
<|endoftext|>


<commit_before>// Karma configuration

module.exports = function(config) {
	var customLaunchers = {
		sl_chrome_latest: {
			base: 'SauceLabs',
			browserName: 'chrome',
			version: 'latest'
		},
		sl_chrome_previous: {
			base: 'SauceLabs',
			browserName: 'chrome',
			version: 'latest-1'
		},
		sl_firefox_latest: {
			base: 'SauceLabs',
			browserName: 'firefox',
			version: 'latest'
		},
		sl_firefox_previous: {
			base: 'SauceLabs',
			browserName: 'firefox',
			version: 'latest-1'
		},
		sl_edge_latest: {
			base: 'SauceLabs',
      		browserName: 'microsoftedge',
      		platform: 'Windows 10',
      		version: 'latest'
		},
		sl_edge_previous: {
			base: 'SauceLabs',
      		browserName: 'microsoftedge',
      		platform: 'Windows 10',
      		version: 'latest-1'
		},
		sl_safari_latest: {
			base: 'SauceLabs',
			browserName: 'safari',
			platform: 'macOS 10.12',
			version: 'latest'
		},
		sl_safari_previous: {
			base: 'SauceLabs',
			browserName: 'safari',
			platform: 'macOS 10.12',
			version: 'latest-1'
		},
		sl_ie_11: {
			base: 'SauceLabs',
			browserName: 'internet explorer',
			platform: 'Windows 7',
			version: '11.0'
		},
		sl_ie_10: {
			base: 'SauceLabs',
			browserName: 'internet explorer',
			platform: 'Windows 7',
			version: '10.0'
		}
	}

	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['mocha', 'fixture', 'sinon-chai', 'chai-dom', 'chai', 'calling'],

		// list of files / patterns to load in the browser
		files: [
			'dist/scrollmarks.js',
			'test/helpers/**/*',
			'test/*.spec.js',
			'test/fixtures/**/*'
		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			'dist/scrollmarks.js': ['eslint', 'coverage'],
			'**/*.html': ['html2js']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['dots', 'saucelabs', 'coverage', 'coveralls'],

		coverageReporter: {
			type : 'lcov',
			dir: 'coverage/'
		},

		// web server port
		port: 9876,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_INFO,

		sauceLabs: {
			build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
			startConnect: true,
			tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
		},

		customLaunchers: customLaunchers,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: Object.keys(customLaunchers),

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true,

		// Concurrency level
		// how many browser should be started simultaneous
		concurrency: 4
	})
}<commit_msg>Add iOS Safari<commit_after>// Karma configuration

module.exports = function(config) {
	var customLaunchers = {
		sl_chrome_latest: {
			base: 'SauceLabs',
			browserName: 'chrome',
			version: 'latest'
		},
		sl_chrome_previous: {
			base: 'SauceLabs',
			browserName: 'chrome',
			version: 'latest-1'
		},
		sl_firefox_latest: {
			base: 'SauceLabs',
			browserName: 'firefox',
			version: 'latest'
		},
		sl_firefox_previous: {
			base: 'SauceLabs',
			browserName: 'firefox',
			version: 'latest-1'
		},
		sl_edge_latest: {
			base: 'SauceLabs',
      		browserName: 'microsoftedge',
      		platform: 'Windows 10',
      		version: 'latest'
		},
		sl_edge_previous: {
			base: 'SauceLabs',
      		browserName: 'microsoftedge',
      		platform: 'Windows 10',
      		version: 'latest-1'
		},
		sl_safari_latest: {
			base: 'SauceLabs',
			browserName: 'safari',
			platform: 'macOS 10.12',
			version: 'latest'
		},
		sl_safari_previous: {
			base: 'SauceLabs',
			browserName: 'safari',
			platform: 'macOS 10.12',
			version: 'latest-1'
		},
		sl_ie_11: {
			base: 'SauceLabs',
			browserName: 'internet explorer',
			platform: 'Windows 7',
			version: '11.0'
		},
		sl_ie_10: {
			base: 'SauceLabs',
			browserName: 'internet explorer',
			platform: 'Windows 7',
			version: '10.0'
		},
		sl_ios_safari_latest: {
			base: 'SauceLabs',
			browserName: 'Safari',
			appiumVersion: '1.7.1',
			deviceName: 'iPhone 8 Simulator',
			deviceOrientation: 'portrait',
			platformVersion: '11.0',
			platformName: 'iOS'
		},
		sl_ios_safari_previous: {
			base: 'SauceLabs',
			browserName: 'Safari',
			appiumVersion: '1.7.1',
			deviceName: 'iPhone 7 Simulator',
			deviceOrientation: 'portrait',
			platformVersion: '10.3',
			platformName: 'iOS'
		}
	}

	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['mocha', 'fixture', 'sinon-chai', 'chai-dom', 'chai', 'calling'],

		// list of files / patterns to load in the browser
		files: [
			'dist/scrollmarks.js',
			'test/helpers/**/*',
			'test/*.spec.js',
			'test/fixtures/**/*'
		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			'dist/scrollmarks.js': ['eslint', 'coverage'],
			'**/*.html': ['html2js']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['dots', 'saucelabs', 'coverage', 'coveralls'],

		coverageReporter: {
			type : 'lcov',
			dir: 'coverage/'
		},

		// web server port
		port: 9876,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_INFO,

		sauceLabs: {
			build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
			startConnect: true,
			tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
		},

		customLaunchers: customLaunchers,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: Object.keys(customLaunchers),

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true,

		// Concurrency level
		// how many browser should be started simultaneous
		concurrency: 4
	})
}<|endoftext|>


<commit_before>var StaticCanvasMap;
var ZoomableCanvasMap;

! function() {
    "use strict";

    // TODO use turf inside as a dependency?
    // Copied from turf.inside
    function inside(pt, polygon) {
        var polys = polygon.geometry.coordinates
        // normalize to multipolygon
        if (polygon.geometry.type === 'Polygon')
            polys = [polys]

        var insidePoly = false
        var i = 0
        while (i < polys.length && !insidePoly) {
            // check if it is in the outer ring first
            if (inRing(pt, polys[i][0])) {
                var inHole = false
                var k = 1
                // check for the point in any of the holes
                while (k < polys[i].length && !inHole) {
                    if (inRing(pt, polys[i][k])) {
                        inHole = true
                    }
                    k++
                }
                if(!inHole)
                    insidePoly = true
            }
            i++
        }
        return insidePoly
    }

    // pt is [x,y] and ring is [[x,y], [x,y],..]
    function inRing (pt, ring) {
        var isInside = false
        for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
            var xi = ring[i][0], yi = ring[i][1]
            var xj = ring[j][0], yj = ring[j][1]
            var intersect = ((yi > pt[1]) !== (yj > pt[1])) &&
                (pt[0] < (xj - xi) * (pt[1] - yi) / (yj - yi) + xi)
            if (intersect) isInside = !isInside
        }
        return isInside
    }

    function maxBounds(one, two) {
        var bounds = two
        if (one[0][0] < two[0][0])
            bounds[0][0] = one[0][0]
        if (one[0][1] < two[0][1])
            bounds[0][1] = one[0][1]
        if (one[1][0] > two[1][0])
            bounds[1][0] = one[1][0]
        if (one[1][1] > two[1][1])
            bounds[1][1] = one[1][1]
        return bounds
    }

    function createRTree(element, dataPath) {
        element.lookupTree = rbush(4)
        for (var j in element.features.features) {
            var bounds = dataPath.bounds(element.features.features[j])
            // FIXME does bulk insert work faster?
            element.lookupTree.insert([
                bounds[0][0].toFixed(0),
                bounds[0][1].toFixed(0),
                bounds[1][0].toFixed(0),
                bounds[1][1].toFixed(0),
                element.features.features[j]
            ])
        }
    }

    function CanvasMap(parameters) {
        var settings = jQuery.extend({
                height: 200,
                width: $(parameters.element).innerWidth(),
                ratio: 1,
                area: 0,
                scale: 1,
                translate: [0, 0],
                background: null,
                backgroundScale: 1,
                backgroundTranslate: [0, 0],
                map: this
            }, parameters),
            simplify = d3.geo.transform({
                point: function(x, y, z) {
                    if (z >= settings.area) this.stream.point(x, y)
                }
            }),
            canvas = null,
            context = null

        if (parameters.projection) {
            var dataPath = d3.geo.path().projection({
                    stream: function(s) {
                        return simplify.stream(settings.projection.stream(s))
                    }
                })
        } else {
            var b = [[Number.MAX_VALUE, Number.MAX_VALUE],
                     [Number.MIN_VALUE, Number.MIN_VALUE]]
            for (var i in settings.data) {
                var featureBounds = d3.geo.bounds(settings.data[i].features)
                b = maxBounds(featureBounds, b)
            }
            settings.projection = d3.geo.mercator()
                .scale(1)
                .center([(b[1][0] + b[0][0]) / 2, (b[1][1] + b[0][1]) / 2])
            var dataPath = d3.geo.path().projection({
                    stream: function(s) {
                        return simplify.stream(settings.projection.stream(s))
                    }
                })
            b = [[Number.MAX_VALUE, Number.MAX_VALUE],
                 [Number.MIN_VALUE, Number.MIN_VALUE]]
            for (var i in settings.data) {
                featureBounds = dataPath.bounds(settings.data[i].features)
                b = maxBounds(featureBounds, b)
            }

            var dx = b[1][0] - b[0][0],
                dy = b[1][1] - b[0][1],
                scale = 0.9 * (settings.width / dx)

            settings.height = Math.ceil(dy * settings.width / dx)
            settings.projection.scale(scale)
                .translate([settings.width / 2, settings.height / 2])
        }
        $(this).height(settings.height)

        // TODO make this code more playful

        function init() {
            canvas = d3.select(settings.element)
                .append("canvas")
                .on("click", click)
                .on("mousemove", hover)
            context = canvas.node().getContext("2d")

            var devicePixelRatio = window.devicePixelRatio || 1,
                backingStoreRatio = context.webkitBackingStorePixelRatio ||
                context.mozBackingStorePixelRatio ||
                context.msBackingStorePixelRatio ||
                context.oBackingStorePixelRatio ||
                context.backingStorePixelRatio || 1
            settings.ratio = devicePixelRatio / backingStoreRatio
            settings.area = 1 / settings.projection.scale() / settings.ratio

            canvas.attr("width", settings.width * settings.ratio)
            canvas.attr("height", settings.height * settings.ratio)
            canvas.style("width", settings.width + "px")
            canvas.style("height", settings.height + "px")
            context.lineJoin = "round"
            context.lineCap = "round"

            dataPath.context(context)
            context.clearRect(0, 0, settings.width * settings.ratio, settings.height * settings.ratio)
            context.save()
            // TODO why does this line break it?
            context.scale(settings.ratio, settings.ratio)

            // TODO move rtree part out?
            for (var i in settings.data) {
                createRTree(settings.data[i], dataPath)
            }

            settings.background = new Image()
            settings.backgroundScale = settings.scale
            settings.backgroundTranslate = settings.translate
            var callback = function() {
                for (var i in settings.data) {
                    var element = settings.data[i]

                    if (element.dynamicpaint)
                        element.dynamicpaint(context, dataPath, null)
                }

                context.restore()
            }
            saveBackground(canvas, dataPath, settings.background, callback)

            //Prevent another call to the init method
            this.init = function() {}
        }

        // TODO later when saving while zooming
        // We need a duplicated datapath with a different area to do the painting, as well as a different canvas
        // This should be created in another class
        // Then we can use the paintElement class, to paint stuff at each zoom step :)
        // With the hope that it will be sorta smooth
        // need to test on mobile as well
        function paintBackgroundElement(element, saveDataPath) {
            element.prepaint(saveDataPath.context())
            for (var j in element.features.features) {
                saveDataPath.context().beginPath()
                saveDataPath(element.features.features[j])
                element.paintfeature(saveDataPath.context(), element.features.features[j])
            }
            element.postpaint(saveDataPath.context())
        }

        function saveBackground(saveCanvas, saveDataPath, background, callback) {
            for (var i in settings.data) {
                var element = settings.data[i]
                paintBackgroundElement(element, saveDataPath)
            }

            background.onload = callback
            background.src = saveCanvas.node().toDataURL()
        }

        function paint() {
            console.log("PAINT")
            context.save()
            context.clearRect(0, 0, settings.width * settings.ratio, settings.height * settings.ratio)
            context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
            context.translate(settings.translate[0], settings.translate[1])

            var imageTranslate = [(settings.backgroundTranslate[0] - settings.translate[0])
                    * settings.backgroundScale * settings.ratio,
                (settings.backgroundTranslate[1] - settings.translate[1])
                    * settings.backgroundScale * settings.ratio],
                translatedZero = translatePoint([0, 0]),
                translatedMax = translatePoint([settings.width, settings.height])

            console.log(settings.translate)
            console.log("Image dimensions are " + settings.background.width + " x " + settings.background.height)
            console.log("We are showing")
            var imageWidth = Math.floor((translatedMax[0] - translatedZero[0]) * settings.backgroundScale * settings.ratio)
            var imageHeight = Math.floor((translatedMax[1] - translatedZero[1]) * settings.backgroundScale * settings.ratio)

            console.log("X: " + imageTranslate[0] + " Y: " + (imageTranslate[1]) + " width: " + imageWidth + " height " + imageHeight)
            context.drawImage(settings.background,
                imageTranslate[0], imageTranslate[1],
                Math.floor((translatedMax[0] - translatedZero[0]) * settings.backgroundScale * settings.ratio),
                Math.floor((translatedMax[1] - translatedZero[1]) * settings.backgroundScale * settings.ratio),
                translatedZero[0], translatedZero[1],
                translatedMax[0] - translatedZero[0],
                translatedMax[1] - translatedZero[1])

            // FIXME this needs a way for the callback to use the lookupTree?
            for (var i in settings.data) {
                var element = settings.data[i]
                console.log(element.hoverElement)
                if (element.dynamicpaint)
                    element.dynamicpaint(context, dataPath, element.hoverElement)
            }

            context.restore()
        }

        function translatePoint(point) {
            return [point[0] / settings.scale - settings.translate[0],
                point[1] / settings.scale - settings.translate[1]
            ]
        }

        function click() {
            var point = translatePoint(d3.mouse(this))

            for (var i in settings.data) {
                var element = settings.data[i]
                if (!element.click)
                    continue

                var lookup = element.lookupTree.search([point[0], point[1], point[0], point[1]])
                for (var j in lookup) {
                    var feature = lookup[j][4]
                    if (inside(settings.projection.invert(point), feature))
                        element.click(settings.map, feature)
                }
            }
        }

        function hover() {
            var point = translatePoint(d3.mouse(this)),
                repaint = false

            console.log("HOVER")

            for (var i in settings.data) {
                var element = settings.data[i]
                var lookup = element.lookupTree.search([point[0], point[1], point[0], point[1]])
                var isInside = false
                for (var j in lookup) {
                    var feature = lookup[j][4]
                    isInside = false
                    if (inside(settings.projection.invert(point), feature)) {
                        isInside = true
                        console.log(feature)
                        if (element.hoverElement == feature) // FIXME is this mutability hack a good thang?
                            continue
                        element.hoverElement = feature
                        repaint = true
                    }
                    isInside || !element.hoverElement || (element.hoverElement = false, repaint = true)
                    console.log(element.hoverElement)
                }
            }
            repaint && paint()
        }

        this.init = init
        this.paint = paint
        this.settings = function() {
            return settings
        }
        this.saveBackground = saveBackground
    }

    StaticCanvasMap = function(parameters) {
        var map = new CanvasMap(parameters)

        this.init = function() {
            map.init()
        }
        this.paint = function() {
            map.paint()
        }
    }

    ZoomableCanvasMap = function(parameters) {
        // TODO define api for zoom polygons?
        // handle clicks, zoom into polygon
        // FIXME how to handle zoom outs?
        var map = new CanvasMap(parameters),
            simplify = d3.geo.transform({
                point: function(x, y, z) {
                    if (z >= area) this.stream.point(x, y)
                }
            }),
            area = 0,
            canvas = null,
            context = null,
            settings = map.settings(),
            dataPath = d3.geo.path().projection({
                stream: function(s) {
                    return simplify.stream(settings.projection.stream(s))
                }
            })

        settings.map = this

        this.init = function() {
            map.init()

            canvas = d3.select(settings.element)
                .append("canvas")
            context = canvas.node().getContext("2d")
            area = 1 / settings.projection.scale() / settings.ratio

            canvas.attr("width", settings.width * settings.ratio)
            canvas.attr("height", settings.height * settings.ratio)
            canvas.style("width", settings.width + "px")
            canvas.style("height", settings.height + "px")
            context.lineJoin = "round"
            context.lineCap = "round"

            dataPath.context(context)

            /*var scale = 1.2, translate = [0, 0]
            settings.scale = scale
            settings.translate = translate
            area = 1 / settings.projection.scale() / settings.scale / settings.ratio

            context.save()
            context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
            context.translate(settings.translate[0], settings.translate[1])
            map.saveBackground(canvas, dataPath, settings.background, function() {
                context.restore()
                settings.backgroundScale = settings.scale
                settings.backgroundTranslate = settings.translate
                map.paint()
            })*/
        }
        this.paint = function() {
            map.paint()
        }
        this.zoom = function(d) {
            console.log("ZOOM")
            var bounds = dataPath.bounds(d),
                dx = bounds[1][0] - bounds[0][0],
                dy = bounds[1][1] - bounds[0][1],
                bx = (bounds[0][0] + bounds[1][0]) / 2,
                by = (bounds[0][1] + bounds[1][1]) / 2,
                scale = 0.7 * // TODO bring back zoomScaleFactor?
                    Math.min(settings.width / dx, settings.height / dy),
                translate = [-bx + settings.width / scale / 2,
                             -by + settings.height / scale / 2]

            console.log(bx)
            console.log(by)
            /*d3.transition()
                .duration(300)
                .ease("linear")
                .tween("zoom", function() {
                    var i = d3.interpolateNumber(settings.scale, scale)
                    var interpolatedTranslate = d3.interpolateArray(settings.translate, translate)
                    var otherOldTranslate = settings.translate
                    area = 1 / scale / settings.projection.scale() / 4
                    return function(t) {
                        settings.scale = i(t)
                        var iT = interpolatedTranslate(t)
                        settings.translate = [otherOldTranslate[0] + (iT[0] - otherOldTranslate[0]) * scale / i(t),
                            otherOldTranslate[1] + (iT[1] - otherOldTranslate[1]) * scale / i(t)
                        ]
                        map.paint()
                    }
                })
                .each("end", function() {*/
                    settings.scale = scale
                    settings.translate = translate
                    area = 1 / settings.projection.scale() / settings.scale / settings.ratio

                    /*context.save()
                    context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
                    context.translate(settings.translate[0], settings.translate[1])
                    console.log("SAVE BG")
                    map.saveBackground(canvas, dataPath, settings.background, function() {
                        console.log("PAINT")
                        context.restore()
                        settings.backgroundScale = settings.scale
                        settings.backgroundTranslate = settings.translate*/
                        map.paint()
                    //})
                //})
        }
    }
}()
<commit_msg>Put a note<commit_after>var StaticCanvasMap;
var ZoomableCanvasMap;

! function() {
    "use strict";

    // TODO use turf inside as a dependency?
    // Copied from turf.inside
    function inside(pt, polygon) {
        var polys = polygon.geometry.coordinates
        // normalize to multipolygon
        if (polygon.geometry.type === 'Polygon')
            polys = [polys]

        var insidePoly = false
        var i = 0
        while (i < polys.length && !insidePoly) {
            // check if it is in the outer ring first
            if (inRing(pt, polys[i][0])) {
                var inHole = false
                var k = 1
                // check for the point in any of the holes
                while (k < polys[i].length && !inHole) {
                    if (inRing(pt, polys[i][k])) {
                        inHole = true
                    }
                    k++
                }
                if(!inHole)
                    insidePoly = true
            }
            i++
        }
        return insidePoly
    }

    // pt is [x,y] and ring is [[x,y], [x,y],..]
    function inRing (pt, ring) {
        var isInside = false
        for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
            var xi = ring[i][0], yi = ring[i][1]
            var xj = ring[j][0], yj = ring[j][1]
            var intersect = ((yi > pt[1]) !== (yj > pt[1])) &&
                (pt[0] < (xj - xi) * (pt[1] - yi) / (yj - yi) + xi)
            if (intersect) isInside = !isInside
        }
        return isInside
    }

    function maxBounds(one, two) {
        var bounds = two
        if (one[0][0] < two[0][0])
            bounds[0][0] = one[0][0]
        if (one[0][1] < two[0][1])
            bounds[0][1] = one[0][1]
        if (one[1][0] > two[1][0])
            bounds[1][0] = one[1][0]
        if (one[1][1] > two[1][1])
            bounds[1][1] = one[1][1]
        return bounds
    }

    function createRTree(element, dataPath) {
        element.lookupTree = rbush(4)
        for (var j in element.features.features) {
            var bounds = dataPath.bounds(element.features.features[j])
            // FIXME does bulk insert work faster?
            element.lookupTree.insert([
                bounds[0][0].toFixed(0),
                bounds[0][1].toFixed(0),
                bounds[1][0].toFixed(0),
                bounds[1][1].toFixed(0),
                element.features.features[j]
            ])
        }
    }

    function CanvasMap(parameters) {
        var settings = jQuery.extend({
                height: 200,
                width: $(parameters.element).innerWidth(),
                ratio: 1,
                area: 0,
                scale: 1,
                translate: [0, 0],
                background: null,
                backgroundScale: 1,
                backgroundTranslate: [0, 0],
                map: this
            }, parameters),
            simplify = d3.geo.transform({
                point: function(x, y, z) {
                    if (z >= settings.area) this.stream.point(x, y)
                }
            }),
            canvas = null,
            context = null

        if (parameters.projection) {
            var dataPath = d3.geo.path().projection({
                    stream: function(s) {
                        return simplify.stream(settings.projection.stream(s))
                    }
                })
        } else {
            var b = [[Number.MAX_VALUE, Number.MAX_VALUE],
                     [Number.MIN_VALUE, Number.MIN_VALUE]]
            for (var i in settings.data) {
                var featureBounds = d3.geo.bounds(settings.data[i].features)
                b = maxBounds(featureBounds, b)
            }
            settings.projection = d3.geo.mercator()
                .scale(1)
                .center([(b[1][0] + b[0][0]) / 2, (b[1][1] + b[0][1]) / 2])
            var dataPath = d3.geo.path().projection({
                    stream: function(s) {
                        return simplify.stream(settings.projection.stream(s))
                    }
                })
            b = [[Number.MAX_VALUE, Number.MAX_VALUE],
                 [Number.MIN_VALUE, Number.MIN_VALUE]]
            for (var i in settings.data) {
                featureBounds = dataPath.bounds(settings.data[i].features)
                b = maxBounds(featureBounds, b)
            }

            var dx = b[1][0] - b[0][0],
                dy = b[1][1] - b[0][1],
                scale = 0.9 * (settings.width / dx)

            settings.height = Math.ceil(dy * settings.width / dx)
            settings.projection.scale(scale)
                .translate([settings.width / 2, settings.height / 2])
        }
        $(this).height(settings.height)

        // TODO make this code more playful

        function init() {
            canvas = d3.select(settings.element)
                .append("canvas")
                .on("click", click)
                .on("mousemove", hover)
            context = canvas.node().getContext("2d")

            var devicePixelRatio = window.devicePixelRatio || 1,
                backingStoreRatio = context.webkitBackingStorePixelRatio ||
                context.mozBackingStorePixelRatio ||
                context.msBackingStorePixelRatio ||
                context.oBackingStorePixelRatio ||
                context.backingStorePixelRatio || 1
            settings.ratio = devicePixelRatio / backingStoreRatio
            settings.area = 1 / settings.projection.scale() / settings.ratio

            canvas.attr("width", settings.width * settings.ratio)
            canvas.attr("height", settings.height * settings.ratio)
            canvas.style("width", settings.width + "px")
            canvas.style("height", settings.height + "px")
            context.lineJoin = "round"
            context.lineCap = "round"

            dataPath.context(context)
            context.clearRect(0, 0, settings.width * settings.ratio, settings.height * settings.ratio)
            context.save()
            context.scale(settings.ratio, settings.ratio)

            // TODO move rtree part out?
            for (var i in settings.data) {
                createRTree(settings.data[i], dataPath)
            }

            settings.background = new Image()
            settings.backgroundScale = settings.scale
            settings.backgroundTranslate = settings.translate
            var callback = function() {
                for (var i in settings.data) {
                    var element = settings.data[i]

                    if (element.dynamicpaint)
                        element.dynamicpaint(context, dataPath, null)
                }

                context.restore()
            }
            saveBackground(canvas, dataPath, settings.background, callback)

            //Prevent another call to the init method
            this.init = function() {}
        }

        // TODO later when saving while zooming
        // We need a duplicated datapath with a different area to do the painting, as well as a different canvas
        // This should be created in another class
        // Then we can use the paintElement class, to paint stuff at each zoom step :)
        // With the hope that it will be sorta smooth
        // need to test on mobile as well
        function paintBackgroundElement(element, saveDataPath) {
            element.prepaint(saveDataPath.context())
            for (var j in element.features.features) {
                saveDataPath.context().beginPath()
                saveDataPath(element.features.features[j])
                element.paintfeature(saveDataPath.context(), element.features.features[j])
            }
            element.postpaint(saveDataPath.context())
        }

        function saveBackground(saveCanvas, saveDataPath, background, callback) {
            for (var i in settings.data) {
                var element = settings.data[i]
                paintBackgroundElement(element, saveDataPath)
            }

            background.onload = callback
            background.src = saveCanvas.node().toDataURL()
        }

        function paint() {
            console.log("PAINT")
            context.save()
            context.clearRect(0, 0, settings.width * settings.ratio, settings.height * settings.ratio)
            context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
            context.translate(settings.translate[0], settings.translate[1])

            var imageTranslate = [(settings.backgroundTranslate[0] - settings.translate[0])
                    * settings.backgroundScale * settings.ratio,
                (settings.backgroundTranslate[1] - settings.translate[1])
                    * settings.backgroundScale * settings.ratio],
                translatedZero = translatePoint([0, 0]),
                translatedMax = translatePoint([settings.width, settings.height])

            console.log(settings.translate)
            console.log("Image dimensions are " + settings.background.width + " x " + settings.background.height)
            console.log("We are showing")
            var imageWidth = Math.floor((translatedMax[0] - translatedZero[0]) * settings.backgroundScale * settings.ratio)
            var imageHeight = Math.floor((translatedMax[1] - translatedZero[1]) * settings.backgroundScale * settings.ratio)

            console.log("X: " + imageTranslate[0] + " Y: " + (imageTranslate[1]) + " width: " + imageWidth + " height " + imageHeight)
            context.drawImage(settings.background,
                imageTranslate[0], imageTranslate[1],
                Math.floor((translatedMax[0] - translatedZero[0]) * settings.backgroundScale * settings.ratio),
                Math.floor((translatedMax[1] - translatedZero[1]) * settings.backgroundScale * settings.ratio),
                translatedZero[0], translatedZero[1],
                translatedMax[0] - translatedZero[0],
                translatedMax[1] - translatedZero[1])

            // FIXME this needs a way for the callback to use the lookupTree?
            for (var i in settings.data) {
                var element = settings.data[i]
                console.log(element.hoverElement)
                if (element.dynamicpaint)
                    element.dynamicpaint(context, dataPath, element.hoverElement)
            }

            context.restore()
        }

        function translatePoint(point) {
            return [point[0] / settings.scale - settings.translate[0],
                point[1] / settings.scale - settings.translate[1]
            ]
        }

        function click() {
            var point = translatePoint(d3.mouse(this))

            for (var i in settings.data) {
                var element = settings.data[i]
                if (!element.click)
                    continue

                var lookup = element.lookupTree.search([point[0], point[1], point[0], point[1]])
                for (var j in lookup) {
                    var feature = lookup[j][4]
                    if (inside(settings.projection.invert(point), feature))
                        element.click(settings.map, feature)
                }
            }
        }

        function hover() {
            var point = translatePoint(d3.mouse(this)),
                repaint = false

            console.log("HOVER")

            for (var i in settings.data) {
                var element = settings.data[i]
                var lookup = element.lookupTree.search([point[0], point[1], point[0], point[1]])
                var isInside = false
                for (var j in lookup) {
                    var feature = lookup[j][4]
                    isInside = false
                    if (inside(settings.projection.invert(point), feature)) {
                        isInside = true
                        console.log(feature)
                        if (element.hoverElement == feature) // FIXME is this mutability hack a good thang?
                            continue
                        element.hoverElement = feature
                        repaint = true
                    }
                    isInside || !element.hoverElement || (element.hoverElement = false, repaint = true)
                    console.log(element.hoverElement)
                }
            }
            repaint && paint()
        }

        this.init = init
        this.paint = paint
        this.settings = function() {
            return settings
        }
        this.saveBackground = saveBackground
    }

    StaticCanvasMap = function(parameters) {
        var map = new CanvasMap(parameters)

        this.init = function() {
            map.init()
        }
        this.paint = function() {
            map.paint()
        }
    }

    ZoomableCanvasMap = function(parameters) {
        // TODO define api for zoom polygons?
        // handle clicks, zoom into polygon
        // FIXME how to handle zoom outs?
        var map = new CanvasMap(parameters),
            simplify = d3.geo.transform({
                point: function(x, y, z) {
                    if (z >= area) this.stream.point(x, y)
                }
            }),
            area = 0,
            canvas = null,
            context = null,
            settings = map.settings(),
            dataPath = d3.geo.path().projection({
                stream: function(s) {
                    return simplify.stream(settings.projection.stream(s))
                }
            })

        settings.map = this

        this.init = function() {
            map.init()

            canvas = d3.select(settings.element)
                .append("canvas")
            context = canvas.node().getContext("2d")
            area = 1 / settings.projection.scale() / settings.ratio

            canvas.attr("width", settings.width * settings.ratio)
            canvas.attr("height", settings.height * settings.ratio)
            canvas.style("width", settings.width + "px")
            canvas.style("height", settings.height + "px")
            context.lineJoin = "round"
            context.lineCap = "round"

            dataPath.context(context)

            var scale = 1.2, translate = [0, 0]
            settings.scale = scale
            settings.translate = translate
            area = 1 / settings.projection.scale() / settings.scale / settings.ratio

            // TODO fix backgroundScale stuff?
            /*context.save()
            context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
            context.translate(settings.translate[0], settings.translate[1])
            map.saveBackground(canvas, dataPath, settings.background, function() {
                context.restore()
                settings.backgroundScale = settings.scale
                //settings.backgroundTranslate = settings.translate
                map.paint()
            })*/
        }
        this.paint = function() {
            map.paint()
        }
        this.zoom = function(d) {
            console.log("ZOOM")
            var bounds = dataPath.bounds(d),
                dx = bounds[1][0] - bounds[0][0],
                dy = bounds[1][1] - bounds[0][1],
                bx = (bounds[0][0] + bounds[1][0]) / 2,
                by = (bounds[0][1] + bounds[1][1]) / 2,
                scale = 0.7 * // TODO bring back zoomScaleFactor?
                    Math.min(settings.width / dx, settings.height / dy),
                translate = [-bx + settings.width / scale / 2,
                             -by + settings.height / scale / 2]

            console.log(bx)
            console.log(by)
            /*d3.transition()
                .duration(300)
                .ease("linear")
                .tween("zoom", function() {
                    var i = d3.interpolateNumber(settings.scale, scale)
                    var interpolatedTranslate = d3.interpolateArray(settings.translate, translate)
                    var otherOldTranslate = settings.translate
                    area = 1 / scale / settings.projection.scale() / 4
                    return function(t) {
                        settings.scale = i(t)
                        var iT = interpolatedTranslate(t)
                        settings.translate = [otherOldTranslate[0] + (iT[0] - otherOldTranslate[0]) * scale / i(t),
                            otherOldTranslate[1] + (iT[1] - otherOldTranslate[1]) * scale / i(t)
                        ]
                        map.paint()
                    }
                })
                .each("end", function() {*/
                    settings.scale = scale
                    settings.translate = translate
                    area = 1 / settings.projection.scale() / settings.scale / settings.ratio

                    /*context.save()
                    context.scale(settings.scale * settings.ratio, settings.scale * settings.ratio)
                    context.translate(settings.translate[0], settings.translate[1])
                    console.log("SAVE BG")
                    map.saveBackground(canvas, dataPath, settings.background, function() {
                        console.log("PAINT")
                        context.restore()
                        settings.backgroundScale = settings.scale
                        settings.backgroundTranslate = settings.translate*/
                        map.paint()
                    //})
                //})
        }
    }
}()
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

const defaultTinymceConfig = {
  // ============================================================================
  // Values in this section have acceptable defaults which you may want
  // to override (though you really should provide content_css)
  // ============================================================================

  auto_focus: false,

  // any values provided (in an array) will be merged into the standard then sorted
  // you provide the translations
  block_formats: undefined,

  // replace to provide your own
  body_class: 'default-theme',

  // urls to branding css go here
  content_css: [],

  // these are up to you
  directionality: 'ltr',
  height: undefined, // '400px' is s the standard height
  language: 'en',

  // any menubar entries here will be appended to the standard menubar
  menubar: undefined,
  // any menus here will be merged into the standard menubar menus
  menu: undefined,

  // any toolbar data here will be merged into the standard toolbars
  toolbar: undefined,

  // plugins included here will be added to the standard set
  plugins: undefined,

  // ==================================================================================
  // values below this line define standard behavior and probably shouldn't be changed
  // but will be used if you do
  // ==================================================================================
  branding: false,
  browser_spellcheck: true,

  content_style: undefined, // this will always be provided by the RCE

  convert_urls: false,

  font_formats:
    "Lato=lato,Helvetica Neue,Helvetica,Arial,sans-serif; Balsamiq Sans=Balsamiq Sans,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Architect's Daughter=Architects Daughter,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol; Tahoma=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats",

  language_load: false,
  language_url: 'none',

  toolbar_mode: 'floating',
  toolbar_ticky: true,

  mobile: {theme: 'silver'},
  preview_styles:
    'font-family font-size font-weight font-style text-decoration text-transform border border-radius outline text-shadow',
  remove_script_host: true,
  resize: true,
  // will get set from textareaId
  // selector: '#textarea2',
  skin: false,
  statusbar: false,

  // part of unifying tinymce and canvas_sanitize's element whitelist
  // copied from
  // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@valid_elements/#defaultruleset
  // then edited.
  //
  // this list needs to be kept in sync with the list in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
  valid_elements:
    '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup|role],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt|title|hspace|vspace|width|height|align|role],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,kbd,label[for],legend,q[cite],samp,small,tt,var,big,figure,figcaption,source[media|sizes|src|srcset|type],track,mark,article,aside,details,footer,header,nav,section,summary,time',
  extended_valid_elements:
    '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|contextmenu|draggable|dropzone|hidden|longdesc|spellcheck|translate|align|role|aria-labelledby|aria-atomic|aria-busy|aria-controls|aria-describedby|aria-disabled|aria-dropeffect|aria-flowto|aria-grabbed|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-labelledby|aria-live|aria-owns|aria-relevant|aria-autocomplete|aria-checked|aria-disabled|aria-expanded|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-level|aria-multiline|aria-multiselectable|aria-orientation|aria-pressed|aria-readonly|aria-required|aria-selected|aria-sort|aria-valuemax|aria-valuemin|aria-valuenow|aria-valuetext],iframe[id|data-media-type|title|src|width|height|name|align|style|class|sandbox|allowfullscreen|webkitallowfullscreen|mozallowfullscreen|allow],i[iclass],a[hidden|href|target|rel|media|hreflang|type|charset|name|rev|shape|coords|download|alt],#p,li[value],-ol[reversed|start|type|compact],pre[width],table[border|summary|width|frame|rules|cellspacing|cellpadding|bgcolor],tbody[char|charoff|valign],td[colspan|rowspan|headers|abbr|axis|scope|align|char|charoff|valign|nowrap|bgcolor|width|height],tfoot[char|charoff|valign],th[colspan|rowspan|headers|scope|abbr|axis|align|char|charoff|valign|nowrap|bgcolor|width|height],thead[char|charoff|valign],tr[char|charoff|valign|bgcolor],-ul[compact],video[name|src|allowfullscreen|muted|poster|width|height|controls|playsinline],audio[name|src|muted|controls],annotation[href|xref|definitionURL|encoding|cd|name|src],annotation-xml[href|xref|definitionURL|encoding|cd|name|src],maction[href|xref|mathcolor|mathbackground|actiontype|selection],maligngroup[href|xref|mathcolor|mathbackground|groupalign],malignmark[href|xref|mathcolor|mathbackground|edge],math[xmlns|href|xref|display|maxwidth|overflow|altimg|altimg-width|altimg-height|altimg-valign|alttext|cdgroup|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],menclose[href|xref|mathcolor|mathbackground|notation],merror[href|xref|mathcolor|mathbackground],mfenced[href|xref|mathcolor|mathbackground|open|close|separators],mfrac[href|xref|mathcolor|mathbackground|linethickness|munalign|denomalign|bevelled],mglyph[href|xref|mathcolor|mathbackground|src|alt|width|height|valign],mi[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mlabeledtr[href|xref|mathcolor|mathbackground],mlongdiv[href|xref|mathcolor|mathbackground|longdivstyle|align|stackalign|charalign|charspacing],mmultiscripts[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],mn[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mo[href|xref|mathcolor|mathbackground|mathvariant|mathsize|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast],mover[href|xref|mathcolor|mathbackground|accent|align],mpadded[href|xref|mathcolor|mathbackground|height|depth|width|lspace|voffset],mphantom[href|xref|mathcolor|mathbackground],mprescripts[href|xref|mathcolor|mathbackground],mroot[href|xref|mathcolor|mathbackground],mrow[href|xref|mathcolor|mathbackground],ms[href|xref|mathcolor|mathbackground|mathvariant|mathsize|lquote|rquote],mscarries[href|xref|mathcolor|mathbackground|position|location|crossout|scriptsizemultiplier],mscarry[href|xref|mathcolor|mathbackground|location|crossout],msgroup[href|xref|mathcolor|mathbackground|position|shift],msline[href|xref|mathcolor|mathbackground|position|length|leftoverhang|rightoverhang|mslinethickness],mspace[href|xref|mathcolor|mathbackground|mathvariant|mathsize],msqrt[href|xref|mathcolor|mathbackground],msrow[href|xref|mathcolor|mathbackground|position],mstack[href|xref|mathcolor|mathbackground|align|stackalign|charalign|charspacing],mstyle[href|xref|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],msub[href|xref|mathcolor|mathbackground|subscriptshift],msubsup[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],msup[href|xref|mathcolor|mathbackground|superscriptshift],mtable[href|xref|mathcolor|mathbackground|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|width|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|displaystyle|side|minlabelspacing],mtd[href|xref|mathcolor|mathbackground|rowspan|columnspan|rowalign|columnalign|groupalign],mtext[href|xref|mathcolor|mathbackground|mathvariant|mathsize|width|height|depth|linebreak],mtr[href|xref|mathcolor|mathbackground|rowalign|columnalign|groupalign],munder[href|xref|mathcolor|mathbackground|accentunder|align],munderover[href|xref|mathcolor|mathbackground|accent|accentunder|align],none[href|xref|mathcolor|mathbackground],semantics[href|xref|definitionURL|encoding],picture,ruby,rp,rt,' +
    // the svg necessary for the uploading placeholder's spinner
    'svg[*],g[*],circle[*]',
  non_empty_elements:
    'td th iframe video audio object script a i area base basefont br col frame hr img input isindex link meta param embed source wbr track ruby',

  // tiny's external link create/edit dialog config
  target_list: false, // don't show the target list when creating/editing links
  link_title: false, // don't show the title input when creating/editing links
  default_link_target: '_blank'
}
export default defaultTinymceConfig
<commit_msg>treesame commit of origin/stable/2021-07-07<commit_after>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

const defaultTinymceConfig = {
  // ============================================================================
  // Values in this section have acceptable defaults which you may want
  // to override (though you really should provide content_css)
  // ============================================================================

  auto_focus: false,

  // any values provided (in an array) will be merged into the standard then sorted
  // you provide the translations
  block_formats: undefined,

  // replace to provide your own
  body_class: 'default-theme',

  // urls to branding css go here
  content_css: [],

  // these are up to you
  directionality: 'ltr',
  height: undefined, // '400px' is s the standard height
  language: 'en',

  // any menubar entries here will be appended to the standard menubar
  menubar: undefined,
  // any menus here will be merged into the standard menubar menus
  menu: undefined,

  // any toolbar data here will be merged into the standard toolbars
  toolbar: undefined,

  // plugins included here will be added to the standard set
  plugins: undefined,

  // ==================================================================================
  // values below this line define standard behavior and probably shouldn't be changed
  // but will be used if you do
  // ==================================================================================
  branding: false,
  browser_spellcheck: true,

  content_style: undefined, // this will always be provided by the RCE

  convert_urls: false,

  font_formats:
    "Lato=lato,Helvetica Neue,Helvetica,Arial,sans-serif; Balsamiq Sans=Balsamiq Sans,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Architect's Daughter=Architects Daughter,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol; Tahoma=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats",

  language_load: false,
  language_url: 'none',

  toolbar_mode: 'floating',
  toolbar_ticky: true,

  mobile: {theme: 'silver'},
  preview_styles:
    'font-family font-size font-weight font-style text-decoration text-transform border border-radius outline text-shadow',
  remove_script_host: true,
  resize: true,
  // will get set from textareaId
  // selector: '#textarea2',
  skin: false,
  statusbar: false,

  // part of unifying tinymce and canvas_sanitize's element whitelist
  // copied from
  // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@valid_elements/#defaultruleset
  // then edited.
  //
  // this list needs to be kept in sync with the list in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
  valid_elements:
    '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|role],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt|title|hspace|vspace|width|height|align|role],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,kbd,label[for],legend,q[cite],samp,small,tt,var,big,figure,figcaption,source[media|sizes|src|srcset|type],track,mark,article,aside,details,footer,header,nav,section,summary,time',
  extended_valid_elements:
    '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|contextmenu|draggable|dropzone|hidden|longdesc|spellcheck|translate|align|role|aria-labelledby|aria-atomic|aria-busy|aria-controls|aria-describedby|aria-disabled|aria-dropeffect|aria-flowto|aria-grabbed|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-labelledby|aria-live|aria-owns|aria-relevant|aria-autocomplete|aria-checked|aria-disabled|aria-expanded|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-level|aria-multiline|aria-multiselectable|aria-orientation|aria-pressed|aria-readonly|aria-required|aria-selected|aria-sort|aria-valuemax|aria-valuemin|aria-valuenow|aria-valuetext],iframe[id|data-media-type|title|src|width|height|name|align|style|class|sandbox|allowfullscreen|webkitallowfullscreen|mozallowfullscreen|allow],i[iclass],a[hidden|href|target|rel|media|hreflang|type|charset|name|rev|shape|coords|download|alt],#p,li[value],-ol[reversed|start|type|compact],pre[width],table[border|summary|width|frame|rules|cellspacing|cellpadding|bgcolor],tbody[char|charoff|valign],td[colspan|rowspan|headers|abbr|axis|scope|align|char|charoff|valign|nowrap|bgcolor|width|height],tfoot[char|charoff|valign],th[colspan|rowspan|headers|scope|abbr|axis|align|char|charoff|valign|nowrap|bgcolor|width|height],thead[char|charoff|valign],tr[char|charoff|valign|bgcolor],-ul[compact],video[name|src|allowfullscreen|muted|poster|width|height|controls|playsinline],audio[name|src|muted|controls],annotation[href|xref|definitionURL|encoding|cd|name|src],annotation-xml[href|xref|definitionURL|encoding|cd|name|src],maction[href|xref|mathcolor|mathbackground|actiontype|selection],maligngroup[href|xref|mathcolor|mathbackground|groupalign],malignmark[href|xref|mathcolor|mathbackground|edge],math[xmlns|href|xref|display|maxwidth|overflow|altimg|altimg-width|altimg-height|altimg-valign|alttext|cdgroup|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],menclose[href|xref|mathcolor|mathbackground|notation],merror[href|xref|mathcolor|mathbackground],mfenced[href|xref|mathcolor|mathbackground|open|close|separators],mfrac[href|xref|mathcolor|mathbackground|linethickness|munalign|denomalign|bevelled],mglyph[href|xref|mathcolor|mathbackground|src|alt|width|height|valign],mi[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mlabeledtr[href|xref|mathcolor|mathbackground],mlongdiv[href|xref|mathcolor|mathbackground|longdivstyle|align|stackalign|charalign|charspacing],mmultiscripts[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],mn[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mo[href|xref|mathcolor|mathbackground|mathvariant|mathsize|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast],mover[href|xref|mathcolor|mathbackground|accent|align],mpadded[href|xref|mathcolor|mathbackground|height|depth|width|lspace|voffset],mphantom[href|xref|mathcolor|mathbackground],mprescripts[href|xref|mathcolor|mathbackground],mroot[href|xref|mathcolor|mathbackground],mrow[href|xref|mathcolor|mathbackground],ms[href|xref|mathcolor|mathbackground|mathvariant|mathsize|lquote|rquote],mscarries[href|xref|mathcolor|mathbackground|position|location|crossout|scriptsizemultiplier],mscarry[href|xref|mathcolor|mathbackground|location|crossout],msgroup[href|xref|mathcolor|mathbackground|position|shift],msline[href|xref|mathcolor|mathbackground|position|length|leftoverhang|rightoverhang|mslinethickness],mspace[href|xref|mathcolor|mathbackground|mathvariant|mathsize],msqrt[href|xref|mathcolor|mathbackground],msrow[href|xref|mathcolor|mathbackground|position],mstack[href|xref|mathcolor|mathbackground|align|stackalign|charalign|charspacing],mstyle[href|xref|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],msub[href|xref|mathcolor|mathbackground|subscriptshift],msubsup[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],msup[href|xref|mathcolor|mathbackground|superscriptshift],mtable[href|xref|mathcolor|mathbackground|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|width|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|displaystyle|side|minlabelspacing],mtd[href|xref|mathcolor|mathbackground|rowspan|columnspan|rowalign|columnalign|groupalign],mtext[href|xref|mathcolor|mathbackground|mathvariant|mathsize|width|height|depth|linebreak],mtr[href|xref|mathcolor|mathbackground|rowalign|columnalign|groupalign],munder[href|xref|mathcolor|mathbackground|accentunder|align],munderover[href|xref|mathcolor|mathbackground|accent|accentunder|align],none[href|xref|mathcolor|mathbackground],semantics[href|xref|definitionURL|encoding],picture,ruby,rp,rt,' +
    // the svg necessary for the uploading placeholder's spinner
    'svg[*],g[*],circle[*]',
  non_empty_elements:
    'td th iframe video audio object script a i area base basefont br col frame hr img input isindex link meta param embed source wbr track ruby',

  // tiny's external link create/edit dialog config
  target_list: false, // don't show the target list when creating/editing links
  link_title: false, // don't show the title input when creating/editing links
  default_link_target: '_blank'
}
export default defaultTinymceConfig
<|endoftext|>


<commit_before>import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import useEventCallback from '../utils/useEventCallback';
import useEnhancedEffect from '../utils/useEnhancedEffect';

/**
 * @ignore - internal component.
 */
function Ripple(props) {
  const {
    className,
    classes,
    pulsate = false,
    rippleX,
    rippleY,
    rippleSize,
    in: inProp,
    onExited = () => {},
    timeout,
  } = props;
  const [leaving, setLeaving] = React.useState(false);

  const rippleClassName = clsx(className, classes.ripple, classes.rippleVisible, {
    [classes.ripplePulsate]: pulsate,
  });

  const rippleStyles = {
    width: rippleSize,
    height: rippleSize,
    top: -(rippleSize / 2) + rippleY,
    left: -(rippleSize / 2) + rippleX,
  };

  const childClassName = clsx(classes.child, {
    [classes.childLeaving]: leaving,
    [classes.childPulsate]: pulsate,
  });

  const handleExited = useEventCallback(onExited);
  // Ripple is used for user feedback (e.g. click or press) so we want to apply styles with the highest priority
  useEnhancedEffect(() => {
    if (!inProp) {
      // react-transition-group#onExit
      setLeaving(true);

      // react-transition-group#onExited
      const timeoutId = setTimeout(handleExited, timeout);
      return () => {
        clearTimeout(timeoutId);
      };
    }
    return undefined;
  }, [handleExited, inProp, timeout]);

  return (

  );
}

Ripple.propTypes = {
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css) below for more details.
   */
  classes: PropTypes.object.isRequired,
  className: PropTypes.string,
  /**
   * @ignore - injected from TransitionGroup
   */
  in: PropTypes.bool,
  /**
   * @ignore - injected from TransitionGroup
   */
  onExited: PropTypes.func,
  /**
   * If `true`, the ripple pulsates, typically indicating the keyboard focus state of an element.
   */
  pulsate: PropTypes.bool,
  /**
   * Diameter of the ripple.
   */
  rippleSize: PropTypes.number,
  /**
   * Horizontal position of the ripple center.
   */
  rippleX: PropTypes.number,
  /**
   * Vertical position of the ripple center.
   */
  rippleY: PropTypes.number,
  /**
   * exit delay
   */
  timeout: PropTypes.number.isRequired,
};

export default Ripple;
<commit_msg>[ButtonBase] Derive state on render instead of in layout effects (#26762)<commit_after>import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';

/**
 * @ignore - internal component.
 */
function Ripple(props) {
  const {
    className,
    classes,
    pulsate = false,
    rippleX,
    rippleY,
    rippleSize,
    in: inProp,
    onExited,
    timeout,
  } = props;
  const [leaving, setLeaving] = React.useState(false);

  const rippleClassName = clsx(className, classes.ripple, classes.rippleVisible, {
    [classes.ripplePulsate]: pulsate,
  });

  const rippleStyles = {
    width: rippleSize,
    height: rippleSize,
    top: -(rippleSize / 2) + rippleY,
    left: -(rippleSize / 2) + rippleX,
  };

  const childClassName = clsx(classes.child, {
    [classes.childLeaving]: leaving,
    [classes.childPulsate]: pulsate,
  });

  if (!inProp && !leaving) {
    setLeaving(true);
  }
  React.useEffect(() => {
    if (!inProp && onExited != null) {
      // react-transition-group#onExited
      const timeoutId = setTimeout(onExited, timeout);
      return () => {
        clearTimeout(timeoutId);
      };
    }
    return undefined;
  }, [onExited, inProp, timeout]);

  return (

  );
}

Ripple.propTypes = {
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css) below for more details.
   */
  classes: PropTypes.object.isRequired,
  className: PropTypes.string,
  /**
   * @ignore - injected from TransitionGroup
   */
  in: PropTypes.bool,
  /**
   * @ignore - injected from TransitionGroup
   */
  onExited: PropTypes.func,
  /**
   * If `true`, the ripple pulsates, typically indicating the keyboard focus state of an element.
   */
  pulsate: PropTypes.bool,
  /**
   * Diameter of the ripple.
   */
  rippleSize: PropTypes.number,
  /**
   * Horizontal position of the ripple center.
   */
  rippleX: PropTypes.number,
  /**
   * Vertical position of the ripple center.
   */
  rippleY: PropTypes.number,
  /**
   * exit delay
   */
  timeout: PropTypes.number.isRequired,
};

export default Ripple;
<|endoftext|>


<commit_before>var horizon_dependencies = ['hz.conf', 'hz.utils', 'ngCookies'];
dependencies = horizon_dependencies.concat(angularModuleExtension);
var horizonApp = angular.module('hz', dependencies)
  .config(['$interpolateProvider', '$httpProvider',
    function ($interpolateProvider, $httpProvider) {
      $interpolateProvider.startSymbol('{$');
      $interpolateProvider.endSymbol('$}');
      $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
      $httpProvider.defaults.xsrfCookieName = 'csrftoken';
      $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    }])
  .run(['hzConfig', 'hzUtils', '$cookieStore',
    function (hzConfig, hzUtils, $cookieStore) {
      //expose the configuration for horizon legacy variable
      horizon.conf = hzConfig;
      horizon.utils = hzUtils;
      angular.extend(horizon.cookies = {}, $cookieStore);
      horizon.cookies.put = function (key, value) {
        //cookies are updated at the end of current $eval, so for the horizon
        //namespace we need to wrap it in a $apply function.
        angular.element('body').scope().$apply(function () {
          $cookieStore.put(key, value);
        });
      };
    }]);

<commit_msg>POST instantiation for Django-Angular<commit_after>var horizon_dependencies = ['hz.conf', 'hz.utils', 'ngCookies'];
dependencies = horizon_dependencies.concat(angularModuleExtension);
var horizonApp = angular.module('hz', dependencies)
  .config(['$interpolateProvider', '$httpProvider',
    function ($interpolateProvider, $httpProvider) {
      $interpolateProvider.startSymbol('{$');
      $interpolateProvider.endSymbol('$}');
      $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
      $httpProvider.defaults.xsrfCookieName = 'csrftoken';
      $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    }])
  .run(['hzConfig', 'hzUtils', '$cookieStore', '$http', '$cookies',
    function (hzConfig, hzUtils, $cookieStore, $http, $cookies) {
      $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
      //expose the configuration for horizon legacy variable
      horizon.conf = hzConfig;
      horizon.utils = hzUtils;
      angular.extend(horizon.cookies = {}, $cookieStore);
      horizon.cookies.put = function (key, value) {
        //cookies are updated at the end of current $eval, so for the horizon
        //namespace we need to wrap it in a $apply function.
        angular.element('body').scope().$apply(function () {
          $cookieStore.put(key, value);
        });
      };
    }]);

<|endoftext|>