<commit_before>var should = require("should"),
    requirejs = require('requirejs');

requirejs.config({
    baseUrl: 'pulsr',
    nodeRequire: require
});

describe('Pulsr', function (){
    describe('htmlFileHandler', function (){
        it('should return 200 statusCode for HTML file request within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'fs', 'path', 'module', 'http', 'conf'], function (async, fs, path, module, http, conf) {
                var htmlDirs = ['docs'];

                function traverseDir(dirName, done) {
                    var dirPath = path.join(path.dirname(path.resolve(module.uri)), '../', dirName);

                    function requestFile(fileName, done) {
                        if (/.+\.(html)/.test(fileName)) {
                            var filePath = path.join(dirPath, fileName);

                            fs.stat(filePath, function (err, stat) {
                                if (err) {
                                    throw err;
                                }
                                else if (stat.isFile()){
                                    http.get('http://' + conf.app.domains.root + '/' + dirName + '/' + fileName, function (response) {
                                        response.should.have.status(200);
                                        response.should.be.html;
                                        response.should.have.header('vary', 'accept-encoding');
                                        response.headers.should.not.have.property('content-encoding');

                                        http.get({
                                            host: (conf.app.domains.root.split(':')[0]),
                                            path: '/' + dirName + '/' + fileName,
                                            port: 1337,
                                            headers: { 'accept-encoding': 'gzip' } },
                                        function (response) {
                                            response.should.have.status(200);
                                            response.should.be.html;
                                            response.should.have.header('content-encoding', 'gzip');
                                            response.should.have.header('vary', 'accept-encoding');

                                            done();
                                        });
                                    });
                                }
                                else{
                                    done();
                                }
                            });
                        }
                        else{
                            done();
                        }
                    }

                    fs.readdir(dirPath, function (err, files) {
                        if (err) {
                            throw err;
                        }
                        else{
                            async.forEach(files, requestFile, function (err) {
                                done();
                            });
                        }
                    });
                }

                async.forEach(htmlDirs, traverseDir, function (err) {
                    done();
                });
            });
        });

        it('should return 404 statusCode for non-existing HTML file requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var htmlFiles = ['docs/zubazuba.html', 'docs/lambalamba.html'];

                function requestFile(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(404);
                        done();
                    });
                }

                async.forEach(htmlFiles, requestFile, function (err) {
                    done();
                });
            });
        });
    });
});<commit_msg>Test: added more tests to htmlFileHandler to check 304 HTTP response.<commit_after>var should = require("should"),
    requirejs = require('requirejs');

requirejs.config({
    baseUrl: 'pulsr',
    nodeRequire: require
});

describe('Pulsr', function (){
    describe('htmlFileHandler', function (){
        it('should return 200 statusCode for HTML file request within 20s', function (done){
            // set timeout to 20s
            this.timeout(30000);

            requirejs(['async', 'fs', 'path', 'module', 'http', 'conf'], function (async, fs, path, module, http, conf) {
                var htmlDirs = ['docs'];

                function traverseDir(dirName, done) {
                    var dirPath = path.join(path.dirname(path.resolve(module.uri)), '../', dirName);

                    function requestFile(fileName, done) {
                        if (/.+\.(html)/.test(fileName)) {
                            var filePath = path.join(dirPath, fileName);

                            fs.stat(filePath, function (err, stat) {
                                if (err) {
                                    throw err;
                                }
                                else if (stat.isFile()){
                                    http.get('http://' + conf.app.domains.root + '/' + dirName + '/' + fileName, function (response) {
                                        response.should.have.status(200);
                                        response.should.be.html;
                                        response.should.have.header('vary', 'accept-encoding');
                                        response.should.have.header('etag');
                                        response.headers.should.not.have.property('content-encoding');

                                        http.get({
                                            host: (conf.app.domains.root.split(':')[0]),
                                            path: '/' + dirName + '/' + fileName,
                                            port: 1337,
                                            headers: { 'accept-encoding': 'gzip' } },
                                        function (response) {
                                            response.should.have.status(200);
                                            response.should.be.html;
                                            response.should.have.header('content-encoding', 'gzip');
                                            response.should.have.header('etag');
                                            response.should.have.header('vary', 'accept-encoding');

                                            http.get({
                                                host: (conf.app.domains.root.split(':')[0]),
                                                path: '/' + dirName + '/' + fileName,
                                                port: 1337,
                                                headers: {
                                                    'accept-encoding': 'gzip',
                                                    'if-none-match': response.headers['etag']
                                                }
                                            }, function (response) {
                                                response.should.have.status(304);
                                                response.should.have.header('vary', 'accept-encoding');

                                                done();
                                            });
                                        });
                                    });
                                }
                                else{
                                    done();
                                }
                            });
                        }
                        else{
                            done();
                        }
                    }

                    fs.readdir(dirPath, function (err, files) {
                        if (err) {
                            throw err;
                        }
                        else{
                            async.forEach(files, requestFile, function (err) {
                                done();
                            });
                        }
                    });
                }

                async.forEach(htmlDirs, traverseDir, function (err) {
                    done();
                });
            });
        });

        it('should return 404 statusCode for non-existing HTML file requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var htmlFiles = ['docs/zubazuba.html', 'docs/lambalamba.html'];

                function requestFile(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(404);
                        done();
                    });
                }

                async.forEach(htmlFiles, requestFile, function (err) {
                    done();
                });
            });
        });
    });
});<|endoftext|>


<commit_before>/**
 * Wait until the test condition is true or a timeout occurs. Useful for waiting
 * on a server response or for a ui change (fadeIn, etc.) to occur.
 *
 * @param testFx javascript condition that evaluates to a boolean,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param onReady what to do when testFx condition is fulfilled,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
 */

function waitFor(testFx, onReady, timeOutMillis) {
  var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 30001,
       //< Default Max Timout is 3s, just kidding it's 30s
      start = new Date().getTime(),
      condition = false,
      interval = setInterval(function() {
      if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
        // If not time-out yet and condition not yet fulfilled
        condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
      } else {
        if (!condition) {
          // If condition still not fulfilled (timeout but condition is 'false')
          console.log("Tests timeout");
          phantom.exit(1);
        } else {
          // Condition fulfilled (timeout and/or condition is 'true')
          console.log("Tests finished in " + (new Date().getTime() - start) + "ms.");
          typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
          clearInterval(interval); //< Stop this interval
        }
      }
    }, 100); //< repeat check every 250ms
};

if (phantom.args.length === 0 || phantom.args.length > 2) {
  console.log('Usage: run-qunit.js URL');
  phantom.exit();
}

var page = new WebPage();

// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
// hide these warnings ...

page.onConsoleMessage = function(msg) {
  // annoying warnings are annoying
  if (msg.indexOf('is deprecated') < 0) {
    console.log(msg);
  }
};

page.open(phantom.args[0], function(status) {
  if (status !== "success") {
    console.log("Unable to access network");
    phantom.exit(1);
  } else {
    console.log("\n");
    waitFor(function() {
      return page.evaluate(function() {
        var el = document.getElementById('qunit-testresult');
        if (el && el.innerText.match('completed')) {
          return true;
        }
        return false;
      });
    }, function() {
      var data = page.evaluate(function() {
        var el = document.getElementById('qunit-testresult');

        try {
          failedNum = el.getElementsByClassName('failed')[0].innerHTML;
        } catch (e) {
          failedNum = 10000;
        }

        return { failedNum: failedNum, resultsText: el.innerText };
      });

      var failed = parseInt(data.failedNum, 10) > 0;

      if (failed) {
        var failures = page.evaluate(function() {
          var testEls = document.getElementById('qunit-tests').children,
              failures = [];
              len = testEls.length, idx;

          for (idx=0; idx<len; idx++) {
            if (testEls[idx].className === 'fail') {
              var children = testEls[idx].children;
              failures.push(children[0].innerText+"\n    "+(children[2].innerText.split("\n").join("    \n")));
            }
          }

          return failures;
        });

        console.log("\nFailures:\n\n"+failures.join("\n\n")+"\n\n");
      }

      console.log(data.resultsText);

      phantom.exit(failed ? 1 : 0);
    });
  }
});
<commit_msg>Use QUnit logging handlers in our PhantomJS runner instead of old and busted DOM traversing<commit_after>// PhantomJS QUnit Test Runner

var args = phantom.args;

if (args.length < 1 || args.length > 2) {
  console.log("Usage: " + phantom.scriptName + " <URL> <timeout>");
  phantom.exit(1);
}

var page = require('webpage').create();

page.onConsoleMessage = function(msg) {
  if (!/^DEPRECATION:/.test(msg)) console.log(msg);
};

page.open(args[0], function(status) {
  if (status !== 'success') {
    console.error("Unable to access network");
    phantom.exit(1);
  }

  page.evaluate(addLogging);

  var timeout = parseInt(args[1] || 30000);
  var start = Date.now();

  var interval = setInterval(function() {
    if (Date.now() > start + timeout) {
      console.error("Tests timed out");
      phantom.exit(1);
    }

    if (isDone()) {
      clearInterval(interval);
      if (didFail()) phantom.exit(1);
      phantom.exit();
    }
  }, 500);
});

function isDone() {
  return page.evaluate(function() {
    return !!window.qunitDone;
  });
}

function didFail() {
  var output = page.evaluate(function() {
    return window.qunitDone;
  });
  return output && output.failed > 0;
}

function addLogging() {
  var testErrors = [];
  var assertionErrors = [];

  QUnit.moduleDone(function(context) {
    if (context.failed) {
      var msg = "Module Failed: " + context.name + "\n" + testErrors.join("\n");
      console.error(msg);
      testErrors = [];
    }
  });

  QUnit.testDone(function(context) {
    if (context.failed) {
      var msg = "  Test Failed: " + context.name + assertionErrors.join("    ");
      testErrors.push(msg);
      assertionErrors = [];
    }
  });

  QUnit.log(function(context) {
    if (context.result) return;

    var msg = "\n    Assertion Failed: " + (context.message || "");
    if (context.expected) {
      msg += "\n      Expected: " + context.expected + ", Actual: " + context.actual;
    }

    assertionErrors.push(msg);
  });

  QUnit.done(function(context) {
    var stats = [
      "Time: " + context.runtime + "ms",
      "Total: " + context.total,
      "Passed: " + context.passed,
      "Failed: " + context.failed
    ];
    console.log(stats.join(", "));
    window.qunitDone = context;
  });
}
<|endoftext|>


<commit_before>define(function(require, exports, module) {

// Imports

var $ = require('jquery');
var rich = require('rich');
var Engine = require('famous/core/Engine');
var Modifier = require('famous/core/Modifier');
var Transform = require('famous/core/Transform');
var Rectangle = require('app/shared/models/rectangle').Rectangle;
var RectangleView = require('app/shared/views/rectangle-view').RectangleView;
var utils = require('rich/utils');

describe('Utils:', function() {

    it('initializes root view with tag', function(){

        var options = {
            el: 'body'
        };

        var view = utils.initializeRichContext(options);
        expect(view).not.toBe(undefined);

    });

    it('initializes root view with selector', function(){
        loadFixtures('famous.html');

        var options = {
            el: '#jasmine-fixtures'
        };

        var view = utils.initializeRichContext(options);
        expect(view).not.toBe(undefined);
    });

    it('modifierWithTransform with array', function(){

        var modifier = utils.modifierWithTransform({
            transform: [10, 10, 10]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithTransform with func', function(){

        var modifier = utils.modifierWithTransform({
            transform: function(){return Transform.translate(10, 10, 10);}
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithSize', function(){

        var modifier = utils.modifierWithSize({
            size: [10, 10]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithOpacity', function(){

        var modifier = utils.modifierWithOpacity({
            opacity: 1
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithOrigin', function(){

        var modifier = utils.modifierWithOrigin({
            origin: [0, 0]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithAlign', function(){

        var modifier = utils.modifierWithAlign({
            align: 50
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('postrenderOnce', function(done){
        var context = Engine.createContext();
        var handler = jasmine.createSpy('handler');

        utils.postrenderOnce(handler);

        setTimeout(function(){
            expect(handler.calls.count()).toBe(1);
            done();
        }, 500);
    });

    it('getViewSize', function(){
        var model = new Rectangle({
            size: [300, 500]
        });

        var view = new RectangleView({model: model});

        var size = utils.getViewSize(view);
        expect(size[0]).toBe(300);
        expect(size[1]).toBe(500);
    });

}); // eof describe
}); // eof define
<commit_msg>fixes multiple contexts<commit_after>define(function(require, exports, module) {

// Imports

var $ = require('jquery');
var rich = require('rich');
var Engine = require('famous/core/Engine');
var Modifier = require('famous/core/Modifier');
var Transform = require('famous/core/Transform');
var Rectangle = require('app/shared/models/rectangle').Rectangle;
var RectangleView = require('app/shared/views/rectangle-view').RectangleView;
var utils = require('rich/utils');

describe('Utils:', function() {

    it('initializes root view with tag', function(){

        var options = {
            el: 'body'
        };

        var view = utils.initializeRichContext(options);
        expect(view).not.toBe(undefined);
        utils.disposeRichContext(view);
    });

    it('initializes root view with selector', function(){
        loadFixtures('famous.html');

        var options = {
            el: '#jasmine-fixtures'
        };

        var view = utils.initializeRichContext(options);
        expect(view).not.toBe(undefined);
        utils.disposeRichContext(view);
    });

    it('modifierWithTransform with array', function(){

        var modifier = utils.modifierWithTransform({
            transform: [10, 10, 10]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithTransform with func', function(){

        var modifier = utils.modifierWithTransform({
            transform: function(){return Transform.translate(10, 10, 10);}
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithSize', function(){

        var modifier = utils.modifierWithSize({
            size: [10, 10]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithOpacity', function(){

        var modifier = utils.modifierWithOpacity({
            opacity: 1
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithOrigin', function(){

        var modifier = utils.modifierWithOrigin({
            origin: [0, 0]
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('modifierWithAlign', function(){

        var modifier = utils.modifierWithAlign({
            align: 50
        });

        expect(modifier).not.toBe(undefined);
        expect(modifier instanceof Modifier).toBe(true);

    });

    it('postrenderOnce', function(done){
        var options = {
            el: 'body'
        };

        var view = utils.initializeRichContext(options);
        var handler = jasmine.createSpy('handler');
        utils.postrenderOnce(handler);

        setTimeout(function(){
            expect(handler.calls.count()).toBe(1);
            done();
            utils.disposeRichContext(view);
        }, 500);
    });

    it('getViewSize', function(){
        var model = new Rectangle({
            size: [300, 500]
        });

        var view = new RectangleView({model: model});

        var size = utils.getViewSize(view);
        expect(size[0]).toBe(300);
        expect(size[1]).toBe(500);
    });

}); // eof describe
}); // eof define
<|endoftext|>


<commit_before>"use strict";

const fs = require("fs");
const extname = require("path").extname;
const prettier = require("../");
const types = require("../src/ast-types");
const parser = require("../src/parser");
const massageAST = require('../src/clean-ast.js').massageAST;

const RUN_AST_TESTS = process.env["AST_COMPARE"];
const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false;
const ALL_PARSERS = process.env["ALL_PARSERS"]
  ? JSON.parse(process.env["ALL_PARSERS"])
  : ["flow", "babylon", "typescript"];

function run_spec(dirname, options, additionalParsers) {
  fs.readdirSync(dirname).forEach(filename => {
    const extension = extname(filename);
    if (/^\.[jt]sx?$/.test(extension) && filename !== "jsfmt.spec.js") {
      const path = dirname + "/" + filename;
      const mergedOptions = mergeDefaultOptions(options || {});

      const source = read(path).replace(/\r\n/g, "\n");
      const output = prettyprint(source, path, mergedOptions);
      test(`${mergedOptions.parser} - ${parser.parser}-verify`, () => {
        expect(raw(source + "~".repeat(80) + "\n" + output)).toMatchSnapshot(
          filename
        );
      });

      getParsersToVerify(
        mergedOptions.parser,
        additionalParsers || []
      ).forEach(parserName => {
        test(`${filename} - ${parserName}-verify`, () => {
          const verifyOptions = Object.assign(mergedOptions, {
            parser: parserName
          });
          const verifyOutput = prettyprint(source, path, verifyOptions);
          expect(output).toEqual(verifyOutput);
        });
      });

      if (RUN_AST_TESTS) {
        const source = read(dirname + "/" + filename);
        const ast = parse(source, mergedOptions);
        const astMassaged = massageAST(ast);
        let ppastMassaged;
        let pperr = null;
        try {
          const ppast = parse(prettyprint(source, path, mergedOptions), mergedOptions)
          ppastMassaged = massageAST(ppast);
        } catch (e) {
          pperr = e.stack;
        }

        test(path + " parse", () => {
          expect(pperr).toBe(null);
          expect(ppastMassaged).toBeDefined();
          if (!ast.errors || ast.errors.length === 0) {
            expect(astMassaged).toEqual(ppastMassaged);
          }
        });
      }
    }
  });
}
global.run_spec = run_spec;

function stripLocation(ast) {
  if (Array.isArray(ast)) {
    return ast.map(e => stripLocation(e));
  }
  if (typeof ast === "object") {
    const newObj = {};
    for (var key in ast) {
      if (
        key === "loc" || key === "range" || key === "raw" || key === "comments"
      ) {
        continue;
      }
      newObj[key] = stripLocation(ast[key]);
    }
    return newObj;
  }
  return ast;
}

function parse(string, opts) {
  return stripLocation(parser.parse(string, opts));
}

function prettyprint(src, filename, options) {
  return prettier.format(
    src,
    Object.assign(
      {
        filename
      },
      options
    )
  );
}

function read(filename) {
  return fs.readFileSync(filename, "utf8");
}

/**
 * Wraps a string in a marker object that is used by `./raw-serializer.js` to
 * directly print that string in a snapshot without escaping all double quotes.
 * Backticks will still be escaped.
 */
function raw(string) {
  if (typeof string !== "string") {
    throw new Error("Raw snapshots have to be strings.");
  }
  return { [Symbol.for("raw")]: string };
}

function mergeDefaultOptions(parserConfig) {
  return Object.assign(
    {
      parser: "flow",
      printWidth: 80
    },
    parserConfig
  );
}

function getParsersToVerify(parser, additionalParsers) {
  if (VERIFY_ALL_PARSERS) {
    return ALL_PARSERS.splice(ALL_PARSERS.indexOf(parser), 1);
  }
  return additionalParsers;
}
<commit_msg>tests: Rename `RUN_AST_TESTS` variable to `AST_COMPARE` (#1563)<commit_after>"use strict";

const fs = require("fs");
const extname = require("path").extname;
const prettier = require("../");
const types = require("../src/ast-types");
const parser = require("../src/parser");
const massageAST = require('../src/clean-ast.js').massageAST;

const AST_COMPARE = process.env["AST_COMPARE"];
const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false;
const ALL_PARSERS = process.env["ALL_PARSERS"]
  ? JSON.parse(process.env["ALL_PARSERS"])
  : ["flow", "babylon", "typescript"];

function run_spec(dirname, options, additionalParsers) {
  fs.readdirSync(dirname).forEach(filename => {
    const extension = extname(filename);
    if (/^\.[jt]sx?$/.test(extension) && filename !== "jsfmt.spec.js") {
      const path = dirname + "/" + filename;
      const mergedOptions = mergeDefaultOptions(options || {});

      const source = read(path).replace(/\r\n/g, "\n");
      const output = prettyprint(source, path, mergedOptions);
      test(`${mergedOptions.parser} - ${parser.parser}-verify`, () => {
        expect(raw(source + "~".repeat(80) + "\n" + output)).toMatchSnapshot(
          filename
        );
      });

      getParsersToVerify(
        mergedOptions.parser,
        additionalParsers || []
      ).forEach(parserName => {
        test(`${filename} - ${parserName}-verify`, () => {
          const verifyOptions = Object.assign(mergedOptions, {
            parser: parserName
          });
          const verifyOutput = prettyprint(source, path, verifyOptions);
          expect(output).toEqual(verifyOutput);
        });
      });

      if (AST_COMPARE) {
        const source = read(dirname + "/" + filename);
        const ast = parse(source, mergedOptions);
        const astMassaged = massageAST(ast);
        let ppastMassaged;
        let pperr = null;
        try {
          const ppast = parse(prettyprint(source, path, mergedOptions), mergedOptions)
          ppastMassaged = massageAST(ppast);
        } catch (e) {
          pperr = e.stack;
        }

        test(path + " parse", () => {
          expect(pperr).toBe(null);
          expect(ppastMassaged).toBeDefined();
          if (!ast.errors || ast.errors.length === 0) {
            expect(astMassaged).toEqual(ppastMassaged);
          }
        });
      }
    }
  });
}
global.run_spec = run_spec;

function stripLocation(ast) {
  if (Array.isArray(ast)) {
    return ast.map(e => stripLocation(e));
  }
  if (typeof ast === "object") {
    const newObj = {};
    for (var key in ast) {
      if (
        key === "loc" || key === "range" || key === "raw" || key === "comments"
      ) {
        continue;
      }
      newObj[key] = stripLocation(ast[key]);
    }
    return newObj;
  }
  return ast;
}

function parse(string, opts) {
  return stripLocation(parser.parse(string, opts));
}

function prettyprint(src, filename, options) {
  return prettier.format(
    src,
    Object.assign(
      {
        filename
      },
      options
    )
  );
}

function read(filename) {
  return fs.readFileSync(filename, "utf8");
}

/**
 * Wraps a string in a marker object that is used by `./raw-serializer.js` to
 * directly print that string in a snapshot without escaping all double quotes.
 * Backticks will still be escaped.
 */
function raw(string) {
  if (typeof string !== "string") {
    throw new Error("Raw snapshots have to be strings.");
  }
  return { [Symbol.for("raw")]: string };
}

function mergeDefaultOptions(parserConfig) {
  return Object.assign(
    {
      parser: "flow",
      printWidth: 80
    },
    parserConfig
  );
}

function getParsersToVerify(parser, additionalParsers) {
  if (VERIFY_ALL_PARSERS) {
    return ALL_PARSERS.splice(ALL_PARSERS.indexOf(parser), 1);
  }
  return additionalParsers;
}
<|endoftext|>


<commit_before>var Choropleth = require('@src/traces/choropleth');

var Plotly = require('@lib/index');
var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');
var loggers = require('@src/lib/loggers');

var d3Select = require('../../strict-d3').select;
var d3SelectAll = require('../../strict-d3').selectAll;
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');

var customAssertions = require('../assets/custom_assertions');
var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;

describe('Test choropleth', function() {
    'use strict';

    describe('supplyDefaults', function() {
        var traceIn;
        var traceOut;

        var defaultColor = '#444';
        var layout = {
            font: Plots.layoutAttributes.font,
            _dfltTitle: {colorbar: 'cb'}
        };

        beforeEach(function() {
            traceOut = {};
        });

        it('should set _length based on locations and z but not slice', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2, 3]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.z).toEqual([1, 2, 3]);
            expect(traceOut.locations).toEqual(['CAN', 'USA']);
            expect(traceOut._length).toBe(2);

            traceIn = {
                locations: ['CAN', 'USA', 'ALB'],
                z: [1, 2]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.z).toEqual([1, 2]);
            expect(traceOut.locations).toEqual(['CAN', 'USA', 'ALB']);
            expect(traceOut._length).toBe(2);
        });

        it('should make trace invisible if locations is not defined', function() {
            traceIn = {
                z: [1, 2, 3]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.visible).toBe(false);
        });

        it('should make trace invisible if z is not an array', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: 'no gonna work'
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.visible).toBe(false);
        });

        it('should not coerce *marker.line.color* when *marker.line.width* is *0*', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                marker: {
                    line: {
                        color: 'red',
                        width: 0
                    }
                }
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.marker.line.width).toBe(0, 'mlw');
            expect(traceOut.marker.line.color).toBe(undefined, 'mlc');
        });

        it('should default locationmode to *geojson-id* when a valid *geojson* is provided', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: 'url'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('geojson-id', 'valid url string');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: {}
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('geojson-id', 'valid object');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: ''
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('ISO-3', 'invalid sting');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: []
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('ISO-3', 'invalid object');
        });

        it('should only coerce *featureidkey* when locationmode is *geojson-id', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: 'url',
                featureidkey: 'properties.name'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.featureidkey).toBe('properties.name', 'coerced');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                featureidkey: 'properties.name'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.featureidkey).toBe(undefined, 'NOT coerced');
        });
    });
});

describe('Test choropleth hover:', function() {
    var gd;

    afterEach(destroyGraphDiv);

    function transformPlot(gd, transformString) {
        gd.style.webkitTransform = transformString;
        gd.style.MozTransform = transformString;
        gd.style.msTransform = transformString;
        gd.style.OTransform = transformString;
        gd.style.transform = transformString;
    }

    function run(hasCssTransform, pos, fig, content, style) {
        gd = createGraphDiv();

        style = style || {
            bgcolor: 'rgb(68, 68, 68)',
            bordercolor: 'rgb(255, 255, 255)',
            fontColor: 'rgb(255, 255, 255)',
            fontSize: 13,
            fontFamily: 'Arial'
        };

        function assertHoverLabel(posX, posY) {
            mouseEvent('mousemove', posX, posY);
            assertHoverLabelContent({
                nums: content[0],
                name: content[1]
            });
            assertHoverLabelStyle(
                d3Select('g.hovertext'),
                style
            );
        }

        return Plotly.newPlot(gd, fig)
        .then(function() {
            if(hasCssTransform) {
                transformPlot(gd, 'translate3d(10px, 10px, 0) scale(1)');
                assertHoverLabel(pos[0] + 10, pos[1] + 10);
                transformPlot(gd, 'translate(-25%, -25%) scale(0.5)');
                assertHoverLabel(0.5 * pos[0], 0.5 * pos[1]);
            } else {
                assertHoverLabel(pos[0], pos[1]);
            }
        });
    }

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (base), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS\n10', 'trace 1']
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should use the hovertemplate, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hovertemplate = 'tpl %{z}<extra>x</extra>';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['tpl 10', 'x']
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (\'text\' single value case), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].text = 'tExT';
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['tExT', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (\'text\' array case), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].text = ['tExT', 'TeXt', '-text-'];
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['-text-', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover labels from `hovertext`, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hovertext = ['tExT', 'TeXt', '-text-'];
            fig.data[1].text = ['N', 'O', 'P'];
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['-text-', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label with custom styling, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hoverlabel = {
                bgcolor: 'red',
                bordercolor: ['blue', 'black', 'green'],
                font: {family: 'Roboto'}
            };

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS\n10', 'trace 1'],
                {
                    bgcolor: 'rgb(255, 0, 0)',
                    bordercolor: 'rgb(0, 128, 0)',
                    fontColor: 'rgb(0, 128, 0)',
                    fontSize: 13,
                    fontFamily: 'Roboto'
                }
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label with arrayOk \'hoverinfo\' settings, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hoverinfo = ['location', 'z', 'location+name'];

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS', 'trace 1']
            )
            .then(done, done.fail);
        });
    });

    describe('should preserve z formatting hovetemplate equivalence', function() {
        var base = function() {
            return {
                data: [{
                    type: 'choropleth',
                    locations: ['RUS'],
                    z: [10.02132132143214321]
                }]
            };
        };

        var pos = [400, 160];
        var exp = ['10.02132', 'RUS'];

        [false, true].forEach(function(hasCssTransform) {
            it('- base case (truncate z decimals), hasCssTransform: ' + hasCssTransform, function(done) {
                run(hasCssTransform, pos, base(), exp)
                .then(done, done.fail);
            });
        });

        [false, true].forEach(function(hasCssTransform) {
            it('- hovertemplate case (same z truncation), hasCssTransform: ' + hasCssTransform, function(done) {
                var fig = base();
                fig.hovertemplate = '%{z}<extra>%{location}</extra>';
                run(hasCssTransform, pos, fig, exp)
                .then(done, done.fail);
            });
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should include *properties* from input custom geojson, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_custom-geojson.json'));
            fig.data = [fig.data[1]];
            fig.data[0].hovertemplate = '%{properties.name}<extra>%{ct[0]:.1f} | %{ct[1]:.1f}</extra>';
            fig.layout.geo.projection = {scale: 20};

            run(hasCssTransform, [300, 200], fig, ['New York', '-75.1 | 42.6'])
            .then(done, done.fail);
        });
    });
});

describe('choropleth drawing', function() {
    var gd;

    beforeEach(function() {
        gd = createGraphDiv();
    });

    afterEach(destroyGraphDiv);

    it('should not throw an error with bad locations', function(done) {
        spyOn(loggers, 'log');
        Plotly.newPlot(gd, [{
            locations: ['canada', 0, null, '', 'utopia'],
            z: [1, 2, 3, 4, 5],
            locationmode: 'country names',
            type: 'choropleth'
        }])
        .then(function() {
            // only utopia logs - others are silently ignored
            expect(loggers.log).toHaveBeenCalledTimes(1);
        })
        .then(done, done.fail);
    });

    it('preserves order after hide/show', function(done) {
        function getIndices() {
            var out = [];
            d3SelectAll('.choropleth').each(function(d) { out.push(d[0].trace.index); });
            return out;
        }

        Plotly.newPlot(gd, [{
            type: 'choropleth',
            locations: ['CAN', 'USA'],
            z: [1, 2]
        }, {
            type: 'choropleth',
            locations: ['CAN', 'USA'],
            z: [2, 1]
        }])
        .then(function() {
            expect(getIndices()).toEqual([0, 1]);
            return Plotly.restyle(gd, 'visible', false, [0]);
        })
        .then(function() {
            expect(getIndices()).toEqual([1]);
            return Plotly.restyle(gd, 'visible', true, [0]);
        })
        .then(function() {
            expect(getIndices()).toEqual([0, 1]);
        })
        .then(done, done.fail);
    });
});
<commit_msg>Revert "extend choropleth tests to include translate3d transform (#5525)"<commit_after>var Choropleth = require('@src/traces/choropleth');

var Plotly = require('@lib/index');
var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');
var loggers = require('@src/lib/loggers');

var d3Select = require('../../strict-d3').select;
var d3SelectAll = require('../../strict-d3').selectAll;
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');

var customAssertions = require('../assets/custom_assertions');
var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;

describe('Test choropleth', function() {
    'use strict';

    describe('supplyDefaults', function() {
        var traceIn;
        var traceOut;

        var defaultColor = '#444';
        var layout = {
            font: Plots.layoutAttributes.font,
            _dfltTitle: {colorbar: 'cb'}
        };

        beforeEach(function() {
            traceOut = {};
        });

        it('should set _length based on locations and z but not slice', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2, 3]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.z).toEqual([1, 2, 3]);
            expect(traceOut.locations).toEqual(['CAN', 'USA']);
            expect(traceOut._length).toBe(2);

            traceIn = {
                locations: ['CAN', 'USA', 'ALB'],
                z: [1, 2]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.z).toEqual([1, 2]);
            expect(traceOut.locations).toEqual(['CAN', 'USA', 'ALB']);
            expect(traceOut._length).toBe(2);
        });

        it('should make trace invisible if locations is not defined', function() {
            traceIn = {
                z: [1, 2, 3]
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.visible).toBe(false);
        });

        it('should make trace invisible if z is not an array', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: 'no gonna work'
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.visible).toBe(false);
        });

        it('should not coerce *marker.line.color* when *marker.line.width* is *0*', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                marker: {
                    line: {
                        color: 'red',
                        width: 0
                    }
                }
            };

            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.marker.line.width).toBe(0, 'mlw');
            expect(traceOut.marker.line.color).toBe(undefined, 'mlc');
        });

        it('should default locationmode to *geojson-id* when a valid *geojson* is provided', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: 'url'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('geojson-id', 'valid url string');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: {}
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('geojson-id', 'valid object');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: ''
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('ISO-3', 'invalid sting');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: []
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.locationmode).toBe('ISO-3', 'invalid object');
        });

        it('should only coerce *featureidkey* when locationmode is *geojson-id', function() {
            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                geojson: 'url',
                featureidkey: 'properties.name'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.featureidkey).toBe('properties.name', 'coerced');

            traceIn = {
                locations: ['CAN', 'USA'],
                z: [1, 2],
                featureidkey: 'properties.name'
            };
            traceOut = {};
            Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout);
            expect(traceOut.featureidkey).toBe(undefined, 'NOT coerced');
        });
    });
});

describe('Test choropleth hover:', function() {
    var gd;

    afterEach(destroyGraphDiv);

    function transformPlot(gd, transformString) {
        gd.style.webkitTransform = transformString;
        gd.style.MozTransform = transformString;
        gd.style.msTransform = transformString;
        gd.style.OTransform = transformString;
        gd.style.transform = transformString;
    }

    function run(hasCssTransform, pos, fig, content, style) {
        gd = createGraphDiv();
        var scale = 1;

        style = style || {
            bgcolor: 'rgb(68, 68, 68)',
            bordercolor: 'rgb(255, 255, 255)',
            fontColor: 'rgb(255, 255, 255)',
            fontSize: 13,
            fontFamily: 'Arial'
        };

        return Plotly.newPlot(gd, fig)
        .then(function() {
            if(hasCssTransform) {
                scale = 0.5;
                transformPlot(gd, 'translate(-25%, -25%) scale(0.5)');
            }

            mouseEvent('mousemove', scale * pos[0], scale * pos[1]);
            assertHoverLabelContent({
                nums: content[0],
                name: content[1]
            });
            assertHoverLabelStyle(
                d3Select('g.hovertext'),
                style
            );
        });
    }

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (base), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS\n10', 'trace 1']
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should use the hovertemplate, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hovertemplate = 'tpl %{z}<extra>x</extra>';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['tpl 10', 'x']
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (\'text\' single value case), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].text = 'tExT';
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['tExT', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label info (\'text\' array case), hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].text = ['tExT', 'TeXt', '-text-'];
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['-text-', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover labels from `hovertext`, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hovertext = ['tExT', 'TeXt', '-text-'];
            fig.data[1].text = ['N', 'O', 'P'];
            fig.data[1].hoverinfo = 'text';

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['-text-', null]
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label with custom styling, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hoverlabel = {
                bgcolor: 'red',
                bordercolor: ['blue', 'black', 'green'],
                font: {family: 'Roboto'}
            };

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS\n10', 'trace 1'],
                {
                    bgcolor: 'rgb(255, 0, 0)',
                    bordercolor: 'rgb(0, 128, 0)',
                    fontColor: 'rgb(0, 128, 0)',
                    fontSize: 13,
                    fontFamily: 'Roboto'
                }
            )
            .then(done, done.fail);
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should generate hover label with arrayOk \'hoverinfo\' settings, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_first.json'));
            fig.data[1].hoverinfo = ['location', 'z', 'location+name'];

            run(
                hasCssTransform,
                [400, 160],
                fig,
                ['RUS', 'trace 1']
            )
            .then(done, done.fail);
        });
    });

    describe('should preserve z formatting hovetemplate equivalence', function() {
        var base = function() {
            return {
                data: [{
                    type: 'choropleth',
                    locations: ['RUS'],
                    z: [10.02132132143214321]
                }]
            };
        };

        var pos = [400, 160];
        var exp = ['10.02132', 'RUS'];

        [false, true].forEach(function(hasCssTransform) {
            it('- base case (truncate z decimals), hasCssTransform: ' + hasCssTransform, function(done) {
                run(hasCssTransform, pos, base(), exp)
                .then(done, done.fail);
            });
        });

        [false, true].forEach(function(hasCssTransform) {
            it('- hovertemplate case (same z truncation), hasCssTransform: ' + hasCssTransform, function(done) {
                var fig = base();
                fig.hovertemplate = '%{z}<extra>%{location}</extra>';
                run(hasCssTransform, pos, fig, exp)
                .then(done, done.fail);
            });
        });
    });

    [false, true].forEach(function(hasCssTransform) {
        it('should include *properties* from input custom geojson, hasCssTransform: ' + hasCssTransform, function(done) {
            var fig = Lib.extendDeep({}, require('@mocks/geo_custom-geojson.json'));
            fig.data = [fig.data[1]];
            fig.data[0].hovertemplate = '%{properties.name}<extra>%{ct[0]:.1f} | %{ct[1]:.1f}</extra>';
            fig.layout.geo.projection = {scale: 20};

            run(hasCssTransform, [300, 200], fig, ['New York', '-75.1 | 42.6'])
            .then(done, done.fail);
        });
    });
});

describe('choropleth drawing', function() {
    var gd;

    beforeEach(function() {
        gd = createGraphDiv();
    });

    afterEach(destroyGraphDiv);

    it('should not throw an error with bad locations', function(done) {
        spyOn(loggers, 'log');
        Plotly.newPlot(gd, [{
            locations: ['canada', 0, null, '', 'utopia'],
            z: [1, 2, 3, 4, 5],
            locationmode: 'country names',
            type: 'choropleth'
        }])
        .then(function() {
            // only utopia logs - others are silently ignored
            expect(loggers.log).toHaveBeenCalledTimes(1);
        })
        .then(done, done.fail);
    });

    it('preserves order after hide/show', function(done) {
        function getIndices() {
            var out = [];
            d3SelectAll('.choropleth').each(function(d) { out.push(d[0].trace.index); });
            return out;
        }

        Plotly.newPlot(gd, [{
            type: 'choropleth',
            locations: ['CAN', 'USA'],
            z: [1, 2]
        }, {
            type: 'choropleth',
            locations: ['CAN', 'USA'],
            z: [2, 1]
        }])
        .then(function() {
            expect(getIndices()).toEqual([0, 1]);
            return Plotly.restyle(gd, 'visible', false, [0]);
        })
        .then(function() {
            expect(getIndices()).toEqual([1]);
            return Plotly.restyle(gd, 'visible', true, [0]);
        })
        .then(function() {
            expect(getIndices()).toEqual([0, 1]);
        })
        .then(done, done.fail);
    });
});
<|endoftext|>


<commit_before>'use strict';

const ExistingBrowser = require('../../../lib/browser/existing-browser');

describe('browser/existing-browser', () => {
    it('should use pixel ratio if it set in calibration', () => {
        const calibration = {usePixelRatio: true};
        const browser = new ExistingBrowser('sessionId', {some: 'config'}, calibration);

        assert.equal(browser.usePixelRatio, true);
    });
});
<commit_msg>Add tests on 'ExistingBrowser.prototype.attach'<commit_after>'use strict';

const _ = require('lodash');
const wd = require('wd');
const q = require('q');

const ExistingBrowser = require('../../../lib/browser/existing-browser');

describe('browser/existing-browser', () => {
    const sandbox = sinon.sandbox.create();

    let wdRemote;

    const mkExistingBrowser = (opts) => {
        opts = _.defaults(opts || {}, {
            sessionId: '100500',
            config: {},
            calibration: {}
        });

        return new ExistingBrowser(opts.sessionId, opts.config, opts.calibration);
    };

    beforeEach(() => {
        wdRemote = {
            configureHttp: sinon.stub().returns(q()),
            attach: sinon.stub().returns(q())
        };

        sandbox.stub(wd, 'promiseRemote');
        wd.promiseRemote.returns(wdRemote);
    });

    afterEach(() => sandbox.restore());

    it('should use pixel ratio if it set in calibration', () => {
        const browser = mkExistingBrowser({calibration: {usePixelRatio: true}});

        assert.equal(browser.usePixelRatio, true);
    });

    describe('attach', () => {
        it('should attach the browser with the specified session id', () => {
            return mkExistingBrowser({sessionId: '100500'})
                .attach()
                .then(() => assert.calledWith(wdRemote.attach, '100500'));
        });

        it('should set http timeout for all requests', () => {
            return mkExistingBrowser({config: {httpTimeout: 100500}})
                .attach()
                .then(() => assert.calledWithMatch(wdRemote.configureHttp, {timeout: 100500}));
        });

        it('should return promise with ExistingBrowser instance', () => {
            const browser = mkExistingBrowser().attach();

            return assert.eventually.instanceOf(browser, ExistingBrowser);
        });
    });
});
<|endoftext|>


<commit_before>/*
 * @name Drop
 * @description You will need to include the
 * p5.dom library
 * for this example to work in your own project.

 * Drag an image file onto the canvas to see it displayed.
 */

function setup() {
  // create canvas
  var c = createCanvas(710, 400);
  background(100);
  // Add an event for when a file is dropped onto the canvas
  c.drop(gotFile);
}

function draw() {
  fill(255);
  noStroke();
  textSize(24);
  textAlign(CENTER);
  text('Drag an image file onto the canvas.', width/2, height/2);
  noLoop();
}

function gotFile(file) {
  // If it's an image file
  if (file.type === 'image') {
    // Create an image DOM element but don't show it
    var img = createImg(file.data).hide();
    // Draw the image onto the canvas
    image(img, 0, 0, width, height);
  } else {
    println('Not an image file!');
  }
}<commit_msg>Update 12_Drop.js<commit_after>/*
 * @name Drop
 * @description You will need to include the
 * p5.dom library
 * for this example to work in your own project.

 * Drag an image file onto the canvas to see it displayed.
 */

function setup() {
  // create canvas
  var c = createCanvas(710, 400);
  background(100);
  // Add an event for when a file is dropped onto the canvas
  c.drop(gotFile);
}

function draw() {
  fill(255);
  noStroke();
  textSize(24);
  textAlign(CENTER);
  text('Drag an image file onto the canvas.', width/2, height/2);
  noLoop();
}

function gotFile(file) {
  // If it's an image file
  if (file.type === 'image') {
    // Create an image DOM element but don't show it
    var img = createImg(file.data).hide();
    // Draw the image onto the canvas
    image(img, 0, 0, width, height);
  } else {
    console.log('Not an image file!');
  }
}
<|endoftext|>


<commit_before>import Backbone from 'backbone';
import {
  isEmpty,
  isArray,
  isString,
  each,
  includes,
  extend,
  flatten,
  debounce
} from 'underscore';
import Component, { keySymbol, keySymbols } from './Component';

const getIdsToKeep = (prev, res = []) => {
  const pr = prev || [];
  pr.forEach(comp => {
    res.push(comp.getId());
    getIdsToKeep(comp.components(), res);
  });
  return res;
};

const getNewIds = (items, res = []) => {
  items.map(item => {
    res.push(item.getId());
    getNewIds(item.components(), res);
  });
  return res;
};

export default Backbone.Collection.extend({
  initialize(models, opt = {}) {
    this.opt = opt;
    this.listenTo(this, 'add', this.onAdd);
    this.listenTo(this, 'remove', this.removeChildren);
    this.listenTo(this, 'reset', this.resetChildren);
    const { em, config } = opt;
    this.config = config;
    this.em = em;
    this.domc = opt.domc || (em && em.get('DomComponents'));
  },

  resetChildren(models, opts = {}) {
    const coll = this;
    const prev = opts.previousModels || [];
    const toRemove = prev.filter(prev => !models.get(prev.cid));
    const newIds = getNewIds(models);
    opts.keepIds = getIdsToKeep(prev).filter(pr => newIds.indexOf(pr) >= 0);
    toRemove.forEach(md => this.removeChildren(md, coll, opts));
    models.each(model => this.onAdd(model));
  },

  removeChildren(removed, coll, opts = {}) {
    // Removing a parent component can cause this function
    // to be called with an already removed child element
    if (!removed) {
      return;
    }

    const { domc, em } = this;
    const allByID = domc ? domc.allById() : {};
    const isTemp = opts.temporary;
    removed.prevColl = this; // This one is required for symbols

    if (!isTemp) {
      // Remove the component from the global list
      const id = removed.getId();
      const sels = em.get('SelectorManager').getAll();
      const rules = em.get('CssComposer').getAll();
      const canRemoveStyle = (opts.keepIds || []).indexOf(id) < 0;
      delete allByID[id];

      // Remove all component related styles
      const rulesRemoved = canRemoveStyle
        ? rules.remove(
            rules.filter(r => r.getSelectors().getFullString() === `#${id}`),
            opts
          )
        : [];

      // Clean selectors
      sels.remove(rulesRemoved.map(rule => rule.getSelectors().at(0)));

      if (!removed.opt.temporary) {
        const cm = em.get('Commands');
        const hasSign = removed.get('style-signature');
        const optStyle = { target: removed };
        hasSign && cm.run('core:component-style-clear', optStyle);
        removed.removed();
        removed.trigger('removed');
        em.trigger('component:remove', removed);
      }

      const inner = removed.components();
      inner.forEach(it => this.removeChildren(it, coll, opts));
      // removed.empty(opts);
    }

    // Remove stuff registered in DomComponents.handleChanges
    const inner = removed.components();
    em.stopListening(inner);
    em.stopListening(removed);
    em.stopListening(removed.get('classes'));
    removed.__postRemove();
  },

  model(attrs, options) {
    const { opt } = options.collection;
    const { em } = opt;
    let model;
    const df = em.get('DomComponents').componentTypes;
    options.em = em;
    options.config = opt.config;
    options.componentTypes = df;
    options.domc = opt.domc;

    for (let it = 0; it < df.length; it++) {
      const dfId = df[it].id;
      if (dfId == attrs.type) {
        model = df[it].model;
        break;
      }
    }

    // If no model found, get the default one
    if (!model) {
      model = df[df.length - 1].model;
      em &&
        attrs.type &&
        em.logWarning(`Component type '${attrs.type}' not found`, {
          attrs,
          options
        });
    }

    return new model(attrs, options);
  },

  parseString(value, opt = {}) {
    const { em, domc } = this;
    const cssc = em.get('CssComposer');
    const parsed = em.get('Parser').parseHtml(value);
    // We need this to avoid duplicate IDs
    Component.checkId(parsed.html, parsed.css, domc.componentsById, opt);

    if (parsed.css && cssc && !opt.temporary) {
      cssc.addCollection(parsed.css, {
        ...opt,
        extend: 1
      });
    }

    return parsed.html;
  },

  add(models, opt = {}) {
    const { parent } = this;
    opt.keepIds = getIdsToKeep(opt.previousModels);

    if (isString(models)) {
      models = this.parseString(models, opt);
    } else if (isArray(models)) {
      models.forEach((item, index) => {
        if (isString(item)) {
          const nodes = this.parseString(item, opt);
          models[index] = isArray(nodes) && !nodes.length ? null : nodes;
        }
      });
    }

    const isMult = isArray(models);
    models = (isMult ? models : [models])
      .filter(i => i)
      .map(model => this.processDef(model));
    models = isMult ? flatten(models, 1) : models[0];

    const result = Backbone.Collection.prototype.add.apply(this, [models, opt]);
    this.__firstAdd = result;
    return result;
  },

  /**
   * Process component definition.
   */
  processDef(mdl) {
    // Avoid processing Models
    if (mdl.cid && mdl.ccid) return mdl;
    const { em, config = {} } = this;
    const { processor } = config;
    let model = mdl;

    if (processor) {
      model = { ...model }; // Avoid 'Cannot delete property ...'
      const modelPr = processor(model);
      if (modelPr) {
        each(model, (val, key) => delete model[key]);
        extend(model, modelPr);
      }
    }

    // React JSX preset
    if (model.$$typeof && typeof model.props == 'object') {
      model = { ...model };
      model.props = { ...model.props };
      const domc = em.get('DomComponents');
      const parser = em.get('Parser');
      const { parserHtml } = parser;

      each(model, (value, key) => {
        if (!includes(['props', 'type'], key)) delete model[key];
      });
      const { props } = model;
      const comps = props.children;
      delete props.children;
      delete model.props;
      const res = parserHtml.splitPropsFromAttr(props);
      model.attributes = res.attrs;

      if (comps) {
        model.components = comps;
      }
      if (!model.type) {
        model.type = 'textnode';
      } else if (!domc.getType(model.type)) {
        model.tagName = model.type;
        delete model.type;
      }

      extend(model, res.props);
    }

    return model;
  },

  onAdd(model, c, opts = {}) {
    const { domc, em } = this;
    const style = model.getStyle();
    const avoidInline = em && em.getConfig('avoidInlineStyle');
    domc && domc.Component.ensureInList(model);

    if (
      !isEmpty(style) &&
      !avoidInline &&
      em &&
      em.get &&
      em.getConfig('forceClass') &&
      !opts.temporary
    ) {
      const name = model.cid;
      const rule = em.get('CssComposer').setClassRule(name, style);
      model.setStyle({});
      model.addClass(name);
    }

    model.__postAdd({ recursive: 1 });
    this.__onAddEnd();
  },

  __onAddEnd: debounce(function() {
    // TODO to check symbols on load, probably this might be removed as symbols
    // are always recovered from the model
    // const { domc } = this;
    // const allComp = (domc && domc.allById()) || {};
    // const firstAdd = this.__firstAdd;
    // const toCheck = isArray(firstAdd) ? firstAdd : [firstAdd];
    // const silent = { silent: true };
    // const onAll = comps => {
    //   comps.forEach(comp => {
    //     const symbol = comp.get(keySymbols);
    //     const symbolOf = comp.get(keySymbol);
    //     if (symbol && isArray(symbol) && isString(symbol[0])) {
    //       comp.set(
    //         keySymbols,
    //         symbol.map(smb => allComp[smb]).filter(i => i),
    //         silent
    //       );
    //     }
    //     if (isString(symbolOf)) {
    //       comp.set(keySymbol, allComp[symbolOf], silent);
    //     }
    //     onAll(comp.components());
    //   });
    // };
    // onAll(toCheck);
  })
});
<commit_msg>Clear options on components parser<commit_after>import Backbone from 'backbone';
import {
  isEmpty,
  isArray,
  isString,
  each,
  includes,
  extend,
  flatten,
  debounce
} from 'underscore';
import Component, { keySymbol, keySymbols } from './Component';

const getIdsToKeep = (prev, res = []) => {
  const pr = prev || [];
  pr.forEach(comp => {
    res.push(comp.getId());
    getIdsToKeep(comp.components(), res);
  });
  return res;
};

const getNewIds = (items, res = []) => {
  items.map(item => {
    res.push(item.getId());
    getNewIds(item.components(), res);
  });
  return res;
};

export default Backbone.Collection.extend({
  initialize(models, opt = {}) {
    this.opt = opt;
    this.listenTo(this, 'add', this.onAdd);
    this.listenTo(this, 'remove', this.removeChildren);
    this.listenTo(this, 'reset', this.resetChildren);
    const { em, config } = opt;
    this.config = config;
    this.em = em;
    this.domc = opt.domc || (em && em.get('DomComponents'));
  },

  resetChildren(models, opts = {}) {
    const coll = this;
    const prev = opts.previousModels || [];
    const toRemove = prev.filter(prev => !models.get(prev.cid));
    const newIds = getNewIds(models);
    opts.keepIds = getIdsToKeep(prev).filter(pr => newIds.indexOf(pr) >= 0);
    toRemove.forEach(md => this.removeChildren(md, coll, opts));
    models.each(model => this.onAdd(model));
  },

  removeChildren(removed, coll, opts = {}) {
    // Removing a parent component can cause this function
    // to be called with an already removed child element
    if (!removed) {
      return;
    }

    const { domc, em } = this;
    const allByID = domc ? domc.allById() : {};
    const isTemp = opts.temporary;
    removed.prevColl = this; // This one is required for symbols

    if (!isTemp) {
      // Remove the component from the global list
      const id = removed.getId();
      const sels = em.get('SelectorManager').getAll();
      const rules = em.get('CssComposer').getAll();
      const canRemoveStyle = (opts.keepIds || []).indexOf(id) < 0;
      delete allByID[id];

      // Remove all component related styles
      const rulesRemoved = canRemoveStyle
        ? rules.remove(
            rules.filter(r => r.getSelectors().getFullString() === `#${id}`),
            opts
          )
        : [];

      // Clean selectors
      sels.remove(rulesRemoved.map(rule => rule.getSelectors().at(0)));

      if (!removed.opt.temporary) {
        const cm = em.get('Commands');
        const hasSign = removed.get('style-signature');
        const optStyle = { target: removed };
        hasSign && cm.run('core:component-style-clear', optStyle);
        removed.removed();
        removed.trigger('removed');
        em.trigger('component:remove', removed);
      }

      const inner = removed.components();
      inner.forEach(it => this.removeChildren(it, coll, opts));
      // removed.empty(opts);
    }

    // Remove stuff registered in DomComponents.handleChanges
    const inner = removed.components();
    em.stopListening(inner);
    em.stopListening(removed);
    em.stopListening(removed.get('classes'));
    removed.__postRemove();
  },

  model(attrs, options) {
    const { opt } = options.collection;
    const { em } = opt;
    let model;
    const df = em.get('DomComponents').componentTypes;
    options.em = em;
    options.config = opt.config;
    options.componentTypes = df;
    options.domc = opt.domc;

    for (let it = 0; it < df.length; it++) {
      const dfId = df[it].id;
      if (dfId == attrs.type) {
        model = df[it].model;
        break;
      }
    }

    // If no model found, get the default one
    if (!model) {
      model = df[df.length - 1].model;
      em &&
        attrs.type &&
        em.logWarning(`Component type '${attrs.type}' not found`, {
          attrs,
          options
        });
    }

    return new model(attrs, options);
  },

  parseString(value, opt = {}) {
    const { em, domc } = this;
    const cssc = em.get('CssComposer');
    const parsed = em.get('Parser').parseHtml(value);
    // We need this to avoid duplicate IDs
    Component.checkId(parsed.html, parsed.css, domc.componentsById, opt);

    if (parsed.css && cssc && !opt.temporary) {
      const { at, ...optsToPass } = opt;
      cssc.addCollection(parsed.css, {
        ...optsToPass,
        extend: 1
      });
    }

    return parsed.html;
  },

  add(models, opt = {}) {
    const { parent } = this;
    opt.keepIds = getIdsToKeep(opt.previousModels);

    if (isString(models)) {
      models = this.parseString(models, opt);
    } else if (isArray(models)) {
      models.forEach((item, index) => {
        if (isString(item)) {
          const nodes = this.parseString(item, opt);
          models[index] = isArray(nodes) && !nodes.length ? null : nodes;
        }
      });
    }

    const isMult = isArray(models);
    models = (isMult ? models : [models])
      .filter(i => i)
      .map(model => this.processDef(model));
    models = isMult ? flatten(models, 1) : models[0];

    const result = Backbone.Collection.prototype.add.apply(this, [models, opt]);
    this.__firstAdd = result;
    return result;
  },

  /**
   * Process component definition.
   */
  processDef(mdl) {
    // Avoid processing Models
    if (mdl.cid && mdl.ccid) return mdl;
    const { em, config = {} } = this;
    const { processor } = config;
    let model = mdl;

    if (processor) {
      model = { ...model }; // Avoid 'Cannot delete property ...'
      const modelPr = processor(model);
      if (modelPr) {
        each(model, (val, key) => delete model[key]);
        extend(model, modelPr);
      }
    }

    // React JSX preset
    if (model.$$typeof && typeof model.props == 'object') {
      model = { ...model };
      model.props = { ...model.props };
      const domc = em.get('DomComponents');
      const parser = em.get('Parser');
      const { parserHtml } = parser;

      each(model, (value, key) => {
        if (!includes(['props', 'type'], key)) delete model[key];
      });
      const { props } = model;
      const comps = props.children;
      delete props.children;
      delete model.props;
      const res = parserHtml.splitPropsFromAttr(props);
      model.attributes = res.attrs;

      if (comps) {
        model.components = comps;
      }
      if (!model.type) {
        model.type = 'textnode';
      } else if (!domc.getType(model.type)) {
        model.tagName = model.type;
        delete model.type;
      }

      extend(model, res.props);
    }

    return model;
  },

  onAdd(model, c, opts = {}) {
    const { domc, em } = this;
    const style = model.getStyle();
    const avoidInline = em && em.getConfig('avoidInlineStyle');
    domc && domc.Component.ensureInList(model);

    if (
      !isEmpty(style) &&
      !avoidInline &&
      em &&
      em.get &&
      em.getConfig('forceClass') &&
      !opts.temporary
    ) {
      const name = model.cid;
      const rule = em.get('CssComposer').setClassRule(name, style);
      model.setStyle({});
      model.addClass(name);
    }

    model.__postAdd({ recursive: 1 });
    this.__onAddEnd();
  },

  __onAddEnd: debounce(function() {
    // TODO to check symbols on load, probably this might be removed as symbols
    // are always recovered from the model
    // const { domc } = this;
    // const allComp = (domc && domc.allById()) || {};
    // const firstAdd = this.__firstAdd;
    // const toCheck = isArray(firstAdd) ? firstAdd : [firstAdd];
    // const silent = { silent: true };
    // const onAll = comps => {
    //   comps.forEach(comp => {
    //     const symbol = comp.get(keySymbols);
    //     const symbolOf = comp.get(keySymbol);
    //     if (symbol && isArray(symbol) && isString(symbol[0])) {
    //       comp.set(
    //         keySymbols,
    //         symbol.map(smb => allComp[smb]).filter(i => i),
    //         silent
    //       );
    //     }
    //     if (isString(symbolOf)) {
    //       comp.set(keySymbol, allComp[symbolOf], silent);
    //     }
    //     onAll(comp.components());
    //   });
    // };
    // onAll(toCheck);
  })
});
<|endoftext|>


<commit_before>import Backbone from 'backbone';
import { isEmpty, isArray, isString, each, includes, extend } from 'underscore';

let Component;

export default Backbone.Collection.extend({
  initialize(models, opt = {}) {
    this.opt = opt;
    this.listenTo(this, 'add', this.onAdd);
    this.listenTo(this, 'remove', this.removeChildren);
    this.listenTo(this, 'reset', this.resetChildren);
    this.config = opt.config;
    this.em = opt.em;
    this.domc = opt.domc;
  },

  resetChildren(models, opts = {}) {
    const coll = this;
    const { previousModels = [] } = opts;
    previousModels.forEach(md => this.removeChildren(md, coll, opts));
    models.each(model => this.onAdd(model));
  },

  removeChildren(removed, coll, opts = {}) {
    const { domc, em } = this;
    const allByID = domc ? domc.allById() : {};

    if (!opts.temporary) {
      // Remove the component from the gloabl list
      const id = removed.getId();
      const sels = em.get('SelectorManager').getAll();
      const rules = em.get('CssComposer').getAll();
      delete allByID[id];

      // Remove all component related styles
      const rulesRemoved = rules.remove(
        rules.filter(r => r.getSelectors().getFullString() === `#${id}`)
      );

      // Clean selectors
      sels.remove(rulesRemoved.map(rule => rule.getSelectors().at(0)));

      if (!removed.opt.temporary) {
        const cm = em.get('Commands');
        const hasSign = removed.get('style-signature');
        const optStyle = { target: removed };
        hasSign && cm.run('core:component-style-clear', optStyle);
        removed.removed();
        em.trigger('component:remove', removed);
      }

      const inner = removed.components();
      inner.forEach(it => this.removeChildren(it, coll, opts));
      // removed.empty(opts);
    }

    // Remove stuff registered in DomComponents.handleChanges
    const inner = removed.components();
    const um = em.get('UndoManager');
    em.stopListening(inner);
    em.stopListening(removed);
    em.stopListening(removed.get('classes'));
    um.remove(removed);
    um.remove(inner);
  },

  model(attrs, options) {
    const { opt } = options.collection;
    const { em } = opt;
    let model;
    const df = em.get('DomComponents').componentTypes;
    options.em = em;
    options.config = opt.config;
    options.componentTypes = df;
    options.domc = opt.domc;

    for (let it = 0; it < df.length; it++) {
      const dfId = df[it].id;
      if (dfId == attrs.type) {
        model = df[it].model;
        break;
      }
    }

    // If no model found, get the default one
    if (!model) {
      model = df[df.length - 1].model;
      em &&
        attrs.type &&
        em.logWarning(`Component type '${attrs.type}' not found`, {
          attrs,
          options
        });
    }

    return new model(attrs, options);
  },

  parseString(value, opt = {}) {
    const { em } = this;
    const { domc } = this.opt;
    const cssc = em.get('CssComposer');
    const parsed = em.get('Parser').parseHtml(value);
    // We need this to avoid duplicate IDs
    if (!Component) Component = require('./Component').default;
    Component.checkId(parsed.html, parsed.css, domc.componentsById);

    if (parsed.css && cssc && !opt.temporary) {
      cssc.addCollection(parsed.css, {
        ...opt,
        extend: 1
      });
    }

    return parsed.html;
  },

  add(models, opt = {}) {
    if (isString(models)) {
      models = this.parseString(models, opt);
    } else if (isArray(models)) {
      models.forEach((item, index) => {
        if (isString(item)) {
          const nodes = this.parseString(item, opt);
          models[index] = isArray(nodes) && !nodes.length ? null : nodes;
        }
      });
    }

    const isMult = isArray(models);
    models = (isMult ? models : [models])
      .filter(i => i)
      .map(model => this.processDef(model));
    models = isMult ? models : models[0];

    return Backbone.Collection.prototype.add.apply(this, [models, opt]);
  },

  /**
   * Process component definition.
   */
  processDef(mdl) {
    // Avoid processing Models
    if (mdl.cid && mdl.ccid) return mdl;
    const { em, config = {} } = this;
    const { processor } = config;
    let model = mdl;

    if (processor) {
      model = { ...model }; // Avoid 'Cannot delete property ...'
      const modelPr = processor(model);
      if (modelPr) {
        each(model, (val, key) => delete model[key]);
        extend(model, modelPr);
      }
    }

    // React JSX preset
    if (model.$$typeof && typeof model.props == 'object') {
      model = { ...model };
      model.props = { ...model.props };
      const domc = em.get('DomComponents');
      const parser = em.get('Parser');
      const { parserHtml } = parser;

      each(model, (value, key) => {
        if (!includes(['props', 'type'], key)) delete model[key];
      });
      const { props } = model;
      const comps = props.children;
      delete props.children;
      delete model.props;
      const res = parserHtml.splitPropsFromAttr(props);
      model.attributes = res.attrs;

      if (comps) {
        model.components = comps;
      }
      if (!model.type) {
        model.type = 'textnode';
      } else if (!domc.getType(model.type)) {
        model.tagName = model.type;
        delete model.type;
      }

      extend(model, res.props);
    }

    return model;
  },

  onAdd(model, c, opts = {}) {
    const { domc, em } = this;
    const style = model.getStyle();
    const avoidInline = em && em.getConfig('avoidInlineStyle');
    domc && domc.Component.ensureInList(model);

    if (
      !isEmpty(style) &&
      !avoidInline &&
      em &&
      em.get &&
      em.getConfig('forceClass') &&
      !opts.temporary
    ) {
      const name = model.cid;
      const rule = em.get('CssComposer').setClassRule(name, style);
      model.setStyle({});
      model.addClass(name);
    }
  }
});
<commit_msg>validating presence of component before removal<commit_after>import Backbone from 'backbone';
import { isEmpty, isArray, isString, each, includes, extend } from 'underscore';

let Component;

export default Backbone.Collection.extend({
  initialize(models, opt = {}) {
    this.opt = opt;
    this.listenTo(this, 'add', this.onAdd);
    this.listenTo(this, 'remove', this.removeChildren);
    this.listenTo(this, 'reset', this.resetChildren);
    this.config = opt.config;
    this.em = opt.em;
    this.domc = opt.domc;
  },

  resetChildren(models, opts = {}) {
    const coll = this;
    const { previousModels = [] } = opts;
    previousModels.forEach(md => this.removeChildren(md, coll, opts));
    models.each(model => this.onAdd(model));
  },

  removeChildren(removed, coll, opts = {}) {
    // Removing a parent component can cause this function
    // to be called with an already removed child element
    if (!removed) {
      return;
    }

    const { domc, em } = this;
    const allByID = domc ? domc.allById() : {};

    if (!opts.temporary) {
      // Remove the component from the gloabl list
      const id = removed.getId();
      const sels = em.get('SelectorManager').getAll();
      const rules = em.get('CssComposer').getAll();
      delete allByID[id];

      // Remove all component related styles
      const rulesRemoved = rules.remove(
        rules.filter(r => r.getSelectors().getFullString() === `#${id}`)
      );

      // Clean selectors
      sels.remove(rulesRemoved.map(rule => rule.getSelectors().at(0)));

      if (!removed.opt.temporary) {
        const cm = em.get('Commands');
        const hasSign = removed.get('style-signature');
        const optStyle = { target: removed };
        hasSign && cm.run('core:component-style-clear', optStyle);
        removed.removed();
        em.trigger('component:remove', removed);
      }

      const inner = removed.components();
      inner.forEach(it => this.removeChildren(it, coll, opts));
      // removed.empty(opts);
    }

    // Remove stuff registered in DomComponents.handleChanges
    const inner = removed.components();
    const um = em.get('UndoManager');
    em.stopListening(inner);
    em.stopListening(removed);
    em.stopListening(removed.get('classes'));
    um.remove(removed);
    um.remove(inner);
  },

  model(attrs, options) {
    const { opt } = options.collection;
    const { em } = opt;
    let model;
    const df = em.get('DomComponents').componentTypes;
    options.em = em;
    options.config = opt.config;
    options.componentTypes = df;
    options.domc = opt.domc;

    for (let it = 0; it < df.length; it++) {
      const dfId = df[it].id;
      if (dfId == attrs.type) {
        model = df[it].model;
        break;
      }
    }

    // If no model found, get the default one
    if (!model) {
      model = df[df.length - 1].model;
      em &&
        attrs.type &&
        em.logWarning(`Component type '${attrs.type}' not found`, {
          attrs,
          options
        });
    }

    return new model(attrs, options);
  },

  parseString(value, opt = {}) {
    const { em } = this;
    const { domc } = this.opt;
    const cssc = em.get('CssComposer');
    const parsed = em.get('Parser').parseHtml(value);
    // We need this to avoid duplicate IDs
    if (!Component) Component = require('./Component').default;
    Component.checkId(parsed.html, parsed.css, domc.componentsById);

    if (parsed.css && cssc && !opt.temporary) {
      cssc.addCollection(parsed.css, {
        ...opt,
        extend: 1
      });
    }

    return parsed.html;
  },

  add(models, opt = {}) {
    if (isString(models)) {
      models = this.parseString(models, opt);
    } else if (isArray(models)) {
      models.forEach((item, index) => {
        if (isString(item)) {
          const nodes = this.parseString(item, opt);
          models[index] = isArray(nodes) && !nodes.length ? null : nodes;
        }
      });
    }

    const isMult = isArray(models);
    models = (isMult ? models : [models])
      .filter(i => i)
      .map(model => this.processDef(model));
    models = isMult ? models : models[0];

    return Backbone.Collection.prototype.add.apply(this, [models, opt]);
  },

  /**
   * Process component definition.
   */
  processDef(mdl) {
    // Avoid processing Models
    if (mdl.cid && mdl.ccid) return mdl;
    const { em, config = {} } = this;
    const { processor } = config;
    let model = mdl;

    if (processor) {
      model = { ...model }; // Avoid 'Cannot delete property ...'
      const modelPr = processor(model);
      if (modelPr) {
        each(model, (val, key) => delete model[key]);
        extend(model, modelPr);
      }
    }

    // React JSX preset
    if (model.$$typeof && typeof model.props == 'object') {
      model = { ...model };
      model.props = { ...model.props };
      const domc = em.get('DomComponents');
      const parser = em.get('Parser');
      const { parserHtml } = parser;

      each(model, (value, key) => {
        if (!includes(['props', 'type'], key)) delete model[key];
      });
      const { props } = model;
      const comps = props.children;
      delete props.children;
      delete model.props;
      const res = parserHtml.splitPropsFromAttr(props);
      model.attributes = res.attrs;

      if (comps) {
        model.components = comps;
      }
      if (!model.type) {
        model.type = 'textnode';
      } else if (!domc.getType(model.type)) {
        model.tagName = model.type;
        delete model.type;
      }

      extend(model, res.props);
    }

    return model;
  },

  onAdd(model, c, opts = {}) {
    const { domc, em } = this;
    const style = model.getStyle();
    const avoidInline = em && em.getConfig('avoidInlineStyle');
    domc && domc.Component.ensureInList(model);

    if (
      !isEmpty(style) &&
      !avoidInline &&
      em &&
      em.get &&
      em.getConfig('forceClass') &&
      !opts.temporary
    ) {
      const name = model.cid;
      const rule = em.get('CssComposer').setClassRule(name, style);
      model.setStyle({});
      model.addClass(name);
    }
  }
});
<|endoftext|>