<commit_before>/* ************************************************************************
 *
 *    qooxdoo-compiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/qooxdoo/qooxdoo-compiler
 *
 *    Copyright:
 *      2011-2017 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: https://opensource.org/licenses/MIT
 *
 *      This software is provided under the same licensing terms as Qooxdoo,
 *      please see the LICENSE file in the Qooxdoo project's top-level directory
 *      for details.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com, @johnspackman)
 *
 * *********************************************************************** */

/* eslint-disable @qooxdoo/qx/no-illegal-private-usage */

var path = require("path");

var log = require("../util").createLog("resource-manager");

/**
 * Analyses library resources, collecting information into a cached database
 * file
 */
qx.Class.define("qx.tool.compiler.resources.Manager", {
  extend: qx.core.Object,

  /**
   * Constructor
   *
   * @param dbFilename
   *          {String?} database filename, default is "resource-db.json"
   */
  construct: function(analyser) {
    this.base(arguments);
    this.__analyser = analyser;
    this.__dbFilename = analyser.getResDbFilename() || "resource-db.json";
    this.__loaders = [
      new qx.tool.compiler.resources.ImageLoader(),
      new qx.tool.compiler.resources.MetaLoader()
    ];
    this.__converters = [
      new qx.tool.compiler.resources.ScssConverter()
    ];
  },

  members: {
    /** {String} filename of database */
    __dbFilename: null,

    /** {Object} Database */
    __db: null,

    /** the used analyser */
    __analyser: null,

    /** {Map{String,Library}} Lookup of libraries, indexed by resource URI */
    __librariesByResourceUri: null,

    /** {String[]} Array of all resource URIs, sorted alphabetically (ie these are the keys in __librariesByResourceUri) */
    __allResourceUris: null,

    /** {ResourceLoader[]} list of resource loaders, used to add info to the database */
    __loaders: null,

    /** {ResourceConverter[]} list of resource converters, used to copy resources to the target */
    __converters: null,

    /**
     * Loads the cached database
     */
    async loadDatabase() {
      this.__db = (await qx.tool.utils.Json.loadJsonAsync(this.__dbFilename))||{};
    },

    /**
     * Saves the database
     */
    async saveDatabase() {
      log.debug("saving resource manager database");
      return qx.tool.utils.Json.saveJsonAsync(this.__dbFilename, this.__db);
    },

    /**
     * Returns the loaded database
     *
     * @returns
     */
    getDatabase: function() {
      return this.__db;
    },

    /**
     * Finds the library needed for a resource; this depends on `findAllResources` having
     * already been called.  `uri` can include optional explicit namespace (eg "qx:blah/blah.png"),
     * otherwise the library resource lookups are examined to find the library.
     *
     * @param uri {String} URI
     * @return {Library?} the library, null if not found
     */
    findLibraryForResource: function(uri) {
      // Explicit library?
      var pos = uri.indexOf(":");
      if (pos !== -1) {
        var ns = uri.substring(0, pos);
        var library = this.__analyser.findLibrary(ns);
        return library || null;
      }

      // Absolute path
      if (uri[0] == "/") {
        let library = this.__analyser.getLibraries().find(lib => uri.startsWith(path.resolve(lib.getRootDir())));
        if (library) {
          return library;
        }
      }

      // Non-wildcards are a direct lookup
      // check for $ and *. less pos wins
      // fix for https://github.com/qooxdoo/qooxdoo-compiler/issues/260
      var pos1 = uri.indexOf("$"); // Variable references are effectively a wildcard lookup
      var pos2 = uri.indexOf("*");
      if (pos1 === -1) {
        pos = pos2;
      } else if (pos2 === -1) {
        pos = pos1;
      } else {
        pos = Math.min(pos1, pos2);
      }
      if (pos === -1) {
        library = this.__librariesByResourceUri[uri] || null;
        return library;
      }

      // Strip wildcard
      var isFolderMatch = uri[pos - 1] === "/";
      uri = uri.substring(0, pos - 1);

      // Fast folder match
      if (isFolderMatch) {
        library = this.__librariesByResourceUri[uri] || null;
        return library;
      }

      // Slow scan
      if (!this.__allResourceUris) {
        this.__allResourceUris = Object.keys(this.__librariesByResourceUri).sort();
      }
      pos = qx.tool.utils.Values.binaryStartsWith(this.__allResourceUris, uri);
      if (pos > -1) {
        var firstUri = this.__allResourceUris[pos];
        library = this.__librariesByResourceUri[firstUri] || null;
        return library;
      }

      return null;
    },

    /**
     * Scans all libraries looking for resources; this does not analyse the
     * files, simply compiles the list
     *
     * @param callback
     */
    async findAllResources() {
      var t = this;
      var db = this.__db;
      if (!db.resources) {
        db.resources = {};
      }
      t.__librariesByResourceUri = {};
      this.__allResourceUris = null;
      this.__assets = {};

      await qx.Promise.all(t.__analyser.getLibraries().map(async library => {
        var resources = db.resources[library.getNamespace()];
        if (!resources) {
          db.resources[library.getNamespace()] = resources = {};
        }
        var unconfirmed = {};
        for (let relFile in resources) {
          unconfirmed[relFile] = true;
        }

        const scanResources = async resourcePath => {
          // If the root folder exists, scan it
          var rootDir = path.join(library.getRootDir(), library.get(resourcePath));
          await qx.tool.utils.files.Utils.findAllFiles(rootDir, async filename => {
            var relFile = filename.substring(rootDir.length + 1).replace(/\\/g, "/");
            var fileInfo = resources[relFile];
            delete unconfirmed[relFile];
            if (!fileInfo) {
              fileInfo = resources[relFile] = {};
            }
            fileInfo.resourcePath = resourcePath;
            fileInfo.mtime = await qx.tool.utils.files.Utils.safeStat(filename).mtime;

            let asset = new qx.tool.compiler.resources.Asset(library, relFile, fileInfo);
            this.__addAsset(asset);
          });
        };

        await scanResources("resourcePath");
        await scanResources("themePath");

        // Check the unconfirmed resources to make sure that they still exist;
        //  delete from the database if they don't
        await qx.Promise.all(Object.keys(unconfirmed).map(async filename => {
          let fileInfo = resources[filename];
          if (!fileInfo) {
            delete resources[filename];
          } else {
            let stat = await qx.tool.utils.files.Utils.safeStat(filename);
            if (!stat) {
              delete resources[filename];
            }
          }
        }));
      }));

      await qx.tool.utils.Promisify.poolEachOf(Object.values(this.__assets), 10, async asset => {
        await asset.load();
        let fileInfo = asset.getFileInfo();
        if (fileInfo.meta) {
          for (var altPath in fileInfo.meta) {
            let otherAsset = this.__assets[asset.getLibrary().getNamespace() + ":" + altPath];
            if (otherAsset) {
              otherAsset.addMetaReferee(asset);
              asset.addMetaReferTo(otherAsset);
            } else {
              this.warn("Cannot find asset " + altPath + " referenced in " + asset);
            }
          }
        }
        if (fileInfo.dependsOn) {
          let dependsOn = [];
          fileInfo.dependsOn.forEach(str => {
            let otherAsset = this.__assets[str];
            if (!otherAsset) {
              this.warn("Cannot find asset " + str + " depended on by " + asset);
            } else {
              dependsOn.push(otherAsset);
            }
          });
          if (dependsOn.length) {
            asset.setDependsOn(dependsOn);
          }
        }
        return null;
      });
    },

    /**
     * Adds an asset
     *
     * @param asset {Asset} the asset to add
     */
    __addAsset(asset) {
      this.__assets[asset.toUri()] = asset;

      let library = asset.getLibrary();
      let filename = asset.getFilename();
      let tmp = "";
      filename.split("/").forEach((seg, index) => {
        if (index) {
          tmp += "/";
        }
        tmp += seg;
        this.__librariesByResourceUri[tmp] = library;
      });

      asset.setLoaders(this.__loaders.filter(loader => loader.matches(filename)));
      asset.setConverters(this.__converters.filter(converter => converter.matches(filename)));
    },

    /**
     * Gets an individual asset
     *
     * @param srcPath {String} the resource name, with or without a namespace prefix
     * @param create {Boolean?} if true the asset will be created if it does not exist
     * @return {Asset?} the asset, if found
     */
    getAsset(srcPath, create) {
      let pos = srcPath.indexOf(":");
      let library = null;
      if (pos > -1) {
        let ns = srcPath.substring(0, pos);
        library = this.__analyser.findLibrary(ns);
        srcPath = srcPath.substring(pos + 1);
      } else {
        library = this.findLibraryForResource(srcPath);
      }

      if (!library) {
        this.warn("Cannot find library for " + srcPath);
        return null;
      }
      let asset = this.__assets[library.getNamespace() + ":" + srcPath];
      if (!asset && create) {
        asset = new qx.tool.compiler.resources.Asset(library, srcPath, {
          resourcePath: "resourcePath"
        });
        this.__addAsset(asset);
      }
      return asset;
    },

    /**
     * Collects information about the assets listed in srcPaths;
     *
     * @param srcPaths
     * @return {Asset[]}
     */
    getAssetsForPaths(srcPaths) {
      var db = this.__db;

      // Generate a lookup that maps the resource name to the meta file that
      //  contains the composite
      var metas = {};
      for (var libraryName in db.resources) {
        var libraryData = db.resources[libraryName];
        for (var resourcePath in libraryData) {
          var fileInfo = libraryData[resourcePath];
          if (!fileInfo.meta) {
            continue;
          }
          for (var altPath in fileInfo.meta) {
            metas[altPath] = resourcePath;
          }
        }
      }

      var assets = [];
      var assetPaths = {};

      srcPaths.forEach(srcPath => {
        let pos = srcPath.indexOf(":");
        let library = null;
        if (pos > -1) {
          let ns = srcPath.substring(0, pos);
          library = this.__analyser.findLibrary(ns);
          srcPath = srcPath.substring(pos + 1);
        } else {
          library = this.findLibraryForResource(srcPath);
        }

        if (!library) {
          this.warn("Cannot find library for " + srcPath);
          return;
        }

        let libraryData = db.resources[library.getNamespace()];
        pos = srcPath.indexOf("*");
        let resourceNames = [];
        if (pos > -1) {
          srcPath = srcPath.substring(0, pos);
          resourceNames = Object.keys(libraryData).filter(resourceName => resourceName.substring(0, srcPath.length) === srcPath);
        } else if (libraryData[srcPath]) {
          resourceNames = [ srcPath ];
        }

        resourceNames.forEach(resourceName => {
          if (assetPaths[resourceName] !== undefined) {
            return;
          }
          let asset = this.__assets[library.getNamespace() + ":" + resourceName];

          let fileInfo = asset.getFileInfo();
          if (fileInfo.doNotCopy === true) {
            return;
          }

          (asset.getMetaReferees()||[]).forEach(meta => {
            // Extract the fragment from the meta data for this particular resource
            var resMetaData = meta.getFileInfo().meta[resourceName];
            fileInfo.composite = resMetaData[3];
            fileInfo.x = resMetaData[4];
            fileInfo.y = resMetaData[5];
          });

          assets.push(asset);
          assetPaths[resourceName] = assets.length - 1;
        });
      });

      return assets;
    }
  }
});

module.exports = qx.tool.compiler.resources.Manager;
<commit_msg>if resource is living in another library like 	"qx/icon/Tango/22/actions/dialog-ok.png": [ 		22, 		22, 		"png", 		"qxl/demobrowser/demo/test/combined/icons22.png", 		0, 		0 	],<commit_after>/* ************************************************************************
 *
 *    qooxdoo-compiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/qooxdoo/qooxdoo-compiler
 *
 *    Copyright:
 *      2011-2017 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: https://opensource.org/licenses/MIT
 *
 *      This software is provided under the same licensing terms as Qooxdoo,
 *      please see the LICENSE file in the Qooxdoo project's top-level directory
 *      for details.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com, @johnspackman)
 *
 * *********************************************************************** */

/* eslint-disable @qooxdoo/qx/no-illegal-private-usage */

var path = require("path");

var log = require("../util").createLog("resource-manager");

/**
 * Analyses library resources, collecting information into a cached database
 * file
 */
qx.Class.define("qx.tool.compiler.resources.Manager", {
  extend: qx.core.Object,

  /**
   * Constructor
   *
   * @param dbFilename
   *          {String?} database filename, default is "resource-db.json"
   */
  construct: function(analyser) {
    this.base(arguments);
    this.__analyser = analyser;
    this.__dbFilename = analyser.getResDbFilename() || "resource-db.json";
    this.__loaders = [
      new qx.tool.compiler.resources.ImageLoader(),
      new qx.tool.compiler.resources.MetaLoader()
    ];
    this.__converters = [
      new qx.tool.compiler.resources.ScssConverter()
    ];
  },

  members: {
    /** {String} filename of database */
    __dbFilename: null,

    /** {Object} Database */
    __db: null,

    /** the used analyser */
    __analyser: null,

    /** {Map{String,Library}} Lookup of libraries, indexed by resource URI */
    __librariesByResourceUri: null,

    /** {String[]} Array of all resource URIs, sorted alphabetically (ie these are the keys in __librariesByResourceUri) */
    __allResourceUris: null,

    /** {ResourceLoader[]} list of resource loaders, used to add info to the database */
    __loaders: null,

    /** {ResourceConverter[]} list of resource converters, used to copy resources to the target */
    __converters: null,

    /**
     * Loads the cached database
     */
    async loadDatabase() {
      this.__db = (await qx.tool.utils.Json.loadJsonAsync(this.__dbFilename))||{};
    },

    /**
     * Saves the database
     */
    async saveDatabase() {
      log.debug("saving resource manager database");
      return qx.tool.utils.Json.saveJsonAsync(this.__dbFilename, this.__db);
    },

    /**
     * Returns the loaded database
     *
     * @returns
     */
    getDatabase: function() {
      return this.__db;
    },

    /**
     * Finds the library needed for a resource; this depends on `findAllResources` having
     * already been called.  `uri` can include optional explicit namespace (eg "qx:blah/blah.png"),
     * otherwise the library resource lookups are examined to find the library.
     *
     * @param uri {String} URI
     * @return {Library?} the library, null if not found
     */
    findLibraryForResource: function(uri) {
      // Explicit library?
      var pos = uri.indexOf(":");
      if (pos !== -1) {
        var ns = uri.substring(0, pos);
        var library = this.__analyser.findLibrary(ns);
        return library || null;
      }

      // Absolute path
      if (uri[0] == "/") {
        let library = this.__analyser.getLibraries().find(lib => uri.startsWith(path.resolve(lib.getRootDir())));
        if (library) {
          return library;
        }
      }

      // Non-wildcards are a direct lookup
      // check for $ and *. less pos wins
      // fix for https://github.com/qooxdoo/qooxdoo-compiler/issues/260
      var pos1 = uri.indexOf("$"); // Variable references are effectively a wildcard lookup
      var pos2 = uri.indexOf("*");
      if (pos1 === -1) {
        pos = pos2;
      } else if (pos2 === -1) {
        pos = pos1;
      } else {
        pos = Math.min(pos1, pos2);
      }
      if (pos === -1) {
        library = this.__librariesByResourceUri[uri] || null;
        return library;
      }

      // Strip wildcard
      var isFolderMatch = uri[pos - 1] === "/";
      uri = uri.substring(0, pos - 1);

      // Fast folder match
      if (isFolderMatch) {
        library = this.__librariesByResourceUri[uri] || null;
        return library;
      }

      // Slow scan
      if (!this.__allResourceUris) {
        this.__allResourceUris = Object.keys(this.__librariesByResourceUri).sort();
      }
      pos = qx.tool.utils.Values.binaryStartsWith(this.__allResourceUris, uri);
      if (pos > -1) {
        var firstUri = this.__allResourceUris[pos];
        library = this.__librariesByResourceUri[firstUri] || null;
        return library;
      }

      return null;
    },

    /**
     * Scans all libraries looking for resources; this does not analyse the
     * files, simply compiles the list
     *
     * @param callback
     */
    async findAllResources() {
      var t = this;
      var db = this.__db;
      if (!db.resources) {
        db.resources = {};
      }
      t.__librariesByResourceUri = {};
      this.__allResourceUris = null;
      this.__assets = {};

      await qx.Promise.all(t.__analyser.getLibraries().map(async library => {
        var resources = db.resources[library.getNamespace()];
        if (!resources) {
          db.resources[library.getNamespace()] = resources = {};
        }
        var unconfirmed = {};
        for (let relFile in resources) {
          unconfirmed[relFile] = true;
        }

        const scanResources = async resourcePath => {
          // If the root folder exists, scan it
          var rootDir = path.join(library.getRootDir(), library.get(resourcePath));
          await qx.tool.utils.files.Utils.findAllFiles(rootDir, async filename => {
            var relFile = filename.substring(rootDir.length + 1).replace(/\\/g, "/");
            var fileInfo = resources[relFile];
            delete unconfirmed[relFile];
            if (!fileInfo) {
              fileInfo = resources[relFile] = {};
            }
            fileInfo.resourcePath = resourcePath;
            fileInfo.mtime = await qx.tool.utils.files.Utils.safeStat(filename).mtime;

            let asset = new qx.tool.compiler.resources.Asset(library, relFile, fileInfo);
            this.__addAsset(asset);
          });
        };

        await scanResources("resourcePath");
        await scanResources("themePath");

        // Check the unconfirmed resources to make sure that they still exist;
        //  delete from the database if they don't
        await qx.Promise.all(Object.keys(unconfirmed).map(async filename => {
          let fileInfo = resources[filename];
          if (!fileInfo) {
            delete resources[filename];
          } else {
            let stat = await qx.tool.utils.files.Utils.safeStat(filename);
            if (!stat) {
              delete resources[filename];
            }
          }
        }));
      }));

      await qx.tool.utils.Promisify.poolEachOf(Object.values(this.__assets), 10, async asset => {
        await asset.load();
        let fileInfo = asset.getFileInfo();
        if (fileInfo.meta) {
          for (var altPath in fileInfo.meta) {
            let lib = this.findLibraryForResource(altPath);
            if (!lib) {
              lib = asset.getLibrary();
            }
            let otherAsset = this.__assets[lib.getNamespace() + ":" + altPath];
            if (otherAsset) {
              otherAsset.addMetaReferee(asset);
              asset.addMetaReferTo(otherAsset);
            } else {
              this.warn("Cannot find asset " + altPath + " referenced in " + asset);
            }
          }
        }
        if (fileInfo.dependsOn) {
          let dependsOn = [];
          fileInfo.dependsOn.forEach(str => {
            let otherAsset = this.__assets[str];
            if (!otherAsset) {
              this.warn("Cannot find asset " + str + " depended on by " + asset);
            } else {
              dependsOn.push(otherAsset);
            }
          });
          if (dependsOn.length) {
            asset.setDependsOn(dependsOn);
          }
        }
        return null;
      });
    },

    /**
     * Adds an asset
     *
     * @param asset {Asset} the asset to add
     */
    __addAsset(asset) {
      this.__assets[asset.toUri()] = asset;

      let library = asset.getLibrary();
      let filename = asset.getFilename();
      let tmp = "";
      filename.split("/").forEach((seg, index) => {
        if (index) {
          tmp += "/";
        }
        tmp += seg;
        this.__librariesByResourceUri[tmp] = library;
      });

      asset.setLoaders(this.__loaders.filter(loader => loader.matches(filename)));
      asset.setConverters(this.__converters.filter(converter => converter.matches(filename)));
    },

    /**
     * Gets an individual asset
     *
     * @param srcPath {String} the resource name, with or without a namespace prefix
     * @param create {Boolean?} if true the asset will be created if it does not exist
     * @return {Asset?} the asset, if found
     */
    getAsset(srcPath, create) {
      let pos = srcPath.indexOf(":");
      let library = null;
      if (pos > -1) {
        let ns = srcPath.substring(0, pos);
        library = this.__analyser.findLibrary(ns);
        srcPath = srcPath.substring(pos + 1);
      } else {
        library = this.findLibraryForResource(srcPath);
      }

      if (!library) {
        this.warn("Cannot find library for " + srcPath);
        return null;
      }
      let asset = this.__assets[library.getNamespace() + ":" + srcPath];
      if (!asset && create) {
        asset = new qx.tool.compiler.resources.Asset(library, srcPath, {
          resourcePath: "resourcePath"
        });
        this.__addAsset(asset);
      }
      return asset;
    },

    /**
     * Collects information about the assets listed in srcPaths;
     *
     * @param srcPaths
     * @return {Asset[]}
     */
    getAssetsForPaths(srcPaths) {
      var db = this.__db;

      // Generate a lookup that maps the resource name to the meta file that
      //  contains the composite
      var metas = {};
      for (var libraryName in db.resources) {
        var libraryData = db.resources[libraryName];
        for (var resourcePath in libraryData) {
          var fileInfo = libraryData[resourcePath];
          if (!fileInfo.meta) {
            continue;
          }
          for (var altPath in fileInfo.meta) {
            metas[altPath] = resourcePath;
          }
        }
      }

      var assets = [];
      var assetPaths = {};

      srcPaths.forEach(srcPath => {
        let pos = srcPath.indexOf(":");
        let library = null;
        if (pos > -1) {
          let ns = srcPath.substring(0, pos);
          library = this.__analyser.findLibrary(ns);
          srcPath = srcPath.substring(pos + 1);
        } else {
          library = this.findLibraryForResource(srcPath);
        }

        if (!library) {
          this.warn("Cannot find library for " + srcPath);
          return;
        }

        let libraryData = db.resources[library.getNamespace()];
        pos = srcPath.indexOf("*");
        let resourceNames = [];
        if (pos > -1) {
          srcPath = srcPath.substring(0, pos);
          resourceNames = Object.keys(libraryData).filter(resourceName => resourceName.substring(0, srcPath.length) === srcPath);
        } else if (libraryData[srcPath]) {
          resourceNames = [ srcPath ];
        }

        resourceNames.forEach(resourceName => {
          if (assetPaths[resourceName] !== undefined) {
            return;
          }
          let asset = this.__assets[library.getNamespace() + ":" + resourceName];

          let fileInfo = asset.getFileInfo();
          if (fileInfo.doNotCopy === true) {
            return;
          }

          (asset.getMetaReferees()||[]).forEach(meta => {
            // Extract the fragment from the meta data for this particular resource
            var resMetaData = meta.getFileInfo().meta[resourceName];
            fileInfo.composite = resMetaData[3];
            fileInfo.x = resMetaData[4];
            fileInfo.y = resMetaData[5];
          });

          assets.push(asset);
          assetPaths[resourceName] = assets.length - 1;
        });
      });

      return assets;
    }
  }
});

module.exports = qx.tool.compiler.resources.Manager;
<|endoftext|>


<commit_before>'use strict';

(function poll() {
  if (typeof ynabToolKit !== 'undefined' && ynabToolKit.pageReady === true) {
    ynabToolKit.runningBalance = (function () {
      var currentlyRunning = false;

      function updateRunningBalanceCalculation(arrangedContent) {
        var i;
        var transaction;
        var runningBalance = 0;

        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');
        if (accountController.get('sortAscending')) {
          for (i = 0; i < arrangedContent.length; i++) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        } else {
          for (i = arrangedContent.length - 1; i >= 0; i--) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        }
      }

      function updateRunningBalanceColumn() {
        insertHeader();

        $('.ynab-grid-container .ynab-grid-body .ynab-grid-body-row').each(function (index, element) {
          var $element = $(element);
          if ($element.hasClass('ynab-grid-body-empty')) return;

          insertValue(element);
        });
      }

      function insertHeader() {
        if ($('.ynab-grid-header .ynab-toolkit-grid-cell-running-balance').length) return;

        var $headerRow = $('.ynab-grid-header');
        var runningBalanceHeader = $('.ynab-grid-cell-inflow', $headerRow).clone();
        runningBalanceHeader.removeClass('ynab-grid-cell-inflow');
        runningBalanceHeader.addClass('ynab-toolkit-grid-cell-running-balance');
        runningBalanceHeader.text('RUNNING BALANCE');
        runningBalanceHeader.insertAfter($('.ynab-grid-cell-inflow', $headerRow));

        if ($('.ynab-grid-body .ynab-grid-body-row-top .ynab-toolkit-grid-cell-running-balance').length) return;
        var $topRow = $('.ynab-grid-body-row-top');
        var topRowRunningBalance = $('.ynab-grid-cell-inflow', $topRow).clone();
        topRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        topRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');
        topRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $topRow));
      }

      function insertValue(element) {
        var $currentRow = $(element);
        var currentRowRunningBalance = $('.ynab-grid-cell-inflow', $currentRow).clone();
        currentRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        currentRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');

        var emberView = ynabToolKit.shared.getEmberView($currentRow.attr('id'));
        var transaction = emberView.get('content');
        var runningBalance = transaction.get('__ynabToolKitRunningBalance');
        var currencySpan = $('.user-data', currentRowRunningBalance);

        if (runningBalance < 0) {
          currencySpan.addClass('user-data currency negative');
        } else if (runningBalance > 0) {
          currencySpan.addClass('user-data currency positive');
        } else {
          currencySpan.addClass('user-data currency zero');
        }

        if (transaction.get('parentEntityId') !== null) {
          currencySpan.text('');
        } else {
          currencySpan.text(ynabToolKit.shared.formatCurrency(runningBalance));
        }

        currentRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $currentRow));
      }

      function onYnabGridyBodyChanged() {
        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');

        if (accountController.get('sortProperties').indexOf('date') !== -1) {
          updateRunningBalanceCalculation(accountController.get('arrangedContent'));
          updateRunningBalanceColumn();
        } else {
          $('.ynab-toolkit-grid-cell-running-balance').remove();
        }
      }

      function addDeadColumnToAddRows() {
        var $ynabGridAddRows = $('.ynab-grid-add-rows');

        if ($ynabGridAddRows.children().length) {
          if ($('.ynab-toolkit-grid-cell-running-balance', $ynabGridAddRows).length) return;
          $('
').insertAfter($('.ynab-grid-cell-inflow', $ynabGridAddRows));
        }
      }

      return {
        invoke: function () {
          currentlyRunning = true;

          Ember.run.next(function () {
            var applicationController = ynabToolKit.shared.containerLookup('controller:application');

            if (applicationController.get('currentPath').indexOf('accounts') > -1 && applicationController.get('selectedAccountId')) {
              onYnabGridyBodyChanged();
            }

            currentlyRunning = false;
          }, 50);
        },

        observe: function invoke(changedNodes) {
          if (changedNodes.has('ynab-grid-body') && !currentlyRunning) {
            ynabToolKit.runningBalance.invoke();
          }

          if (changedNodes.has('ynab-grid-cell ynab-grid-cell-accountName user-data') &&
              changedNodes.has('ynab-grid-cell ynab-grid-cell-date user-data')
          ) {
            addDeadColumnToAddRows();
          }
        }
      };
    }());

    ynabToolKit.runningBalance.invoke();
  } else {
    setTimeout(poll, 250);
  }
}());
<commit_msg>no time needed for run.next<commit_after>'use strict';

(function poll() {
  if (typeof ynabToolKit !== 'undefined' && ynabToolKit.pageReady === true) {
    ynabToolKit.runningBalance = (function () {
      var currentlyRunning = false;

      function updateRunningBalanceCalculation(arrangedContent) {
        var i;
        var transaction;
        var runningBalance = 0;

        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');
        if (accountController.get('sortAscending')) {
          for (i = 0; i < arrangedContent.length; i++) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        } else {
          for (i = arrangedContent.length - 1; i >= 0; i--) {
            transaction = arrangedContent[i];

            if (transaction.get('parentEntityId') !== null) {
              transaction.__ynabToolKitRunningBalance = runningBalance;
              continue;
            }

            if (transaction.get('inflow')) {
              runningBalance += transaction.get('inflow');
            } else if (transaction.get('outflow')) {
              runningBalance -= transaction.get('outflow');
            }

            transaction.__ynabToolKitRunningBalance = runningBalance;
          }
        }
      }

      function updateRunningBalanceColumn() {
        insertHeader();

        $('.ynab-grid-container .ynab-grid-body .ynab-grid-body-row').each(function (index, element) {
          var $element = $(element);
          if ($element.hasClass('ynab-grid-body-empty')) return;

          insertValue(element);
        });
      }

      function insertHeader() {
        if ($('.ynab-grid-header .ynab-toolkit-grid-cell-running-balance').length) return;

        var $headerRow = $('.ynab-grid-header');
        var runningBalanceHeader = $('.ynab-grid-cell-inflow', $headerRow).clone();
        runningBalanceHeader.removeClass('ynab-grid-cell-inflow');
        runningBalanceHeader.addClass('ynab-toolkit-grid-cell-running-balance');
        runningBalanceHeader.text('RUNNING BALANCE');
        runningBalanceHeader.insertAfter($('.ynab-grid-cell-inflow', $headerRow));

        if ($('.ynab-grid-body .ynab-grid-body-row-top .ynab-toolkit-grid-cell-running-balance').length) return;
        var $topRow = $('.ynab-grid-body-row-top');
        var topRowRunningBalance = $('.ynab-grid-cell-inflow', $topRow).clone();
        topRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        topRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');
        topRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $topRow));
      }

      function insertValue(element) {
        var $currentRow = $(element);
        var currentRowRunningBalance = $('.ynab-grid-cell-inflow', $currentRow).clone();
        currentRowRunningBalance.removeClass('ynab-grid-cell-inflow');
        currentRowRunningBalance.addClass('ynab-toolkit-grid-cell-running-balance');

        var emberView = ynabToolKit.shared.getEmberView($currentRow.attr('id'));
        var transaction = emberView.get('content');
        var runningBalance = transaction.get('__ynabToolKitRunningBalance');
        var currencySpan = $('.user-data', currentRowRunningBalance);

        if (runningBalance < 0) {
          currencySpan.addClass('user-data currency negative');
        } else if (runningBalance > 0) {
          currencySpan.addClass('user-data currency positive');
        } else {
          currencySpan.addClass('user-data currency zero');
        }

        if (transaction.get('parentEntityId') !== null) {
          currencySpan.text('');
        } else {
          currencySpan.text(ynabToolKit.shared.formatCurrency(runningBalance));
        }

        currentRowRunningBalance.insertAfter($('.ynab-grid-cell-inflow', $currentRow));
      }

      function onYnabGridyBodyChanged() {
        var accountController = ynabToolKit.shared.containerLookup('controller:accounts');

        if (accountController.get('sortProperties').indexOf('date') !== -1) {
          updateRunningBalanceCalculation(accountController.get('arrangedContent'));
          updateRunningBalanceColumn();
        } else {
          $('.ynab-toolkit-grid-cell-running-balance').remove();
        }
      }

      function addDeadColumnToAddRows() {
        var $ynabGridAddRows = $('.ynab-grid-add-rows');

        if ($ynabGridAddRows.children().length) {
          if ($('.ynab-toolkit-grid-cell-running-balance', $ynabGridAddRows).length) return;
          $('
').insertAfter($('.ynab-grid-cell-inflow', $ynabGridAddRows));
        }
      }

      return {
        invoke: function () {
          currentlyRunning = true;

          Ember.run.next(function () {
            var applicationController = ynabToolKit.shared.containerLookup('controller:application');

            if (applicationController.get('currentPath').indexOf('accounts') > -1 && applicationController.get('selectedAccountId')) {
              onYnabGridyBodyChanged();
            }

            currentlyRunning = false;
          });
        },

        observe: function invoke(changedNodes) {
          if (changedNodes.has('ynab-grid-body') && !currentlyRunning) {
            ynabToolKit.runningBalance.invoke();
          }

          if (changedNodes.has('ynab-grid-cell ynab-grid-cell-accountName user-data') &&
              changedNodes.has('ynab-grid-cell ynab-grid-cell-date user-data')
          ) {
            addDeadColumnToAddRows();
          }
        }
      };
    }());

    ynabToolKit.runningBalance.invoke();
  } else {
    setTimeout(poll, 250);
  }
}());
<|endoftext|>


<commit_before>'use strict';

const { getGroupedFields } = require('@terascope/data-types');
const { FieldType } = require('@terascope/types');
const { Suite } = require('./helpers');
const { config, data } = require('./fixtures/data.json');
const { Column } = require('./src');

const run = async () => {
    const suite = Suite('Build Column');

    for (const name of Object.keys(getGroupedFields(config.fields))) {
        const fieldConfig = config.fields[name];
        // FIXME this is temporary
        if (fieldConfig.type !== FieldType.Object) continue;

        const values = data.map((row) => row[name]);
        const fieldInfo = `${name} (${fieldConfig.type}${fieldConfig.array ? '[]' : ''})`;
        suite.add(fieldInfo, {
            fn() {
                Column.fromJSON(name, fieldConfig, values);
            }
        });
    }

    return suite.run({
        async: true,
        initCount: 2,
        minSamples: 2,
        maxTime: 20,
    });
};
if (require.main === module) {
    run().then((suite) => {
        suite.on('complete', () => {});
    });
} else {
    module.exports = run;
}
<commit_msg>Remove temporary code from build-column-suite<commit_after>'use strict';

const { getGroupedFields } = require('@terascope/data-types');
const { FieldType } = require('@terascope/types');
const { Suite } = require('./helpers');
const { config, data } = require('./fixtures/data.json');
const { Column } = require('./src');

const run = async () => {
    const suite = Suite('Build Column');

    for (const name of Object.keys(getGroupedFields(config.fields))) {
        const fieldConfig = config.fields[name];
        const values = data.map((row) => row[name]);
        const fieldInfo = `${name} (${fieldConfig.type}${fieldConfig.array ? '[]' : ''})`;
        suite.add(fieldInfo, {
            fn() {
                Column.fromJSON(name, fieldConfig, values);
            }
        });
    }

    return suite.run({
        async: true,
        initCount: 2,
        minSamples: 2,
        maxTime: 20,
    });
};
if (require.main === module) {
    run().then((suite) => {
        suite.on('complete', () => {});
    });
} else {
    module.exports = run;
}
<|endoftext|>


<commit_before>// Bot config
var config = require('./lua_bot_config');

// Node libraries
var nodelua = require('nodelua');
var fs = require('fs');

// Print method that overrides the default Lua print method
var output;
function pseudoprint() {
	var args = Array.prototype.slice.call(arguments);
	for(var i = 0; i < args.length; i++) {
		output += (args[i] + " ");
	};
	output += "\n"
}

// Table containing all currently running instances
var luaInstances = {};

// Starts a Lua instance
function setupLuaInstance(name) {
	luaInstances[name] = new nodelua.LuaState(name);
	luaInstances[name].registerFunction('print', pseudoprint);
}

// Captures the output after running some Lua code
function runAndCaptureOutput(name, string) {
	try {
		output = "";
		luaInstances[name].doStringSync(string);
		return output;
	} catch(e) {
		return e.message;
	}
}

// Saves the global table of a Lua instance to a file
function saveGlobalTable(name) {
	var gt = luaInstances[name].doFileSync(__dirname + "/encode_global_table.lua");
	luaInstances[name].setGlobal("GLOBAL_TABLE_ENCODER", null);

	var str = "module.exports = " + gt;

	fs.writeFile(__dirname + "/global-tables/" + name + "_global_table.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot global table file saved.");
	});
}

// Saves the config object to the config file
function saveConfig() {
	var str = "module.exports = " + JSON.stringify(config);

	fs.writeFile(__dirname + "/lua_bot_config.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot config file saved.");
	});
}

// Simple table contains element method
function contains(a, obj) {
	var i = a.length;
	while (i--) {
		if (a[i] === obj) {
			return true;
		}
	}
	return false;
}

// Simple append to array method
function appendToArray(array, element) {
	if(!contains(array, element)) {
		array.push(element);
		return true;
	}
	return false;
}

// Simple remove from array method
function removeFromArray(array, element) {
	var index = array.indexOf(element);
	if(index > -1) {
		array.splice(index, 1);
		return true;
	}
	return false;
}

// Handler function
module.exports = function(formData) {
	console.log("Lua bot handling request.");

	if(formData.user_name == "slackbot") {
		return null;
	}

	if(luaInstances[formData.team_domain] == null) {
		setupLuaInstance(formData.team_domain);
	}

	var ret;
	if(formData.keyword == "lua" && !contains(config.user_blacklist, formData.user_name)) {
		var allowed = true;
		var illegalword = "";
		for (var i = 0; i < config.lua_blacklist.length; i++) {
			if(formData.message.indexOf(config.lua_blacklist[i]) > -1) {
				allowed = false;
				illegalword = config.lua_blacklist[i];
				break;
			}
		};

		if(allowed) {
			ret = runAndCaptureOutput(formData.team_domain, formData.message);
		} else {
			ret = "You aren't allowed to use '" + illegalword + "'' in your code!";
		}
	} else if(formData.keyword == "luaadmin") {
		var firstSpace = formData.message.indexOf(" ")
		var firstWord = formData.message.substr(0, firstSpace);
		var secondWord = formData.message.substr(firstSpace + 1);

		if(contains(config.user_admins, formData.user_name) || contains(config.user_limited_admins, formData.user_name)) {
			if(firstWord == "reset") {
				setupLuaInstance(formData.team_domain);
				ret = "Lua instance reset."
			} else if(firstWord == "setlength") {
				config.max_length = Number(secondWord);
				saveConfig();
				ret = "Max length of output set to: " + config.max_length
			} else if(firstWord == "list") {
				// Print contents of config
			}
		}
		if(contains(config.user_admins, formData.user_name)) {
			if(firstWord == "ban") {
				if(appendToArray(config.user_blacklist, secondWord)) {
					ret = secondWord + " was added to ban list."
					saveConfig();
				} else {
					ret = secondWord + " is already on the ban list."
				}
			} else if(firstWord == "unban") {
				if(removeFromArray(config.user_blacklist, secondWord)) {
					ret = secondWord + " was removed from the ban list."
					saveConfig();
				} else {
					ret = secondWord + " is not on the ban list."
				}
			} else if(firstWord == "promote") {
				if(appendToArray(config.user_limited_admins, secondWord)) {
					ret = secondWord + " was promoted to a limited admin."
					saveConfig();
				} else {
					ret = secondWord + " is already a limited admin."
				}
			} else if(firstWord == "demote") {
				if(removeFromArray(config.user_limited_admins, secondWord)) {
					ret = secondWord + " was demoted from a limited admin."
					saveConfig();
				} else {
					ret = secondWord + " wasn't a limited admin to start with."
				}
			} else if(firstWord == "blacklist") {
				if(appendToArray(config.lua_blacklist, secondWord)) {
					ret = secondWord + " was added to the blacklist."
					saveConfig();
				} else {
					ret = secondWord + " is already on the blacklist."
				}
			} else if(firstWord == "whitelist") {
				if(removeFromArray(config.lua_blacklist, secondWord)) {
					ret = secondWord + " was removed from the blacklist."
					saveConfig();
				} else {
					ret = secondWord + " is already on the whitelist."
				}
			}
		}
	}

	return {
		text: ret
	}
}<commit_msg>worked on Lua bot<commit_after>// Bot config
var config = require('./lua_bot_config');

// Node libraries
var nodelua = require('nodelua');
var fs = require('fs');

// Print method that overrides the default Lua print method
var output;
function pseudoprint() {
	var args = Array.prototype.slice.call(arguments);
	for(var i = 0; i < args.length; i++) {
		output += (args[i] + " ");
	};
	output += "\n"
}

// Table containing all currently running instances
var luaInstances = {};

// Starts a Lua instance
function setupLuaInstance(name) {
	luaInstances[name] = new nodelua.LuaState(name);
	luaInstances[name].registerFunction('print', pseudoprint);
}

// Captures the output after running some Lua code
function runAndCaptureOutput(name, string) {
	try {
		output = "";
		luaInstances[name].doStringSync(string);
		if(output.length > config.max_length) {
			output = output.substr(0, config.max_length);
		}
		return output;
	} catch(e) {
		return e.message;
	}
}

// Saves the global table of a Lua instance to a file
function saveGlobalTable(name) {
	var gt = luaInstances[name].doFileSync(__dirname + "/encode_global_table.lua");
	luaInstances[name].setGlobal("GLOBAL_TABLE_ENCODER", null);

	var str = "module.exports = " + gt;

	fs.writeFile(__dirname + "/global-tables/" + name + "_global_table.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot global table file saved.");
	});
}

// Saves the config object to the config file
function saveConfig() {
	var str = "module.exports = " + JSON.stringify(config);

	fs.writeFile(__dirname + "/lua_bot_config.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot config file saved.");
	});
}

// Simple table contains element method
function contains(a, obj) {
	var i = a.length;
	while (i--) {
		if (a[i] === obj) {
			return true;
		}
	}
	return false;
}

// Simple append to array method
function appendToArray(array, element) {
	if(!contains(array, element)) {
		array.push(element);
		return true;
	}
	return false;
}

// Simple remove from array method
function removeFromArray(array, element) {
	var index = array.indexOf(element);
	if(index > -1) {
		array.splice(index, 1);
		return true;
	}
	return false;
}

// Handler function
module.exports = function(formData) {
	console.log("Lua bot handling request.");

	if(formData.user_name == "slackbot") {
		return null;
	}

	if(luaInstances[formData.team_domain] == null) {
		setupLuaInstance(formData.team_domain);
	}

	var ret;
	if(formData.keyword == "lua" && !contains(config.user_blacklist, formData.user_name)) {
		var allowed = true;
		var illegalword = "";
		for (var i = 0; i < config.lua_blacklist.length; i++) {
			if(formData.message.indexOf(config.lua_blacklist[i]) > -1) {
				allowed = false;
				illegalword = config.lua_blacklist[i];
				break;
			}
		};

		if(allowed) {
			ret = runAndCaptureOutput(formData.team_domain, formData.message);
		} else {
			ret = "You aren't allowed to use '" + illegalword + "'' in your code!";
		}
	} else if(formData.keyword == "luaadmin") {
		var firstSpace = formData.message.indexOf(" ")
		var firstWord = formData.message.substr(0, firstSpace);
		var secondWord = formData.message.substr(firstSpace + 1);

		if(contains(config.user_admins, formData.user_name) || contains(config.user_limited_admins, formData.user_name)) {
			if(firstWord == "reset") {
				setupLuaInstance(formData.team_domain);
				ret = "Lua instance reset."
			} else if(firstWord == "setlength") {
				config.max_length = Number(secondWord);
				saveConfig();
				ret = "Max length of output set to: " + config.max_length
			} else if(firstWord == "list") {
				// Print contents of config
			}
		}
		if(contains(config.user_admins, formData.user_name)) {
			if(firstWord == "ban") {
				if(appendToArray(config.user_blacklist, secondWord)) {
					ret = secondWord + " was added to ban list."
					saveConfig();
				} else {
					ret = secondWord + " is already on the ban list."
				}
			} else if(firstWord == "unban") {
				if(removeFromArray(config.user_blacklist, secondWord)) {
					ret = secondWord + " was removed from the ban list."
					saveConfig();
				} else {
					ret = secondWord + " is not on the ban list."
				}
			} else if(firstWord == "promote") {
				if(appendToArray(config.user_limited_admins, secondWord)) {
					ret = secondWord + " was promoted to a limited admin."
					saveConfig();
				} else {
					ret = secondWord + " is already a limited admin."
				}
			} else if(firstWord == "demote") {
				if(removeFromArray(config.user_limited_admins, secondWord)) {
					ret = secondWord + " was demoted from a limited admin."
					saveConfig();
				} else {
					ret = secondWord + " wasn't a limited admin to start with."
				}
			} else if(firstWord == "blacklist") {
				if(appendToArray(config.lua_blacklist, secondWord)) {
					ret = secondWord + " was added to the blacklist."
					saveConfig();
				} else {
					ret = secondWord + " is already on the blacklist."
				}
			} else if(firstWord == "whitelist") {
				if(removeFromArray(config.lua_blacklist, secondWord)) {
					ret = secondWord + " was removed from the blacklist."
					saveConfig();
				} else {
					ret = secondWord + " is already on the whitelist."
				}
			}
		}
	}

	return {
		text: ret
	}
}<|endoftext|>


<commit_before>import Signal from '../../../lib/src/util/Signal.js';

const CONNECTOR_ADD_SIGNAL = new Signal();

/**
 * Connector element used to link inputs and other connectors together
 * @attribute inputs
 * @attribute outputs
 * @example <graph-connector
 *    inputs="[document.getElementById('input1'), document.getElementById('input2')]"
 *    outputs="[document.getElementById('output1'), document.getElementById('output2')]"
 * ></graph-connector>
 */
class InputConnectorElement extends HTMLElement {
  /**
   * Observed Attributes
   * @private
   * @constant {Array.<String>}
   */
  static get observedAttributes() {
    return ['input', 'output', 'connected'];
  }

  /**
   * Undefined type flag
   * @readonly
   * @constant {number}
   * @default 0
   */
  static get TYPE_UNDEFINED() {
    return 0;
  }

  /**
   * Input type flag
   * @readonly
   * @constant {number}
   * @default 1
   */
  static get TYPE_INPUT() {
    return 1;
  }

  /**
   * Output type flag
   * @readonly
   * @constant {number}
   * @default 2
   */
  static get TYPE_OUTPUT() {
    return 2;
  }

  /**
   * Both type flag
   * @readonly
   * @constant {number}
   * @default 3
   */
  static get TYPE_BOTH() {
    return 3;
  }

  /**
   * @abstract
   */
  constructor() {
    super();

    this.attachShadow({ mode: 'open' }).innerHTML = `
      <slot>
        <style>
          :host {
            display: inline-block;
            position: relative;
            width: 1em;
            height: 1em;
            margin: 3px;
          }

          :host(:not([connected])) .inside {
            visibility: hidden;
          }

          .outside {
            stroke: currentColor;
            fill: none;
            vector-effect: non-scaling-stroke;
          }

          svg {
            width: 100%;
            height: 100%;
          }
        </style>
        <svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
          <circle cx="2.5" cy="2.5" r="1" class="inside"/>
          <circle cx="2.5" cy="2.5" r="2" stroke-dasharray="1 1" class="outside"/>
        </svg>
      </slot>
    `;

    this._onInputChangeBinded = this._onInputChange.bind(this);
    this._checkConnectionBinded = this._checkConnection.bind(this);

    this._inputElementInputs = new Set();
    this._connectorElementInputs = new Set();

    this._inputElementOutputs = new Set();
    this._connectorElementOutputs = new Set();

    const self = this;

    this._inputs = new class extends Set {
      add(value) {
        if (this.has(value) || self === value) {
          return this;
        }
        super.add(value);
        if (value.value !== undefined) {
          self._value = value.value;
        }
        if (value instanceof InputConnectorElement) {
          self._connectorElementInputs.add(value);
          value.outputs.add(self);
        } else {
          self._inputElementInputs.add(value);
        }
        value.addEventListener('input', self._onInputChangeBinded);
        self._updateConnectedStatus();
        if (!(value instanceof InputConnectorElement)) {
          self.dispatchEvent(new InputEvent('input'));
        }
        return this;
      }
      delete(value) {
        const returnValue = super.delete(value);
        if (!returnValue) {
          return;
        }
        value.removeEventListener('input', self._onInputChangeBinded);
        if (value instanceof InputConnectorElement) {
          self._connectorElementInputs.delete(value);
          value.outputs.delete(self);
        } else {
          self._inputElementInputs.delete(value);
        }
        self._updateConnectedStatus();
        return returnValue;
      }
      clear() {
        for (const value of this) {
          this.delete(value);
        }
      }
    };

    this._outputs = new class extends Set {
      add(value) {
        if (this.has(value) || self === value) {
          return this;
        }
        super.add(value);
        if (value instanceof InputConnectorElement) {
          if (self.value !== undefined) {
            value._value = self.value;
          }
          self._connectorElementOutputs.add(value);
          value.inputs.add(self);
          self.dispatchEvent(new CustomEvent('connected', {
            bubbles: true,
            composed: true,
            detail: {
              output: value,
            },
          }));
        } else {
          if (self.value !== undefined) {
            value.value = self.value;
          }
          self._inputElementOutputs.add(value);
        }
        self._updateConnectedStatus();
        if (value instanceof InputConnectorElement) {
          self.dispatchEvent(new InputEvent('input'));
        }
        return this;
      }
      delete(value) {
        const returnValue = super.delete(value);
        if (!returnValue) {
          return;
        }
        self._connectorElementOutputs.delete(value);
        self._inputElementOutputs.delete(value);
        if (value instanceof InputConnectorElement) {
          value.inputs.delete(self);
        }
        self._updateConnectedStatus();
        if (value instanceof InputConnectorElement) {
          self.dispatchEvent(new CustomEvent('disconnected', {
            bubbles: true,
            composed: true,
            detail: {
              output: value,
            },
          }));
        }
        return returnValue;
      }
      clear() {
        for (const value of this) {
          this.delete(value);
        }
      }
    };
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (oldValue === newValue) {
      return;
    }

    switch (name) {
      case 'input':
        const inputIds = newValue.split(' ');
        for (const inputId of inputIds) {
          const input = this.getRootNode().querySelector(`#${inputId}`);
          requestAnimationFrame(() => {
            if (input instanceof InputConnectorElement) {
              return;
            }
            if (input) {
              this.inputs.add(input);
            }
          });
        }
        break;
      case 'output':
        const outputIds = newValue.split(' ');
        for (const outputId of outputIds) {
          const output = this.getRootNode().querySelector(`#${outputId}`);
          if (output) {
            requestAnimationFrame(() => {
              this.outputs.add(output);
            });
          }
        }
        break;
      case 'connected':
        if (!this.connected) {
          this._connectorElementInputs.clear();
          this._connectorElementOutputs.clear();
        }
        break;
    }
  }

  _checkConnection(connector) {
    if (!this.getAttribute('output')) {
      return;
    }
    if (this.getAttribute('output').split(' ').includes(connector.id)) {
      this.outputs.add(connector);
    }
  }

  connectedCallback() {
    CONNECTOR_ADD_SIGNAL.dispatch(this);
    CONNECTOR_ADD_SIGNAL.add(this._checkConnectionBinded);
  }

  disconnectedCallback() {
    CONNECTOR_ADD_SIGNAL.delete(this._checkConnectionBinded);
    if (this.type & InputConnectorElement.TYPE_INPUT) {
      this.inputs.clear();
    }
    if (this.type & InputConnectorElement.TYPE_OUTPUT) {
      this.outputs.clear();
    }
  }

  set connected(value) {
    if (value) {
      this.setAttribute('connected', '');
    } else {
      this.removeAttribute('connected');
    }
  }

  get connected() {
    return this.hasAttribute('connected');
  }

  _onInputChange(event) {
    this._value = event.target.value;
    for (const output of this.outputs) {
      if (!(output instanceof InputConnectorElement)) {
        output.value = this._value;
      }
    }
    this.dispatchEvent(new InputEvent('input'));
  }

  _updateConnectedStatus() {
    this.connected = !!this._connectorElementInputs.size || !!this._connectorElementOutputs.size;
    for (const output of this._inputElementOutputs) {
      output.disabled = this.connected;
    }
  }

  /**
   * Get current value stored in connector
   * @readonly
   */
  get value() {
    return this._value;
  }

  /**
   * Set of inputs
   * @readonly
   * @type {Set.<HTMLInputElement|InputConnectorElement>}
   */
  get inputs() {
    return this._inputs;
  }

  /**
   * Set of outputs
   * @readonly
   * @type {Set.<(HTMLInputElement|InputConnectorElement)>}
   */
  get outputs() {
    return this._outputs;
  }

  /**
   * Type of connector as flag
   * @readonly
   * @type {number}
   */
  get type() {
    return +!!this._inputElementInputs.size << 1 | +!!this._inputElementOutputs.size;
  }
}

export default InputConnectorElement;
<commit_msg>add more parameters to connector<commit_after>import Signal from '../../../lib/src/util/Signal.js';

const CONNECTOR_ADD_SIGNAL = new Signal();

/**
 * Connector element used to link inputs and other connectors together
 * @attribute inputs
 * @attribute outputs
 * @example <graph-connector
 *    inputs="[document.getElementById('input1'), document.getElementById('input2')]"
 *    outputs="[document.getElementById('output1'), document.getElementById('output2')]"
 * ></graph-connector>
 */
class InputConnectorElement extends HTMLElement {
  /**
   * Observed Attributes
   * @private
   * @constant {Array.<String>}
   */
  static get observedAttributes() {
    return ['input', 'output', 'connected'];
  }

  /**
   * Undefined type flag
   * @readonly
   * @constant {number}
   * @default 0
   */
  static get TYPE_UNDEFINED() {
    return 0;
  }

  /**
   * Input type flag
   * @readonly
   * @constant {number}
   * @default 1
   */
  static get TYPE_INPUT() {
    return 1;
  }

  /**
   * Output type flag
   * @readonly
   * @constant {number}
   * @default 2
   */
  static get TYPE_OUTPUT() {
    return 2;
  }

  /**
   * Both type flag
   * @readonly
   * @constant {number}
   * @default 3
   */
  static get TYPE_BOTH() {
    return 3;
  }

  /**
   * @abstract
   */
  constructor() {
    super();

    this.attachShadow({ mode: 'open' }).innerHTML = `
      <slot>
        <style>
          :host {
            display: inline-block;
            position: relative;
            width: 1em;
            height: 1em;
            margin: 3px;
          }

          :host(:not([connected])) .inside {
            visibility: hidden;
          }

          .outside {
            stroke: currentColor;
            fill: none;
            vector-effect: non-scaling-stroke;
          }

          svg {
            width: 100%;
            height: 100%;
          }
        </style>
        <svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
          <circle cx="2.5" cy="2.5" r="1" class="inside"/>
          <circle cx="2.5" cy="2.5" r="2" stroke-dasharray="1 1" class="outside"/>
        </svg>
      </slot>
    `;

    this._onInputChangeBinded = this._onInputChange.bind(this);
    this._checkConnectionBinded = this._checkConnection.bind(this);

    this._inputElementInputs = new Set();
    this._connectorElementInputs = new Set();

    this._inputElementOutputs = new Set();
    this._connectorElementOutputs = new Set();

    const self = this;

    this._inputs = new class extends Set {
      add(value) {
        if (this.has(value) || self === value) {
          return this;
        }
        super.add(value);
        if (value.value !== undefined) {
          self._value = value.value;
        }
        if (value instanceof InputConnectorElement) {
          self._connectorElementInputs.add(value);
          value.outputs.add(self);
        } else {
          self._inputElementInputs.add(value);
        }
        value.addEventListener('input', self._onInputChangeBinded);
        self._updateConnectedStatus();
        if (!(value instanceof InputConnectorElement)) {
          self.dispatchEvent(new InputEvent('input'));
        }
        return this;
      }
      delete(value) {
        const returnValue = super.delete(value);
        if (!returnValue) {
          return;
        }
        value.removeEventListener('input', self._onInputChangeBinded);
        if (value instanceof InputConnectorElement) {
          self._connectorElementInputs.delete(value);
          value.outputs.delete(self);
        } else {
          self._inputElementInputs.delete(value);
        }
        self._updateConnectedStatus();
        return returnValue;
      }
      clear() {
        for (const value of this) {
          this.delete(value);
        }
      }
    };

    this._outputs = new class extends Set {
      add(value) {
        if (this.has(value) || self === value) {
          return this;
        }
        super.add(value);
        if (value instanceof InputConnectorElement) {
          if (self.value !== undefined) {
            value._value = self.value;
          }
          self._connectorElementOutputs.add(value);
          value.inputs.add(self);
          self.dispatchEvent(new CustomEvent('connected', {
            bubbles: true,
            composed: true,
            detail: {
              input: self,
              output: value,
            },
          }));
        } else {
          if (self.value !== undefined) {
            value.value = self.value;
          }
          self._inputElementOutputs.add(value);
        }
        self._updateConnectedStatus();
        if (value instanceof InputConnectorElement) {
          self.dispatchEvent(new InputEvent('input'));
        }
        return this;
      }
      delete(value) {
        const returnValue = super.delete(value);
        if (!returnValue) {
          return;
        }
        self._connectorElementOutputs.delete(value);
        self._inputElementOutputs.delete(value);
        if (value instanceof InputConnectorElement) {
          value.inputs.delete(self);
        }
        self._updateConnectedStatus();
        if (value instanceof InputConnectorElement) {
          self.dispatchEvent(new CustomEvent('disconnected', {
            bubbles: true,
            composed: true,
            detail: {
              input: self,
              output: value,
            },
          }));
        }
        return returnValue;
      }
      clear() {
        for (const value of this) {
          this.delete(value);
        }
      }
    };
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (oldValue === newValue) {
      return;
    }

    switch (name) {
      case 'input':
        const inputIds = newValue.split(' ');
        for (const inputId of inputIds) {
          const input = this.getRootNode().querySelector(`#${inputId}`);
          requestAnimationFrame(() => {
            if (input instanceof InputConnectorElement) {
              return;
            }
            if (input) {
              this.inputs.add(input);
            }
          });
        }
        break;
      case 'output':
        const outputIds = newValue.split(' ');
        for (const outputId of outputIds) {
          const output = this.getRootNode().querySelector(`#${outputId}`);
          if (output) {
            requestAnimationFrame(() => {
              this.outputs.add(output);
            });
          }
        }
        break;
      case 'connected':
        if (!this.connected) {
          this._connectorElementInputs.clear();
          this._connectorElementOutputs.clear();
        }
        break;
    }
  }

  _checkConnection(connector) {
    if (!this.getAttribute('output')) {
      return;
    }
    if (this.getAttribute('output').split(' ').includes(connector.id)) {
      this.outputs.add(connector);
    }
  }

  connectedCallback() {
    CONNECTOR_ADD_SIGNAL.dispatch(this);
    CONNECTOR_ADD_SIGNAL.add(this._checkConnectionBinded);
  }

  disconnectedCallback() {
    CONNECTOR_ADD_SIGNAL.delete(this._checkConnectionBinded);
    if (this.type & InputConnectorElement.TYPE_INPUT) {
      this.inputs.clear();
    }
    if (this.type & InputConnectorElement.TYPE_OUTPUT) {
      this.outputs.clear();
    }
  }

  set connected(value) {
    if (value) {
      this.setAttribute('connected', '');
    } else {
      this.removeAttribute('connected');
    }
  }

  get connected() {
    return this.hasAttribute('connected');
  }

  _onInputChange(event) {
    this._value = event.target.value;
    for (const output of this.outputs) {
      if (!(output instanceof InputConnectorElement)) {
        output.value = this._value;
      }
    }
    this.dispatchEvent(new InputEvent('input'));
  }

  _updateConnectedStatus() {
    this.connected = !!this._connectorElementInputs.size || !!this._connectorElementOutputs.size;
    for (const output of this._inputElementOutputs) {
      output.disabled = this.connected;
    }
  }

  /**
   * Get current value stored in connector
   * @readonly
   */
  get value() {
    return this._value;
  }

  /**
   * Set of inputs
   * @readonly
   * @type {Set.<HTMLInputElement|InputConnectorElement>}
   */
  get inputs() {
    return this._inputs;
  }

  /**
   * Set of outputs
   * @readonly
   * @type {Set.<(HTMLInputElement|InputConnectorElement)>}
   */
  get outputs() {
    return this._outputs;
  }

  /**
   * Set of input elements (non connector) only
   * @readonly
   * @type {Set.<HTMLInputElement>}
   */
  get inputElements() {
    return this._inputElementInputs;
  }

  /**
   * Set of output elements (non connector) only
   * @readonly
   * @type {Set.<(HTMLInputElement)>}
   */
  get outputElements() {
    return this._inputElementOutputs;
  }

  /**
   * Type of connector as flag
   * @readonly
   * @type {number}
   */
  get type() {
    return +!!this._inputElementInputs.size << 1 | +!!this._inputElementOutputs.size;
  }
}

export default InputConnectorElement;
<|endoftext|>


<commit_before>/* Copyright 2015 Bloomberg Finance L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define(["widgets/js/manager", "d3", "./utils", "./ColorUtils", "./Axis"], function(WidgetManager, d3, utils, Col_Pic, Axis) {
    var AxisView = Axis[0];
    var ColorBar = AxisView.extend({
        render: function() {
            this.parent = this.options.parent;
            this.enable_highlight = this.options.enable_highlight;
            this.margin = this.parent.margin;
            this.vertical = this.model.get("orientation") === "vertical";
            this.height = this.parent.height - (this.margin.top + this.margin.bottom);
            this.width = this.parent.width - (this.margin.left + this.margin.right);

            var scale_promise = this.set_scale(this.model.get("scale"));
            this.model.on("change:scale", function(model, value) {
                this.update_scale(model.previous("scale"), value);
                // TODO: rescale_axis does too many things. Decompose
                this.axis.scale(this.axis_scale.scale); // TODO: this is in redraw_axisline
                this.rescale_axis();
            }, this);

            this.side = this.model.get("side");
            this.x_offset = 100;
            this.y_offset = 40;
            this.bar_height = 20;
            // Formatting data particular to the axis
            this.el = d3.select(document.createElementNS(d3.ns.prefix.svg, "g"))
                .attr("class", "ColorBar")
                .attr("display", (this.model.get("visible") ? "inline" : "none"))
                .style("transform", this.get_topg_transform());

            this.label = this.model.get("label");
            this.ordinal = false;
            this.num_ticks = this.model.get("num_ticks");
            this.tick_values = this.model.get_typed_field("tick_values");

            var that = this;
            scale_promise.then(function() {
                that.tick_format = that.generate_tick_formatter();
                that.set_scales_range();
                that.append_axis();
                that.parent.on("margin_updated", that.parent_margin_updated, that);

                that.model.on("change:tick_format", that.tickformat_changed, that);
                that.model.on("change:visible", that.update_visibility, that);
                that.model.on("change:label", that.update_label, that);
                that.model.on_some_change(["side", "orientation"], function() {
                    this.vertical = this.model.get("orientation") === "vertical";
                    this.side = this.model.get("side");
                    this.rescale_axis();
                    this.redraw_axis();
                }, that);
            });
        },
        set_scale: function(model) {
            // Sets the child scale
            var that = this;
            if (this.axis_scale) { this.axis_scale.remove(); }
            return this.create_child_view(model).then(function(view) {
                // Trigger the displayed event of the child view.
                that.after_displayed(function() {
                    view.trigger("displayed");
                }, that);
                that.axis_scale = view;
                that.axis_scale.on("domain_changed", that.redraw_axisline, that);
                that.axis_scale.on("color_scale_range_changed", that.redraw_axis, that);
                that.axis_scale.on("highlight_axis", that.highlight, that);
                that.axis_scale.on("unhighlight_axis", that.unhighlight, that);

                // TODO: eventually removes what follows
                if(that.axis_scale.model.type === "date_color_linear") {
                    that.axis_line_scale = d3.time.scale().nice();
                } else if(that.axis_scale.model.type === "ordinal") {
                    that.axis_line_scale = d3.scale.ordinal();
                    that.ordinal = true;
                } else {
                    that.axis_line_scale = d3.scale.linear();
                }
            });
        },
        append_axis: function() {
            // The label is allocated a space of 100px. If the label
            // occupies more than 100px then you are out of luck.
            var that = this;
            if(this.label) {
                this.el.append("g")
                    .attr("transform", this.get_label_transform())
                    .attr("class", "axis label_g")
                    .append("text")
                    .append("tspan")
                    .attr("id", "text_elem")
                    .attr("dy", "0.5ex")
                    .text(this.model.get("label"))
                    .attr("class", "axislabel")
                    .style("text-anchor", this.vertical ? "middle" : "end");
            }

            var colorBar = this.el.append("g")
                .attr("id","colorBarG" + this.cid);

            this.draw_color_bar();
            this.axis_line_scale.domain(this.axis_scale.scale.domain());

            this.g_axisline = colorBar.append("g")
                .attr("class", "axis");

            this.axis = d3.svg.axis()
                .tickFormat(this.tick_format);
            this.redraw_axisline();
        },
        draw_color_bar: function() {
            var colorBar = this.el.select("#colorBarG" + this.cid);
            colorBar.attr("transform", this.get_colorbar_transform());

            var that = this;
            colorBar.selectAll(".g-rect")
                .remove();
            colorBar.selectAll(".g-defs")
                .remove();

            this.colors = this.axis_scale.scale.range();
            var colorSpacing = 100 / (this.colors.length - 1);

            if(this.ordinal) {
                var bar_width = this.get_color_bar_width() / this.colors.length;
                var rects = colorBar.append("g")
                    .attr("class", "g-rect axis")
                    .style("transform", (this.vertical) ? "rotate(-90deg)" : "")
                    .selectAll("rect")
                    .data(this.colors);

                rects.enter()
                    .append("rect")
                    .attr("y", 0)
                    .attr("height", this.bar_height)
                    .attr("width", bar_width)
                    .style("fill", function(d) { return d; });

                if(this.vertical) {
                    rects.attr("x", function(d, i) {
                        return i * bar_width - (that.height - 2 * that.x_offset);
                    });
                } else {
                    rects.attr("x", function(d, i) {
                        return i * bar_width;
                    });
                }
            }
            else {
                colorBar.append("g")
                    .attr("class", "g-defs")
                    .append("defs")
                    .append("linearGradient")
                    .attr({
                        id : "colorBarGradient" + this.cid,
                        x1 : "0%",
                        y1 : "0%",
                        x2 : "100%",
                        y2 : "0%"
                    })
                    .selectAll("stop")
                    .data(this.colors)
                    .enter()
                    .append("stop")
                    .attr({
                        "offset": function(d,i) {
                            return colorSpacing * (i) + "%";
                        },
                        "stop-color": function(d,i) { return that.colors[i]; },
                        "stop-opacity": 1
                    });

                colorBar.append("g")
                    .attr("class", "g-rect axis")
                    .style("transform", (this.vertical) ? "rotate(-90deg)" : "")
                    .append("rect")
                    .attr({
                        "width": this.get_color_bar_width(),
                        "height": this.bar_height,
                        x: (this.vertical) ? -(this.height - 2 * this.x_offset) : 0,
                        y: 0,
                        "stroke-width": 1
                    })
                    .style("fill","url(#colorBarGradient" + this.cid + ")");
            }
        },
        get_topg_transform: function() {
            if(this.vertical){
                if(this.side === "right") {
                    return "translate(" + this.get_basic_transform() + "px, 0px)"
                        + " translate(" + (this.margin.right/2) + "px, 0px)"
                        + " translate(" + (-this.bar_height) + "px, 0px)";
                }
                    return "translate(" + this.get_basic_transform() + "px, 0px)"
                        + " translate(" + -(this.margin.left/2) + "px, 0px)"
                        + " translate(" + (this.bar_height) + "px, 0px)";
            } else {
                if(this.side === "top") {
                    return "translate(0px, " + this.get_basic_transform() + "px)"
                        + "translate(0px, " + -(this.margin.top) + "px)"
                        + " translate(0px, " + (this.bar_height) + "px)"
                        + " translate(0px, 2em)";
                }
                return "translate(0px, " + this.get_basic_transform() + "px)"
                     + "translate(0px, " + this.margin.bottom + "px)"
                     + " translate(0px, " + (-this.bar_height) + "px)"
                     + " translate(0px, -2em)";
            }
        },
        get_label_transform: function() {
            if(this.vertical) {
                return "translate(" + ((this.side === "right") ?
                    (this.bar_height / 2) : (-this.bar_height / 2)) + ", " + (this.x_offset - 15) + ")";
            }
            return "translate(" + (this.x_offset - 5) + ", " + (this.bar_height / 2)+ ")";
        },
        get_axisline_transform: function() {
            if(this.vertical) {
                return "translate(" + ((this.side === "right") ?
                    this.bar_height : 0) + ", 0)";
            }
            return "translate(0, " + ((this.side === "top") ?
                    0 : this.bar_height) + ")";
        },
        get_colorbar_transform: function() {
            if(this.vertical) {
                return "translate(0, " + (this.x_offset) + ")";
            }
            return "translate(" + this.x_offset + ", 0)";
        },
        set_scales_range: function() {
            this.axis_scale.set_range();
            var range = (this.vertical) ?
                [this.height - 2 * this.x_offset, 0] : [0, this.width -  2 * this.x_offset];
            if(this.ordinal) {
                this.axis_line_scale.rangeRoundBands(range, 0.05);
            } else {
                if(this.axis_scale.divergent) {
                    this.axis_line_scale.range([range[0], (range[0] + range[1]) * 0.5, range[1]]);
                } else {
                    this.axis_line_scale.range(range);
                }
            }
        },
        get_color_bar_width: function() {
            return (this.vertical) ? (this.height - (2 * this.x_offset)) : (this.width - 2 * this.x_offset);
        },
        update_label: function(model, value) {
            this.label = value;
            this.el.select("#text_elem")
                .text(this.label);
        },
        rescale_axis: function() {
            // rescale the axis
            this.height = this.parent.height - (this.margin.top + this.margin.bottom);
            this.width = this.parent.width - (this.margin.left + this.margin.right);
            this.set_scales_range();
            // shifting the entire g of the color bar first.
            this.el.style("transform", this.get_topg_transform());
            var self = this;
            var bar_width = this.get_color_bar_width() / this.colors.length;
            if(this.ordinal) {
                var rectangles = this.el.select("#colorBarG" + this.cid)
                    .select(".g-rect")
                    .selectAll("rect")
                    .attr("width", bar_width);
                if(this.vertical) {
                    rectangles.attr("x", function(d, i) {
                        return (i * bar_width) - (self.height - 2 * self.x_offset);
                    });
                } else {
                    rectangles.attr("x", function(d, i) {
                        return i * bar_width;
                    });
                }
            } else {
                this.el.select("#colorBarG" + this.cid)
                    .select(".g-rect")
                    .selectAll("rect")
                    .attr("width", this.get_color_bar_width())
                    .attr("x", (this.vertical) ? -(this.height - 2 * this.x_offset) : 0);
            }
            if(this.label) {
                this.el.select(".label_g")
                    .attr("transform", this.get_label_transform())
                    .select("#text_elem")
                    .text(this.model.get("label"))
                    .style("text-anchor", this.vertical ? "middle" : "end");
            }
            this.g_axisline.call(this.axis);
        },
        redraw_axisline: function() {
            if (this.axis) {
                this.axis_line_scale.domain(this.axis_scale.scale.domain());
                // We need to set the range here again. Only because, if the
                // domain has changed from a two element array to a three
                // element one, the range of the axis has to be changed
                // accordingly.

                // this.set_scales_range();
                this.axis.orient(this.side)
                    .scale(this.axis_line_scale);
                this.set_tick_values();
                this.g_axisline
                    .attr("transform", this.get_axisline_transform())
                    .call(this.axis);
            }
        },
        redraw_axis: function() {
            this.draw_color_bar();
            this.redraw_axisline();
        },
    });
    WidgetManager.WidgetManager.register_widget_view("bqplot.ColorAxis", ColorBar);
});
<commit_msg>color axis bug fix<commit_after>/* Copyright 2015 Bloomberg Finance L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define(["widgets/js/manager", "d3", "./utils", "./ColorUtils", "./Axis"], function(WidgetManager, d3, utils, Col_Pic, Axis) {
    var AxisView = Axis[0];
    var ColorBar = AxisView.extend({
        render: function() {
            this.parent = this.options.parent;
            this.enable_highlight = this.options.enable_highlight;
            this.margin = this.parent.margin;
            this.vertical = this.model.get("orientation") === "vertical";
            this.height = this.parent.height - (this.margin.top + this.margin.bottom);
            this.width = this.parent.width - (this.margin.left + this.margin.right);

            var scale_promise = this.set_scale(this.model.get("scale"));
            this.model.on("change:scale", function(model, value) {
                this.update_scale(model.previous("scale"), value);
                // TODO: rescale_axis does too many things. Decompose
                this.axis.scale(this.axis_scale.scale); // TODO: this is in redraw_axisline
                this.rescale_axis();
            }, this);

            this.side = this.model.get("side");
            this.x_offset = 100;
            this.y_offset = 40;
            this.bar_height = 20;
            // Formatting data particular to the axis
            this.el = d3.select(document.createElementNS(d3.ns.prefix.svg, "g"))
                .attr("class", "ColorBar")
                .attr("display", (this.model.get("visible") ? "inline" : "none"))
                .style("transform", this.get_topg_transform());

            this.label = this.model.get("label");
            this.ordinal = false;
            this.num_ticks = this.model.get("num_ticks");
            this.tick_values = this.model.get_typed_field("tick_values");

            var that = this;
            scale_promise.then(function() {
                that.tick_format = that.generate_tick_formatter();
                that.set_scales_range();
                that.append_axis();
                that.parent.on("margin_updated", that.parent_margin_updated, that);

                that.model.on("change:tick_format", that.tickformat_changed, that);
                that.model.on("change:visible", that.update_visibility, that);
                that.model.on("change:label", that.update_label, that);
                that.model.on_some_change(["side", "orientation"], function() {
                    this.vertical = this.model.get("orientation") === "vertical";
                    this.side = this.model.get("side");
                    this.rescale_axis();
                    this.redraw_axis();
                }, that);
            });
        },
        set_scale: function(model) {
            // Sets the child scale
            var that = this;
            if (this.axis_scale) { this.axis_scale.remove(); }
            return this.create_child_view(model).then(function(view) {
                // Trigger the displayed event of the child view.
                that.after_displayed(function() {
                    view.trigger("displayed");
                }, that);
                that.axis_scale = view;
                that.axis_scale.on("domain_changed", that.redraw_axisline, that);
                that.axis_scale.on("color_scale_range_changed", that.redraw_axis, that);
                that.axis_scale.on("highlight_axis", that.highlight, that);
                that.axis_scale.on("unhighlight_axis", that.unhighlight, that);

                // TODO: eventually removes what follows
                if(that.axis_scale.model.type === "date_color_linear") {
                    that.axis_line_scale = d3.time.scale().nice();
                } else if(that.axis_scale.model.type === "ordinal") {
                    that.axis_line_scale = d3.scale.ordinal();
                    that.ordinal = true;
                } else {
                    that.axis_line_scale = d3.scale.linear();
                }
            });
        },
        append_axis: function() {
            // The label is allocated a space of 100px. If the label
            // occupies more than 100px then you are out of luck.
            var that = this;
            if(this.label) {
                this.el.append("g")
                    .attr("transform", this.get_label_transform())
                    .attr("class", "axis label_g")
                    .append("text")
                    .append("tspan")
                    .attr("id", "text_elem")
                    .attr("dy", "0.5ex")
                    .text(this.model.get("label"))
                    .attr("class", "axislabel")
                    .style("text-anchor", this.vertical ? "middle" : "end");
            }

            var colorBar = this.el.append("g")
                .attr("id","colorBarG" + this.cid);

            this.draw_color_bar();
            this.axis_line_scale.domain(this.axis_scale.scale.domain());

            this.g_axisline = colorBar.append("g")
                .attr("class", "axis");

            this.axis = d3.svg.axis()
                .tickFormat(this.tick_format);
            this.redraw_axisline();
        },
        draw_color_bar: function() {
            var colorBar = this.el.select("#colorBarG" + this.cid);
            colorBar.attr("transform", this.get_colorbar_transform());

            var that = this;
            colorBar.selectAll(".g-rect")
                .remove();
            colorBar.selectAll(".g-defs")
                .remove();

            this.colors = this.axis_scale.scale.range();
            var colorSpacing = 100 / (this.colors.length - 1);

            if(this.ordinal) {
                var bar_width = this.get_color_bar_width() / this.colors.length;
                var rects = colorBar.append("g")
                    .attr("class", "g-rect axis")
                    .style("transform", (this.vertical) ? "rotate(-90deg)" : "")
                    .selectAll("rect")
                    .data(this.colors);

                rects.enter()
                    .append("rect")
                    .attr("y", 0)
                    .attr("height", this.bar_height)
                    .attr("width", bar_width)
                    .style("fill", function(d) { return d; });

                if(this.vertical) {
                    rects.attr("x", function(d, i) {
                        return i * bar_width - (that.height - 2 * that.x_offset);
                    });
                } else {
                    rects.attr("x", function(d, i) {
                        return i * bar_width;
                    });
                }
            }
            else {
                colorBar.append("g")
                    .attr("class", "g-defs")
                    .append("defs")
                    .append("linearGradient")
                    .attr({
                        id : "colorBarGradient" + this.cid,
                        x1 : "0%",
                        y1 : "0%",
                        x2 : "100%",
                        y2 : "0%"
                    })
                    .selectAll("stop")
                    .data(this.colors)
                    .enter()
                    .append("stop")
                    .attr({
                        "offset": function(d,i) {
                            return colorSpacing * (i) + "%";
                        },
                        "stop-color": function(d,i) { return that.colors[i]; },
                        "stop-opacity": 1
                    });

                colorBar.append("g")
                    .attr("class", "g-rect axis")
                    .style("transform", (this.vertical) ? "rotate(-90deg)" : "")
                    .append("rect")
                    .attr({
                        "width": this.get_color_bar_width(),
                        "height": this.bar_height,
                        x: (this.vertical) ? -(this.height - 2 * this.x_offset) : 0,
                        y: 0,
                        "stroke-width": 1
                    })
                    .style("fill","url(#colorBarGradient" + this.cid + ")");
            }
        },
        get_topg_transform: function() {
            if(this.vertical){
                if(this.side === "right") {
                    return "translate(" + this.get_basic_transform() + "px, 0px)"
                        + " translate(" + (this.margin.right/2) + "px, 0px)"
                        + " translate(" + (-this.bar_height) + "px, 0px)";
                }
                    return "translate(" + this.get_basic_transform() + "px, 0px)"
                        + " translate(" + -(this.margin.left/2) + "px, 0px)"
                        + " translate(" + (this.bar_height) + "px, 0px)";
            } else {
                if(this.side === "top") {
                    return "translate(0px, " + this.get_basic_transform() + "px)"
                        + "translate(0px, " + -(this.margin.top) + "px)"
                        + " translate(0px, " + (this.bar_height) + "px)"
                        + " translate(0px, 2em)";
                }
                return "translate(0px, " + this.get_basic_transform() + "px)"
                     + "translate(0px, " + this.margin.bottom + "px)"
                     + " translate(0px, " + (-this.bar_height) + "px)"
                     + " translate(0px, -2em)";
            }
        },
        get_label_transform: function() {
            if(this.vertical) {
                return "translate(" + ((this.side === "right") ?
                    (this.bar_height / 2) : (-this.bar_height / 2)) + ", " + (this.x_offset - 15) + ")";
            }
            return "translate(" + (this.x_offset - 5) + ", " + (this.bar_height / 2)+ ")";
        },
        get_axisline_transform: function() {
            if(this.vertical) {
                return "translate(" + ((this.side === "right") ?
                    this.bar_height : 0) + ", 0)";
            }
            return "translate(0, " + ((this.side === "top") ?
                    0 : this.bar_height) + ")";
        },
        get_colorbar_transform: function() {
            if(this.vertical) {
                return "translate(0, " + (this.x_offset) + ")";
            }
            return "translate(" + this.x_offset + ", 0)";
        },
        set_color_scale_range: function() {
            this.axis_scale.set_range();
        },
        set_axisline_scale_range: function() {
            var range = (this.vertical) ?
                [this.height - 2 * this.x_offset, 0] : [0, this.width -  2 * this.x_offset];
            if(this.ordinal) {
                this.axis_line_scale.rangeRoundBands(range, 0.05);
            } else {
                if(this.axis_scale.divergent) {
                    this.axis_line_scale.range([range[0], (range[0] + range[1]) * 0.5, range[1]]);
                } else {
                    this.axis_line_scale.range(range);
                }
            }
        },
        set_scales_range: function() {
            this.set_color_scale_range();
            this.set_axisline_scale_range();
        },
        get_color_bar_width: function() {
            return (this.vertical) ? (this.height - (2 * this.x_offset)) : (this.width - 2 * this.x_offset);
        },
        update_label: function(model, value) {
            this.label = value;
            this.el.select("#text_elem")
                .text(this.label);
        },
        rescale_axis: function() {
            // rescale the axis
            this.height = this.parent.height - (this.margin.top + this.margin.bottom);
            this.width = this.parent.width - (this.margin.left + this.margin.right);
            // this.set_scales_range();
            this.set_axisline_scale_range();
            // shifting the entire g of the color bar first.
            this.el.style("transform", this.get_topg_transform());
            var self = this;
            var bar_width = this.get_color_bar_width() / this.colors.length;
            if(this.ordinal) {
                var rectangles = this.el.select("#colorBarG" + this.cid)
                    .select(".g-rect")
                    .selectAll("rect")
                    .attr("width", bar_width);
                if(this.vertical) {
                    rectangles.attr("x", function(d, i) {
                        return (i * bar_width) - (self.height - 2 * self.x_offset);
                    });
                } else {
                    rectangles.attr("x", function(d, i) {
                        return i * bar_width;
                    });
                }
            } else {
                this.el.select("#colorBarG" + this.cid)
                    .select(".g-rect")
                    .selectAll("rect")
                    .attr("width", this.get_color_bar_width())
                    .attr("x", (this.vertical) ? -(this.height - 2 * this.x_offset) : 0);
            }
            if(this.label) {
                this.el.select(".label_g")
                    .attr("transform", this.get_label_transform())
                    .select("#text_elem")
                    .text(this.model.get("label"))
                    .style("text-anchor", this.vertical ? "middle" : "end");
            }
            this.g_axisline.call(this.axis);
        },
        redraw_axisline: function() {
            if (this.axis) {
                this.axis_line_scale.domain(this.axis_scale.scale.domain());
                // We need to set the range of the axis line scale here again.
                // Only because, if the domain has changed from a two element
                // array to a three element one, the range of the axis has to
                // be changed accordingly.
                this.set_axisline_scale_range();
                this.axis.orient(this.side)
                    .scale(this.axis_line_scale);
                this.set_tick_values();
                this.g_axisline
                    .attr("transform", this.get_axisline_transform())
                    .call(this.axis);
            }
        },
        redraw_axis: function() {
            this.draw_color_bar();
            this.redraw_axisline();
        },
    });
    WidgetManager.WidgetManager.register_widget_view("bqplot.ColorAxis", ColorBar);
});
<|endoftext|>