<commit_before>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { Label } from "../Label";
import { utils } from "../utils";
import CSSModules from "react-css-modules";
import styles from "./TextArea.css";
import blacklist from "blacklist";

export class TextArea extends React.PureComponent {
  constructor(props) {
    super(props);

    // Initial state
    this.state = {
      "value": props.value || "",
      "active": false,
      "status": props.status || null,
      "message": props.message || null
    };
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.value !== this.props.value) {
      this.setState({
        "value": nextProps.value
      });
    }
    if (
      typeof nextProps.status !== "undefined" &&
        nextProps.status !== this.props.status ||
      typeof nextProps.message !== "undefined" &&
        nextProps.message !== this.props.message
    ) {
      this.setState({
        "status": nextProps.status,
        "message": nextProps.message
      });
    }
  }

  _eventHandlers = (e, change) => {
    const data = {
      "value": this.state.value,
      "status": this.state.status,
      "message": this.state.message
    };

    if (!change && this.props.onBlur) {
      this.props.onBlur(e, data);
    }

    if (change && this.props.onChange) {
      this.props.onChange(e, data);
    }
  };

  _validate = (e, inputValue, change) => {
    const validationObject = utils.validate(
      inputValue,
      this.props.valid,
      this.props.status,
      this.props.message,
      this.props.required
    );

    const data = {
      "value": inputValue,
      "status": validationObject.status,
      "message": validationObject.message
    };

    let result = true;
    if (this.props.onBeforeChange) {
      result = this.props.onBeforeChange(e, data);
    }

    if (result === false) {
      return;
    }

    this.setState(
      {
        "status": validationObject.status,
        "message": validationObject.message,
        "value": inputValue,
        "active": change
      },
      () => {
        this._eventHandlers(e, change);
      }
    );
  };

  _handleChange = e => {
    e.persist();
    let value = e.target.value;

    if (this.props.uppercase) {
      value = value.toUpperCase();
    }

    this._validate(e, value, true);
  };

  _handleFocus = e => {
    e.persist();
    this.setState({ "active": true }, function() {
      if (typeof this.props.onFocus === "function") {
        this.props.onFocus(e, {
          "value": this.state.value,
          "status": this.state.status,
          "message": this.state.message
        });
      }
    });
  };

  _handleBlur = e => {
    e.persist();
    const value = e.target.value;
    this._validate(e, value, false);
  };

  render() {
    const {
      className,
      disabled,
      id,
      inline,
      label,
      leftLabel,
      required,
      resizable,
      style,
      tooltip,
      type,
      tooltipPosition,
      ...others
    } = this.props;

    // Declaring the following variables so they don't get passed to the Textarea through the prop spread.
    const othersFiltered = blacklist(others, "onBeforeChange");

    let wrapperClasses = cx({
      leftLabel,
      inline,
      "textareaWrapper": true
    });

    let inputClasses = cx({
      "textarea": true,
      "textareaWidth": !this.props.cols,
      "active": this.state.active,
      "error": this.state.status === "error",
      "success": this.state.status === "success",
      "warning": this.state.status === "warning",
      "unsizable": resizable === false,
      disabled
    });

    let messageClasses = cx({
      "success_message": this.state.status === "success",
      "error_message": this.state.status === "error",
      "warning_message": this.state.status === "warning"
    });

    let textAreaLabel = (label || tooltip) &&

    ;

    return (

        {textAreaLabel}
        <textarea
          {...othersFiltered}
          id={id}
          type={type}
          value={this.state.value}
          styleName={inputClasses}
          style={style}
          className={className}
          required={required}
          disabled={disabled}
          onChange={this._handleChange}
          onFocus={this._handleFocus}
          onBlur={this._handleBlur}
        />
        {this.state.message !== null &&

            {this.state.message}

        }

    );
  }
}

TextArea.propTypes = {
  /** An Object, array, or string of CSS classes to apply to TextArea.*/
  "className": PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
    PropTypes.array
  ]),
  /**
   * The cols prop gets passed to TextArea which specifies the visible width of the text area.
   * @examples '<TextArea cols={50}
   */
  "cols": PropTypes.number,
  /**
   * Determines if the TextArea is disabled.
   * @examples '<TextArea disabled/>'
   */
  "disabled": PropTypes.string,
  /** Define an id for the text input.*/
  "id": PropTypes.string,
  /** Sets whether or not TextArea will display as inline */
  "inline": PropTypes.bool,
  /**
   * Define a label to be displayed above the TextArea.
   * @examples '<TextArea label="test"/>'
   */
  "label": PropTypes.string,
  /**
   * Allows user to move the label to the left of the TextArea instead of above it
   */
  "leftLabel": PropTypes.bool,
  /**
   * Sets the status message to display below the TextArea. The color of the message will be determined by the value of the "status" property.
   * @examples '<TextArea message="Incorrect answer" status="error" />'
   */
  "message": PropTypes.string,
  /**
   * A callback that fires onBlur.
   */
  "onBlur": PropTypes.func,
  /**
   * Sets a handler function to be executed when onChange event occurs (at input element).
   * @examples <TextArea onChange={this.customOnChangeFunc}/>
   */
  "onChange": PropTypes.func,
  /**
   * A callback that fires onFocus.
   */
  "onFocus": PropTypes.func,
  /**
   * Sets a handler function to be executed before change event occurs (at input element).
   * return true if you want the chaneg to happen, pass false to deny the change.
   * @examples <TextField onBeforeChange={this.onBeforeChange}/>
   */
  "onBeforeChange": PropTypes.func,
  /**
   * Sets the TextArea as required. Will be validated onChange. Accepts a boolean or a string. If a string is passed it will be displayed instead of the traditional * next to the field label.
   * @examples '<TextArea required/>'
   */
  "required": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  /**
   * Allows TextArea to be resizable.  Accepts a boolean.
   * @examples '<TextArea resizable={false}/>'
   */
  "resizable": PropTypes.bool,
  /**
   * Sets the status of the TextArea. Options are null, "success", "error", and "warning".
   * @examples '<TextArea status="error" />'
   */
  "status": PropTypes.string,
  /** Pass inline styling here. */
  "style": PropTypes.object,
  /** Sets an element to be displayed along with the TextArea. Traditionally used with the Tooltip component, but will accept any component or HTML element. */
  "tooltip": PropTypes.node,
  /** Sets the position of the embedded Tooltip. Defaults to "right", any other value will move it next to the label. */
  "tooltipPosition": PropTypes.string,
  /**
   * Define a type for the text input. Default is "text".
   * @examples '<TextArea type="password"/>'
   */
  "type": PropTypes.string,
  /**
   * Converts all entered text to uppercase.
   */
  "uppercase": PropTypes.bool,
  /**
   * Sets a handler function to be executed and validate against. Will override the required property (you can still use the required prop to add a required indicator next to the label) and must return an object with a status (Options: null, "success", "error") and a message (Options: null or string)
   * @examples '<TextArea valid={this.customValidator}/>'
   */
  "valid": PropTypes.func,
  /**
   * Define a default value for the text input.
   * @examples '<TextArea value="TextArea value here"/>'
   */
  "value": PropTypes.string
};

TextArea.defaultProps = {
  "type": "text",
  "tooltipPosition": "right"
};

export default CSSModules(TextArea, styles, { "allowMultiple": true });
<commit_msg>Fix on before change text area (#833)<commit_after>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { Label } from "../Label";
import { utils } from "../utils";
import CSSModules from "react-css-modules";
import styles from "./TextArea.css";
import blacklist from "blacklist";

export class TextArea extends React.PureComponent {
  constructor(props) {
    super(props);

    // Initial state
    this.state = {
      "value": props.value || "",
      "active": false,
      "status": props.status || null,
      "message": props.message || null
    };
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.value !== this.props.value) {
      this.setState({
        "value": nextProps.value
      });
    }
    if (
      typeof nextProps.status !== "undefined" &&
        nextProps.status !== this.props.status ||
      typeof nextProps.message !== "undefined" &&
        nextProps.message !== this.props.message
    ) {
      this.setState({
        "status": nextProps.status,
        "message": nextProps.message
      });
    }
  }

  _eventHandlers = (e, change) => {
    const data = {
      "value": this.state.value,
      "status": this.state.status,
      "message": this.state.message
    };

    if (!change && this.props.onBlur) {
      this.props.onBlur(e, data);
    }

    if (change && this.props.onChange) {
      this.props.onChange(e, data);
    }
  };

  _handleChange = e => {
    e.persist();
    let value = e.target.value;

    if (this.props.uppercase) {
      value = value.toUpperCase();
    }

    const validationObject = utils.validate(
      value,
      this.props.valid,
      this.props.status,
      this.props.message,
      this.props.required
    );

    const data = {
      "value": value,
      "status": validationObject.status,
      "message": validationObject.message
    };

    let result = true;
    if (this.props.onBeforeChange) {
      result = this.props.onBeforeChange(e, data);
    }

    if (result === false) {
      return;
    }

    this.setState(
      {
        "status": validationObject.status,
        "message": validationObject.message,
        "value": value,
        "active": true
      },
      () => {
        this._eventHandlers(e, change);
      }
    );
  };

  _handleFocus = e => {
    e.persist();
    this.setState({ "active": true }, function() {
      if (typeof this.props.onFocus === "function") {
        this.props.onFocus(e, {
          "value": this.state.value,
          "status": this.state.status,
          "message": this.state.message
        });
      }
    });
  };

  _handleBlur = e => {
    e.persist();
    const value = e.target.value;
    const validationObject = utils.validate(
      value,
      this.props.valid,
      this.props.status,
      this.props.message,
      this.props.required,
      e
    );

    this.setState(
      {
        "status": validationObject.status,
        "message": validationObject.message,
        "value": value,
        "active": false
      },
      () => {
        this._eventHandlers(e, false);
      }
    );
  };

  render() {
    const {
      className,
      disabled,
      id,
      inline,
      label,
      leftLabel,
      required,
      resizable,
      style,
      tooltip,
      type,
      tooltipPosition,
      ...others
    } = this.props;

    // Declaring the following variables so they don't get passed to the Textarea through the prop spread.
    const othersFiltered = blacklist(others, "onBeforeChange");

    let wrapperClasses = cx({
      leftLabel,
      inline,
      "textareaWrapper": true
    });

    let inputClasses = cx({
      "textarea": true,
      "textareaWidth": !this.props.cols,
      "active": this.state.active,
      "error": this.state.status === "error",
      "success": this.state.status === "success",
      "warning": this.state.status === "warning",
      "unsizable": resizable === false,
      disabled
    });

    let messageClasses = cx({
      "success_message": this.state.status === "success",
      "error_message": this.state.status === "error",
      "warning_message": this.state.status === "warning"
    });

    let textAreaLabel = (label || tooltip) &&

    ;

    return (

        {textAreaLabel}
        <textarea
          {...othersFiltered}
          id={id}
          type={type}
          value={this.state.value}
          styleName={inputClasses}
          style={style}
          className={className}
          required={required}
          disabled={disabled}
          onChange={this._handleChange}
          onFocus={this._handleFocus}
          onBlur={this._handleBlur}
        />
        {this.state.message !== null &&

            {this.state.message}

        }

    );
  }
}

TextArea.propTypes = {
  /** An Object, array, or string of CSS classes to apply to TextArea.*/
  "className": PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
    PropTypes.array
  ]),
  /**
   * The cols prop gets passed to TextArea which specifies the visible width of the text area.
   * @examples '<TextArea cols={50}
   */
  "cols": PropTypes.number,
  /**
   * Determines if the TextArea is disabled.
   * @examples '<TextArea disabled/>'
   */
  "disabled": PropTypes.string,
  /** Define an id for the text input.*/
  "id": PropTypes.string,
  /** Sets whether or not TextArea will display as inline */
  "inline": PropTypes.bool,
  /**
   * Define a label to be displayed above the TextArea.
   * @examples '<TextArea label="test"/>'
   */
  "label": PropTypes.string,
  /**
   * Allows user to move the label to the left of the TextArea instead of above it
   */
  "leftLabel": PropTypes.bool,
  /**
   * Sets the status message to display below the TextArea. The color of the message will be determined by the value of the "status" property.
   * @examples '<TextArea message="Incorrect answer" status="error" />'
   */
  "message": PropTypes.string,
  /**
   * A callback that fires onBlur.
   */
  "onBlur": PropTypes.func,
  /**
   * Sets a handler function to be executed when onChange event occurs (at input element).
   * @examples <TextArea onChange={this.customOnChangeFunc}/>
   */
  "onChange": PropTypes.func,
  /**
   * A callback that fires onFocus.
   */
  "onFocus": PropTypes.func,
  /**
   * Sets a handler function to be executed before change event occurs (at input element).
   * return true if you want the chaneg to happen, pass false to deny the change.
   * @examples <TextField onBeforeChange={this.onBeforeChange}/>
   */
  "onBeforeChange": PropTypes.func,
  /**
   * Sets the TextArea as required. Will be validated onChange. Accepts a boolean or a string. If a string is passed it will be displayed instead of the traditional * next to the field label.
   * @examples '<TextArea required/>'
   */
  "required": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  /**
   * Allows TextArea to be resizable.  Accepts a boolean.
   * @examples '<TextArea resizable={false}/>'
   */
  "resizable": PropTypes.bool,
  /**
   * Sets the status of the TextArea. Options are null, "success", "error", and "warning".
   * @examples '<TextArea status="error" />'
   */
  "status": PropTypes.string,
  /** Pass inline styling here. */
  "style": PropTypes.object,
  /** Sets an element to be displayed along with the TextArea. Traditionally used with the Tooltip component, but will accept any component or HTML element. */
  "tooltip": PropTypes.node,
  /** Sets the position of the embedded Tooltip. Defaults to "right", any other value will move it next to the label. */
  "tooltipPosition": PropTypes.string,
  /**
   * Define a type for the text input. Default is "text".
   * @examples '<TextArea type="password"/>'
   */
  "type": PropTypes.string,
  /**
   * Converts all entered text to uppercase.
   */
  "uppercase": PropTypes.bool,
  /**
   * Sets a handler function to be executed and validate against. Will override the required property (you can still use the required prop to add a required indicator next to the label) and must return an object with a status (Options: null, "success", "error") and a message (Options: null or string)
   * @examples '<TextArea valid={this.customValidator}/>'
   */
  "valid": PropTypes.func,
  /**
   * Define a default value for the text input.
   * @examples '<TextArea value="TextArea value here"/>'
   */
  "value": PropTypes.string
};

TextArea.defaultProps = {
  "type": "text",
  "tooltipPosition": "right"
};

export default CSSModules(TextArea, styles, { "allowMultiple": true });
<|endoftext|>


<commit_before>module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: [
    'Chrome'
  ],
  launch_in_dev: [
    'Chrome'
  ],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        // --no-sandbox is needed when running Chrome inside a container
        process.env.TRAVIS ? '--no-sandbox' : null,

        '--disable-gpu',
        '--headless',
        '--remote-debugging-port=0',
        '--window-size=1440,900'
      ].filter(Boolean)
    }
  }
};
<commit_msg>Test locally in multiple browsers<commit_after>module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: [
    'Chrome'
  ],
  launch_in_dev: [
    'Chrome',
    'Firefox',
    'Safari'
  ],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        // --no-sandbox is needed when running Chrome inside a container
        process.env.TRAVIS ? '--no-sandbox' : null,

        '--disable-gpu',
        '--headless',
        '--remote-debugging-port=0',
        '--window-size=1440,900'
      ].filter(Boolean)
    }
  }
};
<|endoftext|>


<commit_before>/*
 * IDBWrapper - A cross-browser wrapper for IndexedDB
 * Copyright (c) 2011 Jens Arps
 * http://jensarps.de/
 *
 * Licensed under the MIT (X11) license
 */

(function (name, definition, global) {
  if (typeof define === 'function') {
    define(definition);
  } else {
    global[name] = definition();
  }
})('IDBStore', function () {

  var IDBStore;

  var defaults = {
    storeName: 'Store',
    dbVersion: 1,
    keyPath: 'id',
    autoIncrement: true,
    onStoreReady: function () {
    }
  };

  IDBStore = function (kwArgs, onStoreReady) {

    function fixupConstants (object, constants) {
      for (var prop in constants) {
        if (!(prop in object))
          object[prop] = constants[prop];
      }
    }

    mixin(this, defaults);
    mixin(this, kwArgs);
    this.dbName = 'IDBWrapper-' + this.storeName;

    onStoreReady && (this.onStoreReady = onStoreReady);

    this.idb = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
    this.consts = window.IDBTransaction || window.webkitIDBTransaction;
    fixupConstants(this.consts, {
      'READ_ONLY': 'readonly',
      'READ_WRITE': 'readwrite',
      'VERSION_CHANGE': 'versionchange'
    });
    this.cursor = window.IDBCursor || window.webkitIDBCursor;
    fixupConstants(this.cursor, {
      'NEXT': 'next',
      'NEXT_NO_DUPLICATE': 'nextunique',
      'PREV': 'prev',
      'PREV_NO_DUPLICATE': 'prevunique'
    });
    this.openDB();
  };

  IDBStore.prototype = {

    db: null,

    dbName: null,

    dbDescription: null,

    dbVersion: null,

    store: null,

    storeName: null,

    keyPath: null,

    autoIncrement: null,

    features: null,

    onStoreReady: null,

    openDB: function () {

      this.newVersionAPI = typeof this.idb.setVersion == 'undefined';

      if(!this.newVersionAPI){
        throw new Error('The IndexedDB implementation in this browser is outdated. Please upgrade your browser.');
      }

      var features = this.features = {};
      features.hasAutoIncrement = !window.mozIndexedDB; // TODO: Still, really?

      var openRequest;
      if (this.newVersionAPI) {
        this.dbVersion = parseInt(this.dbVersion, 10);
        openRequest = this.idb.open(this.dbName, this.dbVersion, this.dbDescription);
      } else {
        openRequest = this.idb.open(this.dbName, this.dbDescription);
      }

      openRequest.onerror = hitch(this, function (error) {
        var gotVersionErr = false;
        if ('error' in error.target) {
          gotVersionErr = error.target.error.name == "VersionError";
        } else if ('errorCode' in error.target) {
          gotVersionErr = error.target.errorCode == 12; // TODO: Use const
        }

        if (gotVersionErr) {
          console.log('Version error');
          //setTimeout(hitch(this, 'openDB'));
        } else {
          console.error('Could not open database, error', error);
        }
      });

      openRequest.onsuccess = hitch(this, function (event) {
        this.db = event.target.result;

        this.db.onversionchange = function (event) {
          event.target.close();
        };

        if (this.newVersionAPI) {
          this.getObjectStore(hitch(this, function () {
            setTimeout(this.onStoreReady);
          }));
        } else {
          // getObjectStore will either call
          //   a) openExistingObjectStore, which will create a new transaction
          //   b) createNewObjectStore, which will try to enter mutation state,
          //      which can only be done via a versionchange transaction
          // Since Chrome 21, both actions require to not be inside of a
          // versionchange transaction, which will be the case if the database
          // is new.
          this.checkVersion(hitch(this, function () {
            this.getObjectStore(hitch(this, function () {
              setTimeout(this.onStoreReady);
            }));
          }), null, { waitForTransactionEnd: true });
        }
      });
    },

    deleteDatabase: function () {
      if (this.idb.deleteDatabase) {
        this.idb.deleteDatabase(this.dbName);
      }
    },

    enterMutationState: function (onSuccess, onError) {
      if (this.newVersionAPI) {
        this.dbVersion++;
        var openRequest = this.idb.open(this.dbName, this.dbVersion, this.dbDescription);
        openRequest.onupgradeneeded = onSuccess;
        openRequest.onsuccess = onError;
        openRequest.onerror = onError;
        openRequest.onblocked = onError;
      } else {
        this.setVersion(onSuccess, onError);
      }
    },

    /**************
     * versioning *
     **************/

    checkVersion: function (onSuccess, onError, options) {
      options || (options = {});
      if (this.getVersion() != this.dbVersion) {
        this.setVersion(onSuccess, onError, options);
      } else {
        onSuccess && onSuccess();
      }
    },

    getVersion: function () {
      return this.db.version;
    },

    setVersion: function (onSuccess, onError, options) {
      options || (options = {});
      onError || (onError = function (error) {
        console.error('Failed to set version.', error);
      });
      var versionRequest = this.db.setVersion(this.dbVersion);
      versionRequest.onerror = onError;
      versionRequest.onblocked = onError;

      versionRequest.onsuccess = function (evt) {
        if (options.waitForTransactionEnd) {
          var transaction = evt.target.result;
          transaction.oncomplete = onSuccess;
        } else {
          onSuccess();
        }
      };
    },

    /*************************
     * object store handling *
     *************************/

    getObjectStore: function (onSuccess, onError) {
      if (this.hasObjectStore()) {
        this.openExistingObjectStore(onSuccess, onError);
      } else {
        this.createNewObjectStore(onSuccess, onError);
      }
    },

    hasObjectStore: function () {
      return this.db.objectStoreNames.contains(this.storeName);
    },

    createNewObjectStore: function (onSuccess, onError) {
      this.enterMutationState(hitch(this, function () {
        this.store = this.db.createObjectStore(this.storeName, { keyPath: this.keyPath, autoIncrement: this.autoIncrement});
        onSuccess && onSuccess(this.store);
      }), onError);
    },

    openExistingObjectStore: function (onSuccess, onError) {
      var emptyTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      this.store = emptyTransaction.objectStore(this.storeName);
      emptyTransaction.abort();
      onSuccess && onSuccess(this.store);
    },

    deleteObjectStore: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Failed to delete objectStore.', error);
      });
      this.enterMutationState(hitch(this, function (evt) {
        var db = evt.target.result;
        db.deleteObjectStore(this.storeName);
        var success = !this.hasObjectStore();
        onSuccess && success && onSuccess();
        onError && !success && onError();
      }), onError);
    },

    /*********************
     * data manipulation *
     *********************/

    put: function (dataObj, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not write data.', error);
      });
      onSuccess || (onSuccess = noop);
      if (typeof dataObj[this.keyPath] == 'undefined' && !this.features.hasAutoIncrement) {
        dataObj[this.keyPath] = this._getUID();
      }
      var putTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var putRequest = putTransaction.objectStore(this.storeName).put(dataObj);
      putRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      putRequest.onerror = onError;
    },

    get: function (key, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not read data.', error);
      });
      onSuccess || (onSuccess = noop);
      var getTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      var getRequest = getTransaction.objectStore(this.storeName).get(key);
      getRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      getRequest.onerror = onError;
    },

    remove: function (key, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not remove data.', error);
      });
      onSuccess || (onSuccess = noop);
      var removeTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var deleteRequest = removeTransaction.objectStore(this.storeName).delete(key);
      deleteRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      deleteRequest.onerror = onError;
    },

    getAll: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not read data.', error);
      });
      onSuccess || (onSuccess = noop);
      var getAllTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      var store = getAllTransaction.objectStore(this.storeName);
      if (store.getAll) {
        var getAllRequest = store.getAll();
        getAllRequest.onsuccess = function (event) {
          onSuccess(event.target.result);
        };
        getAllRequest.onerror = onError;
      } else {
        this._getAllCursor(getAllTransaction, onSuccess, onError);
      }
    },

    _getAllCursor: function (tr, onSuccess, onError) {
      var all = [];
      var store = tr.objectStore(this.storeName);
      var cursorRequest = store.openCursor();

      cursorRequest.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          all.push(cursor.value);
          cursor['continue']();
        }
        else {
          onSuccess(all);
        }
      };
      cursorRequest.onError = onError;
    },

    clear: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not clear store.', error);
      });
      onSuccess || (onSuccess = noop);
      var clearTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var clearRequest = clearTransaction.objectStore(this.storeName).clear();
      clearRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      clearRequest.onerror = onError;
    },

    _getUID: function () {
      // FF bails at times on non-numeric ids. So we take an even
      // worse approach now, using current time as id. Sigh.
      return +new Date();
    },

    /************
     * indexing *
     ************/

    createIndex: function (indexName, propertyName, isUnique, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not create index.', error);
      });
      onSuccess || (onSuccess = noop);
      propertyName || (propertyName = indexName);

      var that = this;

      this.enterMutationState(hitch(this, function (evt) {
        var result = evt.target.result;
        var index;
        if (result.objectStore) { // transaction
          index = db.objectStore(this.storeName).createIndex(indexName, propertyName, { unique: !!isUnique });
        } else { // db
          var putTransaction = result.transaction([that.storeName] /* , this.consts.READ_WRITE */);
          var store = putTransaction.objectStore(that.storeName);
          index = store.createIndex(indexName, propertyName, { unique: !!isUnique });
        }
        onSuccess(index);
      }), onError);
    },

    getIndex: function (indexName) {
      return this.store.index(indexName);
    },

    getIndexList: function () {
      return this.store.indexNames;
    },

    hasIndex: function (indexName) {
      return this.store.indexNames.contains(indexName);
    },

    removeIndex: function (indexName, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not remove index.', error);
      });
      onSuccess || (onSuccess = noop);
      this.enterMutationState(hitch(this, function (evt) {
        evt.target.result.objectStore(this.storeName).deleteIndex(indexName);
        onSuccess();
      }), onError);
    },

    /**********
     * cursor *
     **********/

    iterate: function (callback, options) {
      options = mixin({
        index: null,
        order: 'ASC',
        filterDuplicates: false,
        keyRange: null,
        writeAccess: false,
        onEnd: null,
        onError: function (error) {
          console.error('Could not open cursor.', error);
        }
      }, options || {});

      var directionType = options.order.toLowerCase() == 'desc' ? 'PREV' : 'NEXT';
      if (options.filterDuplicates) {
        directionType += '_NO_DUPLICATE';
      }

      var cursorTransaction = this.db.transaction([this.storeName], this.consts[options.writeAccess ? 'READ_WRITE' : 'READ_ONLY']);
      var cursorTarget = cursorTransaction.objectStore(this.storeName);
      if (options.index) {
        cursorTarget = cursorTarget.index(options.index);
      }

      var cursorRequest = cursorTarget.openCursor(options.keyRange, this.cursor[directionType]);
      cursorRequest.onerror = options.onError;
      cursorRequest.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          callback(cursor.value, cursor, cursorTransaction);
          cursor['continue']();
        } else {
          options.onEnd && options.onEnd() || callback(null, cursor, cursorTransaction)
        }
      };
    }

    /* key ranges */
    // TODO: implement

  };

  /** helpers **/

  var noop = function () {
  };
  var empty = {};
  var mixin = function (target, source) {
    var name, s;
    for (name in source) {
      s = source[name];
      if (s !== empty[name] && s !== target[name]) {
        target[name] = s;
      }
    }
    return target;
  };
  var hitch = function (scope, method) {
    if (!method) {
      method = scope;
      scope = null;
    }
    if (typeof method == "string") {
      scope = scope || window;
      if (!scope[method]) {
        throw(['method not found']);
      }
      return function () {
        return scope[method].apply(scope, arguments || []);
      };
    }
    return !scope ? method : function () {
      return method.apply(scope, arguments || []);
    };
  };

  return IDBStore;

}, this);
<commit_msg>Remove old version api open request<commit_after>/*
 * IDBWrapper - A cross-browser wrapper for IndexedDB
 * Copyright (c) 2011 Jens Arps
 * http://jensarps.de/
 *
 * Licensed under the MIT (X11) license
 */

(function (name, definition, global) {
  if (typeof define === 'function') {
    define(definition);
  } else {
    global[name] = definition();
  }
})('IDBStore', function () {

  var IDBStore;

  var defaults = {
    storeName: 'Store',
    dbVersion: 1,
    keyPath: 'id',
    autoIncrement: true,
    onStoreReady: function () {
    }
  };

  IDBStore = function (kwArgs, onStoreReady) {

    function fixupConstants (object, constants) {
      for (var prop in constants) {
        if (!(prop in object))
          object[prop] = constants[prop];
      }
    }

    mixin(this, defaults);
    mixin(this, kwArgs);
    this.dbName = 'IDBWrapper-' + this.storeName;

    onStoreReady && (this.onStoreReady = onStoreReady);

    this.idb = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
    this.consts = window.IDBTransaction || window.webkitIDBTransaction;
    fixupConstants(this.consts, {
      'READ_ONLY': 'readonly',
      'READ_WRITE': 'readwrite',
      'VERSION_CHANGE': 'versionchange'
    });
    this.cursor = window.IDBCursor || window.webkitIDBCursor;
    fixupConstants(this.cursor, {
      'NEXT': 'next',
      'NEXT_NO_DUPLICATE': 'nextunique',
      'PREV': 'prev',
      'PREV_NO_DUPLICATE': 'prevunique'
    });
    this.openDB();
  };

  IDBStore.prototype = {

    db: null,

    dbName: null,

    dbDescription: null,

    dbVersion: null,

    store: null,

    storeName: null,

    keyPath: null,

    autoIncrement: null,

    features: null,

    onStoreReady: null,

    openDB: function () {

      this.newVersionAPI = typeof this.idb.setVersion == 'undefined';

      if(!this.newVersionAPI){
        throw new Error('The IndexedDB implementation in this browser is outdated. Please upgrade your browser.');
      }

      var features = this.features = {};
      features.hasAutoIncrement = !window.mozIndexedDB; // TODO: Still, really?

      var openRequest;

      this.dbVersion = parseInt(this.dbVersion, 10);
      openRequest = this.idb.open(this.dbName, this.dbVersion);

      openRequest.onerror = hitch(this, function (error) {
        var gotVersionErr = false;
        if ('error' in error.target) {
          gotVersionErr = error.target.error.name == "VersionError";
        } else if ('errorCode' in error.target) {
          gotVersionErr = error.target.errorCode == 12; // TODO: Use const
        }

        if (gotVersionErr) {
          console.log('Version error');
          //setTimeout(hitch(this, 'openDB'));
        } else {
          console.error('Could not open database, error', error);
        }
      });

      openRequest.onsuccess = hitch(this, function (event) {
        this.db = event.target.result;

        this.db.onversionchange = function (event) {
          event.target.close();
        };

        if (this.newVersionAPI) {
          this.getObjectStore(hitch(this, function () {
            setTimeout(this.onStoreReady);
          }));
        } else {
          // getObjectStore will either call
          //   a) openExistingObjectStore, which will create a new transaction
          //   b) createNewObjectStore, which will try to enter mutation state,
          //      which can only be done via a versionchange transaction
          // Since Chrome 21, both actions require to not be inside of a
          // versionchange transaction, which will be the case if the database
          // is new.
          this.checkVersion(hitch(this, function () {
            this.getObjectStore(hitch(this, function () {
              setTimeout(this.onStoreReady);
            }));
          }), null, { waitForTransactionEnd: true });
        }
      });
    },

    deleteDatabase: function () {
      if (this.idb.deleteDatabase) {
        this.idb.deleteDatabase(this.dbName);
      }
    },

    enterMutationState: function (onSuccess, onError) {
      if (this.newVersionAPI) {
        this.dbVersion++;
        var openRequest = this.idb.open(this.dbName, this.dbVersion, this.dbDescription);
        openRequest.onupgradeneeded = onSuccess;
        openRequest.onsuccess = onError;
        openRequest.onerror = onError;
        openRequest.onblocked = onError;
      } else {
        this.setVersion(onSuccess, onError);
      }
    },

    /**************
     * versioning *
     **************/

    checkVersion: function (onSuccess, onError, options) {
      options || (options = {});
      if (this.getVersion() != this.dbVersion) {
        this.setVersion(onSuccess, onError, options);
      } else {
        onSuccess && onSuccess();
      }
    },

    getVersion: function () {
      return this.db.version;
    },

    setVersion: function (onSuccess, onError, options) {
      options || (options = {});
      onError || (onError = function (error) {
        console.error('Failed to set version.', error);
      });
      var versionRequest = this.db.setVersion(this.dbVersion);
      versionRequest.onerror = onError;
      versionRequest.onblocked = onError;

      versionRequest.onsuccess = function (evt) {
        if (options.waitForTransactionEnd) {
          var transaction = evt.target.result;
          transaction.oncomplete = onSuccess;
        } else {
          onSuccess();
        }
      };
    },

    /*************************
     * object store handling *
     *************************/

    getObjectStore: function (onSuccess, onError) {
      if (this.hasObjectStore()) {
        this.openExistingObjectStore(onSuccess, onError);
      } else {
        this.createNewObjectStore(onSuccess, onError);
      }
    },

    hasObjectStore: function () {
      return this.db.objectStoreNames.contains(this.storeName);
    },

    createNewObjectStore: function (onSuccess, onError) {
      this.enterMutationState(hitch(this, function () {
        this.store = this.db.createObjectStore(this.storeName, { keyPath: this.keyPath, autoIncrement: this.autoIncrement});
        onSuccess && onSuccess(this.store);
      }), onError);
    },

    openExistingObjectStore: function (onSuccess, onError) {
      var emptyTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      this.store = emptyTransaction.objectStore(this.storeName);
      emptyTransaction.abort();
      onSuccess && onSuccess(this.store);
    },

    deleteObjectStore: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Failed to delete objectStore.', error);
      });
      this.enterMutationState(hitch(this, function (evt) {
        var db = evt.target.result;
        db.deleteObjectStore(this.storeName);
        var success = !this.hasObjectStore();
        onSuccess && success && onSuccess();
        onError && !success && onError();
      }), onError);
    },

    /*********************
     * data manipulation *
     *********************/

    put: function (dataObj, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not write data.', error);
      });
      onSuccess || (onSuccess = noop);
      if (typeof dataObj[this.keyPath] == 'undefined' && !this.features.hasAutoIncrement) {
        dataObj[this.keyPath] = this._getUID();
      }
      var putTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var putRequest = putTransaction.objectStore(this.storeName).put(dataObj);
      putRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      putRequest.onerror = onError;
    },

    get: function (key, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not read data.', error);
      });
      onSuccess || (onSuccess = noop);
      var getTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      var getRequest = getTransaction.objectStore(this.storeName).get(key);
      getRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      getRequest.onerror = onError;
    },

    remove: function (key, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not remove data.', error);
      });
      onSuccess || (onSuccess = noop);
      var removeTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var deleteRequest = removeTransaction.objectStore(this.storeName).delete(key);
      deleteRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      deleteRequest.onerror = onError;
    },

    getAll: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not read data.', error);
      });
      onSuccess || (onSuccess = noop);
      var getAllTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY);
      var store = getAllTransaction.objectStore(this.storeName);
      if (store.getAll) {
        var getAllRequest = store.getAll();
        getAllRequest.onsuccess = function (event) {
          onSuccess(event.target.result);
        };
        getAllRequest.onerror = onError;
      } else {
        this._getAllCursor(getAllTransaction, onSuccess, onError);
      }
    },

    _getAllCursor: function (tr, onSuccess, onError) {
      var all = [];
      var store = tr.objectStore(this.storeName);
      var cursorRequest = store.openCursor();

      cursorRequest.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          all.push(cursor.value);
          cursor['continue']();
        }
        else {
          onSuccess(all);
        }
      };
      cursorRequest.onError = onError;
    },

    clear: function (onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not clear store.', error);
      });
      onSuccess || (onSuccess = noop);
      var clearTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE);
      var clearRequest = clearTransaction.objectStore(this.storeName).clear();
      clearRequest.onsuccess = function (event) {
        onSuccess(event.target.result);
      };
      clearRequest.onerror = onError;
    },

    _getUID: function () {
      // FF bails at times on non-numeric ids. So we take an even
      // worse approach now, using current time as id. Sigh.
      return +new Date();
    },

    /************
     * indexing *
     ************/

    createIndex: function (indexName, propertyName, isUnique, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not create index.', error);
      });
      onSuccess || (onSuccess = noop);
      propertyName || (propertyName = indexName);

      var that = this;

      this.enterMutationState(hitch(this, function (evt) {
        var result = evt.target.result;
        var index;
        if (result.objectStore) { // transaction
          index = db.objectStore(this.storeName).createIndex(indexName, propertyName, { unique: !!isUnique });
        } else { // db
          var putTransaction = result.transaction([that.storeName] /* , this.consts.READ_WRITE */);
          var store = putTransaction.objectStore(that.storeName);
          index = store.createIndex(indexName, propertyName, { unique: !!isUnique });
        }
        onSuccess(index);
      }), onError);
    },

    getIndex: function (indexName) {
      return this.store.index(indexName);
    },

    getIndexList: function () {
      return this.store.indexNames;
    },

    hasIndex: function (indexName) {
      return this.store.indexNames.contains(indexName);
    },

    removeIndex: function (indexName, onSuccess, onError) {
      onError || (onError = function (error) {
        console.error('Could not remove index.', error);
      });
      onSuccess || (onSuccess = noop);
      this.enterMutationState(hitch(this, function (evt) {
        evt.target.result.objectStore(this.storeName).deleteIndex(indexName);
        onSuccess();
      }), onError);
    },

    /**********
     * cursor *
     **********/

    iterate: function (callback, options) {
      options = mixin({
        index: null,
        order: 'ASC',
        filterDuplicates: false,
        keyRange: null,
        writeAccess: false,
        onEnd: null,
        onError: function (error) {
          console.error('Could not open cursor.', error);
        }
      }, options || {});

      var directionType = options.order.toLowerCase() == 'desc' ? 'PREV' : 'NEXT';
      if (options.filterDuplicates) {
        directionType += '_NO_DUPLICATE';
      }

      var cursorTransaction = this.db.transaction([this.storeName], this.consts[options.writeAccess ? 'READ_WRITE' : 'READ_ONLY']);
      var cursorTarget = cursorTransaction.objectStore(this.storeName);
      if (options.index) {
        cursorTarget = cursorTarget.index(options.index);
      }

      var cursorRequest = cursorTarget.openCursor(options.keyRange, this.cursor[directionType]);
      cursorRequest.onerror = options.onError;
      cursorRequest.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          callback(cursor.value, cursor, cursorTransaction);
          cursor['continue']();
        } else {
          options.onEnd && options.onEnd() || callback(null, cursor, cursorTransaction)
        }
      };
    }

    /* key ranges */
    // TODO: implement

  };

  /** helpers **/

  var noop = function () {
  };
  var empty = {};
  var mixin = function (target, source) {
    var name, s;
    for (name in source) {
      s = source[name];
      if (s !== empty[name] && s !== target[name]) {
        target[name] = s;
      }
    }
    return target;
  };
  var hitch = function (scope, method) {
    if (!method) {
      method = scope;
      scope = null;
    }
    if (typeof method == "string") {
      scope = scope || window;
      if (!scope[method]) {
        throw(['method not found']);
      }
      return function () {
        return scope[method].apply(scope, arguments || []);
      };
    }
    return !scope ? method : function () {
      return method.apply(scope, arguments || []);
    };
  };

  return IDBStore;

}, this);
<|endoftext|>


<commit_before>/* global define */

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory)
  } else if (typeof exports === 'object') {
    module.exports = factory()
  } else {
    root.PromisePool = factory()
    // Legacy API
    root.promisePool = root.PromisePool
  }
})(this, function () {
  'use strict'

  var EventTarget = function () {
    this._listeners = {}
  }

  EventTarget.prototype.addEventListener = function (type, listener) {
    this._listeners[type] = this._listeners[type] || []
    if (this._listeners[type].indexOf(listener) < 0) {
      this._listeners[type].push(listener)
    }
  }

  EventTarget.prototype.removeEventListener = function (type, listener) {
    if (this._listeners[type]) {
      var p = this._listeners[type].indexOf(listener)
      if (p >= 0) {
        this._listeners[type].splice(p, 1)
      }
    }
  }

  EventTarget.prototype.dispatchEvent = function (evt) {
    if (this._listeners[evt.type] && this._listeners[evt.type].length) {
      var listeners = this._listeners[evt.type].slice()
      for (var i = 0, l = listeners.length; i < l; ++i) {
        listeners[i].call(this, evt)
      }
    }
  }

  var isGenerator = function (func) {
    return (typeof func.constructor === 'function' &&
      func.constructor.name === 'GeneratorFunction')
  }

  var functionToIterator = function (func) {
    return {
      next: function () {
        var promise = func()
        return promise ? {value: promise} : {done: true}
      }
    }
  }

  var promiseToIterator = function (promise) {
    var called = false
    return {
      next: function () {
        if (called) {
          return {done: true}
        }
        called = true
        return {value: promise}
      }
    }
  }

  var toIterator = function (obj, Promise) {
    var type = typeof obj
    if (type === 'object') {
      if (typeof obj.next === 'function') {
        return obj
      }
      if (typeof obj.then === 'function') {
        return promiseToIterator(obj)
      }
    }
    if (type === 'function') {
      return isGenerator(obj) ? obj() : functionToIterator(obj)
    }
    return promiseToIterator(Promise.resolve(obj))
  }

  var PromisePoolEvent = function (target, type, data) {
    this.target = target
    this.type = type
    this.data = data
  }

  var PromisePool = function (source, concurrency, options) {
    EventTarget.call(this)
    if (typeof concurrency !== 'number' ||
        Math.floor(concurrency) !== concurrency ||
        concurrency < 1) {
      throw new Error('Invalid concurrency')
    }
    this._concurrency = concurrency
    this._options = options || {}
    this._options.promise = this._options.promise || Promise
    this._iterator = toIterator(source, this._options.promise)
    this._done = false
    this._size = 0
    this._promise = null
    this._callbacks = null
  }
  PromisePool.prototype = new EventTarget()
  PromisePool.prototype.constructor = EventTarget

  PromisePool.prototype.concurrency = function (value) {
    if (typeof value !== 'undefined') {
      this._concurrency = value
      if (this.active()) {
        this._proceed()
      }
    }
    return this._concurrency
  }

  PromisePool.prototype.size = function () {
    return this._size
  }

  PromisePool.prototype.active = function () {
    return !!this._promise
  }

  PromisePool.prototype.promise = function () {
    return this._promise
  }

  PromisePool.prototype.start = function () {
    var that = this
    var Promise = this._options.promise
    this._promise = new Promise(function (resolve, reject) {
      that._callbacks = {
        reject: reject,
        resolve: resolve
      }
      that._proceed()
    })
    return this._promise
  }

  PromisePool.prototype._fireEvent = function (type, data) {
    this.dispatchEvent(new PromisePoolEvent(this, type, data))
  }

  PromisePool.prototype._settle = function (error) {
    if (error) {
      this._callbacks.reject(error)
    } else {
      this._callbacks.resolve()
    }
    this._promise = null
    this._callbacks = null
  }

  PromisePool.prototype._onPooledPromiseFulfilled = function (promise, result) {
    this._size--
    if (this.active()) {
      this._fireEvent('fulfilled', {
        promise: promise,
        result: result
      })
      this._proceed()
    }
  }

  PromisePool.prototype._onPooledPromiseRejected = function (promise, error) {
    this._size--
    if (this.active()) {
      this._fireEvent('rejected', {
        promise: promise,
        error: error
      })
      this._settle(error || new Error('Unknown error'))
    }
  }

  PromisePool.prototype._trackPromise = function (promise) {
    var that = this
    promise
      .then(function (result) {
        that._onPooledPromiseFulfilled(promise, result)
      }, function (error) {
        that._onPooledPromiseRejected(promise, error)
      })['catch'](function (err) {
        that._settle(new Error('Promise processing failed: ' + err))
      })
  }

  PromisePool.prototype._proceed = function () {
    if (!this._done) {
      var result = null
      while (this._size < this._concurrency &&
          !(result = this._iterator.next()).done) {
        this._size++
        this._trackPromise(result.value)
      }
      this._done = (result === null || !!result.done)
    }
    if (this._done && this._size === 0) {
      this._settle()
    }
  }

  PromisePool.PromisePoolEvent = PromisePoolEvent
  // Legacy API
  PromisePool.PromisePool = PromisePool

  return PromisePool
})
<commit_msg>Istanbul ignores<commit_after>/* global define */

(function (root, factory) {
  /* istanbul ignore if */
  if (typeof define === 'function' && define.amd) {
    define([], factory)
  } else /* istanbul ignore else */ if (typeof exports === 'object') {
    module.exports = factory()
  } else {
    root.PromisePool = factory()
    // Legacy API
    root.promisePool = root.PromisePool
  }
})(this, function () {
  'use strict'

  var EventTarget = function () {
    this._listeners = {}
  }

  EventTarget.prototype.addEventListener = function (type, listener) {
    this._listeners[type] = this._listeners[type] || []
    if (this._listeners[type].indexOf(listener) < 0) {
      this._listeners[type].push(listener)
    }
  }

  EventTarget.prototype.removeEventListener = function (type, listener) {
    if (this._listeners[type]) {
      var p = this._listeners[type].indexOf(listener)
      if (p >= 0) {
        this._listeners[type].splice(p, 1)
      }
    }
  }

  EventTarget.prototype.dispatchEvent = function (evt) {
    if (this._listeners[evt.type] && this._listeners[evt.type].length) {
      var listeners = this._listeners[evt.type].slice()
      for (var i = 0, l = listeners.length; i < l; ++i) {
        listeners[i].call(this, evt)
      }
    }
  }

  var isGenerator = function (func) {
    return (typeof func.constructor === 'function' &&
      func.constructor.name === 'GeneratorFunction')
  }

  var functionToIterator = function (func) {
    return {
      next: function () {
        var promise = func()
        return promise ? {value: promise} : {done: true}
      }
    }
  }

  var promiseToIterator = function (promise) {
    var called = false
    return {
      next: function () {
        if (called) {
          return {done: true}
        }
        called = true
        return {value: promise}
      }
    }
  }

  var toIterator = function (obj, Promise) {
    var type = typeof obj
    if (type === 'object') {
      if (typeof obj.next === 'function') {
        return obj
      }
      /* istanbul ignore else */
      if (typeof obj.then === 'function') {
        return promiseToIterator(obj)
      }
    }
    if (type === 'function') {
      return isGenerator(obj) ? obj() : functionToIterator(obj)
    }
    return promiseToIterator(Promise.resolve(obj))
  }

  var PromisePoolEvent = function (target, type, data) {
    this.target = target
    this.type = type
    this.data = data
  }

  var PromisePool = function (source, concurrency, options) {
    EventTarget.call(this)
    if (typeof concurrency !== 'number' ||
        Math.floor(concurrency) !== concurrency ||
        concurrency < 1) {
      throw new Error('Invalid concurrency')
    }
    this._concurrency = concurrency
    this._options = options || {}
    this._options.promise = this._options.promise || Promise
    this._iterator = toIterator(source, this._options.promise)
    this._done = false
    this._size = 0
    this._promise = null
    this._callbacks = null
  }
  PromisePool.prototype = new EventTarget()
  PromisePool.prototype.constructor = EventTarget

  PromisePool.prototype.concurrency = function (value) {
    if (typeof value !== 'undefined') {
      this._concurrency = value
      if (this.active()) {
        this._proceed()
      }
    }
    return this._concurrency
  }

  PromisePool.prototype.size = function () {
    return this._size
  }

  PromisePool.prototype.active = function () {
    return !!this._promise
  }

  PromisePool.prototype.promise = function () {
    return this._promise
  }

  PromisePool.prototype.start = function () {
    var that = this
    var Promise = this._options.promise
    this._promise = new Promise(function (resolve, reject) {
      that._callbacks = {
        reject: reject,
        resolve: resolve
      }
      that._proceed()
    })
    return this._promise
  }

  PromisePool.prototype._fireEvent = function (type, data) {
    this.dispatchEvent(new PromisePoolEvent(this, type, data))
  }

  PromisePool.prototype._settle = function (error) {
    if (error) {
      this._callbacks.reject(error)
    } else {
      this._callbacks.resolve()
    }
    this._promise = null
    this._callbacks = null
  }

  PromisePool.prototype._onPooledPromiseFulfilled = function (promise, result) {
    this._size--
    if (this.active()) {
      this._fireEvent('fulfilled', {
        promise: promise,
        result: result
      })
      this._proceed()
    }
  }

  PromisePool.prototype._onPooledPromiseRejected = function (promise, error) {
    this._size--
    if (this.active()) {
      this._fireEvent('rejected', {
        promise: promise,
        error: error
      })
      this._settle(error || new Error('Unknown error'))
    }
  }

  PromisePool.prototype._trackPromise = function (promise) {
    var that = this
    promise
      .then(function (result) {
        that._onPooledPromiseFulfilled(promise, result)
      }, function (error) {
        that._onPooledPromiseRejected(promise, error)
      })['catch'](function (err) {
        that._settle(new Error('Promise processing failed: ' + err))
      })
  }

  PromisePool.prototype._proceed = function () {
    if (!this._done) {
      var result = null
      while (this._size < this._concurrency &&
          !(result = this._iterator.next()).done) {
        this._size++
        this._trackPromise(result.value)
      }
      this._done = (result === null || !!result.done)
    }
    if (this._done && this._size === 0) {
      this._settle()
    }
  }

  PromisePool.PromisePoolEvent = PromisePoolEvent
  // Legacy API
  PromisePool.PromisePool = PromisePool

  return PromisePool
})
<|endoftext|>


<commit_before>Bokeh.$(function() {
  $('.plotdiv').each(function(index, element){
    var modelId = findModelID(element.id);
    var tableEl = $(element).find(".bk-data-table")[0];
    var dataTableView = findViewObject(tableEl, Bokeh.index[modelId]);
    var fields = getFieldNames(dataTableView.model.attributes.columns, $(element).data("sortingFields"));
    var dataSource = dataTableView.mget("source");

    $(element).append("<form class='column-filters' id="+ element.id +"><input type='submit'></form>")

    for (var i = 0; i < fields.length; i++){
      if (fields[i].field != 'name') {
        var optionsString = optionsConstructor(dataSource, fields[i].field)
        $("form#" + element.id + ".column-filters").append(""+fields[i].title+"<select name=" + fields[i].field + ">"+ optionsString+"</select>")
      };
    }
  })

  $("form.column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }
    var modelId = findModelID(this.id);
    var dataTableView = findViewObject($(document.getElementById(this.id)).find(".bk-data-table")[0], Bokeh.index[modelId])
    var dataSource = dataTableView.mget("source")
    var columns = dataSource.attributes.data
    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })

  $(".plotdiv").on('click', ".bk-ui-state-default.bk-slick-header-column.bk-ui-sortable-handle", function(e){
    if (this.id.match(/slickgrid_\d+_checkbox_selector/) != null) {
      var columnData = $(this).data("column")
      var parentId = $(this).closest(".plotdiv").get(0).id
      var modelid = findModelID(parentId)
      var dataTableView = findViewObject(tableElement(this), Bokeh.index[modelid])
      selectionShift(dataTableView);
    }
  })
})
var findModelID = function(parentId){
  return $.grep(Object.keys(Bokeh.index), function(k){
    return Bokeh.index[k].el.id === parentId
  })
}
// Used to recursively search Bokeh.index
var findViewObject = function(el, currentNode){
  if (el == currentNode.el ) {
    return currentNode;
  }
  else {
    for(key in currentNode.views){
      currentChild = currentNode.views[key]
      result = findViewObject(el, currentChild);
      if (result !== false){
        return result
      }
    }
    return false;
  }
}
// Grabs the top level parent element of the datatable in the DOM
var tableElement = function(el){
  var result;
  $(el).parents().each(function(i){
    if(this.classList.contains("bk-data-table")){
      result = this
    }
  })
  return result
}

var selectionShift = function(view){

  var selectedRowIndices = view.grid.getSelectedRows()
  var data = view.data.getRecords()

  var rowsToShift = collectRowsToShift(data, selectedRowIndices)

  spliceOutRows(data, rowsToShift);

  for(i = 0; i < rowsToShift.length; i++){
    data.unshift(rowsToShift[i])
  }
  updateData(view, data, selectedRowIndices)
}

var collectRowsToShift = function(data, selectedRowIndices){
  var result = []
  for(i = 0; i < selectedRowIndices.length; i++){
    result.push(data.slice(selectedRowIndices[i], (selectedRowIndices[i] + 1))[0])
  }
  return result;
}

var spliceOutRows = function(data, rowsToSplice){
  var attributeMatcher = $(".plotdiv").data().sortingMatcher
  for(i = 0; i < rowsToSplice.length; i++){
    for(j = 0; j < data.length; j++){
      if(data[j][attributeMatcher] === rowsToSplice[i][attributeMatcher]){
        data.splice(j, 1)
      }
    }
  }
}

var updateData = function(view, data, selectedRowIndices){
  for(i = 0; i < data.length; i++){
    view.data._setItem(i, data[i])
  }
  var updateSel = []
  for(i=0; i < selectedRowIndices.length; i++){
    updateSel.push(i)
  }
  view.data.updateSource()
  view.grid.setSelectedRows(updateSel)
}

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      var newRows = []
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] == filterToApply.value){
          newRows.push(rows.slice(j,j+1)[0])
        }
      }
      rows = newRows
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns, sortingFields){
  var result = []
  for(var j = 0; j < sortingFields.length; j++){
    for(var i = 0; i < columns.length; i++){
      var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
      if (columnData.attributes.field == sortingFields[j]) {
      result.push({field : columnData.attributes.field, title : columnData.attributes.title})
      };
    }
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value='"+ elements[i].toString() +"'>" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}<commit_msg>change to relfect latest version of value_sorting.js<commit_after>Bokeh.$(function() {
  linkStyleSheet({'href':"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.6.3/css/bootstrap-select.min.css", 'type':'text/css', 'location':'head', 'rel':'stylesheet'});
  linkScript({'url':"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.6.3/js/bootstrap-select.min.js", 'type':'text/javascript', 'location':'body'});
  $('.plotdiv').each(function(index, element){
    var modelId = findModelID(element.id);
    var tableEl = $(element).find(".bk-data-table")[0];
    var dataTableView = findViewObject(tableEl, Bokeh.index[modelId]);
    var fields = getFieldNames(dataTableView.model.attributes.columns, $(element).data("sortingFields"));
    var dataSource = dataTableView.mget("source");

    $(element).prepend("<form class='column-filters' id="+ element.id +">
<button type='submit' class='btn btn-default'>Sort Data</button>
</form>")

    for (var i = 0; i < fields.length; i++){
      if (fields[i].field != 'name') {
        var optionsString = optionsConstructor(dataSource, fields[i].field)
        $("form#" + element.id + ".column-filters").append("<select class='selectpicker' data-style='btn-primary' title='Sort by "+ fields[i].title +"' name=" + fields[i].field + ">"+ optionsString+"</select>")
      };
    }
  })

  $("form.column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }
    var modelId = findModelID(this.id);
    var dataTableView = findViewObject($(document.getElementById(this.id)).find(".bk-data-table")[0], Bokeh.index[modelId])
    var dataSource = dataTableView.mget("source")
    var columns = dataSource.attributes.data
    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })

  $(".plotdiv").on('click', ".bk-ui-state-default.bk-slick-header-column.bk-ui-sortable-handle", function(e){
    if (this.id.match(/slickgrid_\d+_checkbox_selector/) != null) {
      var columnData = $(this).data("column")
      var parentId = $(this).closest(".plotdiv").get(0).id
      var modelid = findModelID(parentId)
      var dataTableView = findViewObject(tableElement(this), Bokeh.index[modelid])
      selectionShift(dataTableView);
    }
  })
})
var findModelID = function(parentId){
  return $.grep(Object.keys(Bokeh.index), function(k){
    return Bokeh.index[k].el.id === parentId
  })
}
// Used to recursively search Bokeh.index
var findViewObject = function(el, currentNode){
  if (el == currentNode.el ) {
    return currentNode;
  }
  else {
    for(key in currentNode.views){
      currentChild = currentNode.views[key]
      result = findViewObject(el, currentChild);
      if (result !== false){
        return result
      }
    }
    return false;
  }
}
// Grabs the top level parent element of the datatable in the DOM
var tableElement = function(el){
  var result;
  $(el).parents().each(function(i){
    if(this.classList.contains("bk-data-table")){
      result = this
    }
  })
  return result
}

var selectionShift = function(view){

  var selectedRowIndices = view.grid.getSelectedRows()
  var data = view.data.getRecords()

  var rowsToShift = collectRowsToShift(data, selectedRowIndices)

  spliceOutRows(data, rowsToShift);

  for(i = 0; i < rowsToShift.length; i++){
    data.unshift(rowsToShift[i])
  }
  updateData(view, data, selectedRowIndices)
}

var collectRowsToShift = function(data, selectedRowIndices){
  var result = []
  for(i = 0; i < selectedRowIndices.length; i++){
    result.push(data.slice(selectedRowIndices[i], (selectedRowIndices[i] + 1))[0])
  }
  return result;
}

var spliceOutRows = function(data, rowsToSplice){
  var attributeMatcher = $(".plotdiv").data().sortingMatcher
  for(i = 0; i < rowsToSplice.length; i++){
    for(j = 0; j < data.length; j++){
      if(data[j][attributeMatcher] === rowsToSplice[i][attributeMatcher]){
        data.splice(j, 1)
      }
    }
  }
}

var updateData = function(view, data, selectedRowIndices){
  for(i = 0; i < data.length; i++){
    view.data._setItem(i, data[i])
  }
  var updateSel = []
  for(i=0; i < selectedRowIndices.length; i++){
    updateSel.push(i)
  }
  view.data.updateSource()
  view.grid.setSelectedRows(updateSel)
}

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      var newRows = []
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] == filterToApply.value){
          newRows.push(rows.slice(j,j+1)[0])
        }
      }
      rows = newRows
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns, sortingFields){
  var result = []
  for(var j = 0; j < sortingFields.length; j++){
    for(var i = 0; i < columns.length; i++){
      var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
      if (columnData.attributes.field == sortingFields[j]) {
      result.push({field : columnData.attributes.field, title : columnData.attributes.title})
      };
    }
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value='"+ elements[i].toString() +"'>" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}

var linkScript = function(args){
  var script = document.createElement('script');
  script.type = args.type;
  script.src = args.url
  $(args.location).append(script);
}

var linkStyleSheet = function(args){
  var link = document.createElement('link')
  link.type = args.type
  link.rel = args.rel
  link.href = args.href
  $(args.location).append(link)
}<|endoftext|>


<commit_before>(function () { /* Begin of privacy scope */
    "use strict";

    var
        _sources = [
            "base",                 // Basic functions
            "json",                 // JSON compatibility layer         gpf.json
            "events",               // Event management               gpf.events
            "http",                 // HTTP specific functions          gpf.http
            "bin",                  // Binary tools                      gpf.bin
            "class",                // Class
            "attributes",           // Attributes                 gpf.attributes
            "error",                // Error base class
            "att_class",            // Class attributes
            "interface",            // Interface                  gpf.interfaces
            "i_enumerable",         // IEnumerable
            "i_array",              // IArray
            "i_stream",             // ITextStream
            "string",               // String functions
            "date",                 // Date functions

            "parser",               // Parser helper                  gpf.Parser
            "tokenizer",            // Javascript parser                  gpf.js

            "xml",                  // Xml serializer & attributes       gpf.xml
            "xnode",                // Xml 'DOM' structure               gpf.xml
            "xpath"                 // Xml 'XPATH' parser/evaluator      gpf.xml
        ];

    gpf.sources = function () {
        return _sources.join(",");
    };

}()); /* End of privacy scope */
<commit_msg>Problem with loading order<commit_after>(function () { /* Begin of privacy scope */
    "use strict";

    var
        _sources = [
            "base",                 // Basic functions
            "events",               // Event management               gpf.events
            "http",                 // HTTP specific functions          gpf.http
            "bin",                  // Binary tools                      gpf.bin
            "json",                 // JSON compatibility layer         gpf.json
            "class",                // Class
            "attributes",           // Attributes                 gpf.attributes
            "error",                // Error base class
            "att_class",            // Class attributes
            "interface",            // Interface                  gpf.interfaces
            "i_enumerable",         // IEnumerable
            "i_array",              // IArray
            "i_stream",             // ITextStream
            "string",               // String functions
            "date",                 // Date functions

            "parser",               // Parser helper                  gpf.Parser
            "tokenizer",            // Javascript parser                  gpf.js

            "xml",                  // Xml serializer & attributes       gpf.xml
            "xnode",                // Xml 'DOM' structure               gpf.xml
            "xpath"                 // Xml 'XPATH' parser/evaluator      gpf.xml
        ];

    gpf.sources = function () {
        return _sources.join(",");
    };

}()); /* End of privacy scope */
<|endoftext|>


<commit_before>dojo.provide("dojox.charting.Chart2D");

dojo.require("dojox.gfx");
dojo.require("dojox.lang.functional");
dojo.require("dojox.charting.plot2d.axes");
dojo.require("dojox.charting.plot2d.renderers");
dojo.require("dojox.charting.Theme");

(function(){
	var df = dojox.lang.functional, dc = dojox.charting,
		clear = df.lambda("item.clear()"), o = {};

	dojo.declare("dojox.charting.Chart2D", null, {
		constructor: function(node, kwArgs){
			// default initialization
			this.theme = null;
			this.axes = {};
			this.stack = [];
			this.renderers = {};
			this.series = [];
			this.runs = {};

			// create a surface
			this.node = dojo.byId(node);
			var box = dojo.marginBox(node);
			this.surface = dojox.gfx.createSurface(this.node, box.w, box.h);
		},
		setTheme: function(theme){
			this.theme = theme;
			return this;
		},
		addAxis: function(name, kwArgs){
			if(!kwArgs || !("type" in kwArgs)){
				this.axes[name] = new dc.plot2d.axes.Default(kwArgs, this);
			}else{
				this.axes[name] = typeof kwArgs.type == "string" ?
					new dc.plot2d.axes[kwArgs.type](kwArgs, this) :
					new kwArgs.type(kwArgs, this);
			}
			return this;
		},
		addPlot: function(name, kwArgs){
			var renderer;
			if(!kwArgs || !("type" in kwArgs)){
				renderer = new dc.plot2d.renderers.Default(kwArgs, this);
			}else{
				renderer = typeof kwArgs.type == "string" ?
					new dc.plot2d.renderers[kwArgs.type](kwArgs, this) :
					new kwArgs.type(kwArgs, this);
			}
			renderer.name = name;
			if(name in this.renderers){
				this.stack[this.renderers[name]] = renderer;
			}else{
				this.renderers[name] = this.stack.length;
				this.stack.push(renderer);
			}
			return this;
		},
		addSeries: function(name, data, kwArgs){
			var run = {name: name, data: data}, i;
			if(kwArgs){ dojo.mixin(run, kwArgs); }
			if(typeof run.plot != "string"){ run.plot = "default"; }
			if(name in this.runs){
				this.series[this.runs[name]] = run;
			}else{
				this.runs[name] = this.series.length;
				this.series.push(run);
			}
			return this;
		},
		render: function(){
			// clear old values

			dojo.forEach(this.stack, clear);
			this.surface.clear();
			//if(this.theme){ this.theme.clear(); }

			// rebuild new connections, and add defaults

			// assign series
			for(var i = 0; i < this.series.length; ++i){
				var run = this.series[i];
				if(!(run.plot in this.renderers)){
					var renderer = new dc.plot2d.renderers.Default({}, this);
					renderer.name = run.plot;
					this.renderers[run.plot] = this.stack.length;
					this.stack.push(renderer);
				}
				this.stack[this.renderers[run.plot]].addSeries(run);
			}
			// assign axes
			for(var i = 0; i < this.stack.length; ++i){
				var renderer = this.stack[i];
				if("hAxis" in renderer){
					renderer.setAxis(this.axes[renderer.hAxis]);
				}
				if("vAxis" in renderer){
					renderer.setAxis(this.axes[renderer.vAxis]);
				}
			}
			// set up a theme
			if(!this.theme){
				this.theme = new dojox.charting.Theme(dojox.charting._def);
			}
			this.theme.defineColors({num: this.series.length, cache: false});

			// calculate geometry

			var dim = this.surface.getDimensions();
			dim.width  = dojox.gfx.normalizedLength(dim.width);
			dim.height = dojox.gfx.normalizedLength(dim.height);
			df.forIn(this.axes, clear);
			for(var i = 0; i < this.stack.length; ++i){
				var renderer = this.stack[i];
				// use the current dimension as an approximation
				renderer.calculateAxes(dim);
			}
			// assumption: we don't have stacked axes yet
			var offsets = {l: 0, r: 0, t: 0, b: 0};
			for(var i in this.axes){
				if(i in o){ continue; }
				var offs = this.axes[i].getOffsets();
				offsets.l = Math.max(offsets.l, offs.l);
				offsets.r = Math.max(offsets.r, offs.r);
				offsets.t = Math.max(offsets.t, offs.t);
				offsets.b = Math.max(offsets.b, offs.b);
			}
			// add some standard margins
			offsets.l += 10;
			offsets.r += 10;
			offsets.t += 10;
			offsets.b += 10;
			// second run with realistic dimensions
			var plotArea = {width: dim.width - offsets.l - offsets.r, height: dim.height - offsets.t - offsets.b};
			df.forIn(this.axes, clear);
			for(var i = 0; i < this.stack.length; ++i){
				var renderer = this.stack[i];
				renderer.calculateAxes(plotArea);
			}

			// generate shapes

			// draw a chart background
			var t = this.theme;
			if(("chart" in t)){
				if("fill" in t.chart){
					this.surface.createRect({
						width:  dim.width,
						height: dim.height
					}).setFill(t.chart.fill);
				}
				if("stroke" in t.chart){
					this.surface.createRect({
						width:  dim.width - 1,
						height: dim.height - 1
					}).setStroke(t.chart.stroke);
				}
			}
			// draw a plot background
			if(("plotarea" in t)){
				if("fill" in t.plotarea){
					this.surface.createRect({
						x: offsets.l, y: offsets.t,
						width:  dim.width  - offsets.l - offsets.r,
						height: dim.height - offsets.t - offsets.b
					}).setFill(t.plotarea.fill);
				}
				if("stroke" in t.plotarea){
					this.surface.createRect({
						x: offsets.l, y: offsets.t,
						width:  dim.width  - offsets.l - offsets.r - 1,
						height: dim.height - offsets.t - offsets.b - 1
					}).setStroke(t.plotarea.stroke);
				}
			}
			// go over the stack backwards
			for(var i = this.stack.length - 1; i >= 0; --i){
				this.stack[i].render(dim, offsets);
			}
			// go over axes
			for(var i in this.axes){
				if(i in o){ continue; }
				this.axes[i].render(dim, offsets);
			}

			return this;
		}
	});
})();
<commit_msg>charting: cleaning up the chart code to minimize and streamline it.<commit_after>dojo.provide("dojox.charting.Chart2D");

dojo.require("dojox.gfx");
dojo.require("dojox.lang.functional");
dojo.require("dojox.charting.plot2d.axes");
dojo.require("dojox.charting.plot2d.renderers");
dojo.require("dojox.charting.Theme");

(function(){
	var df = dojox.lang.functional, dc = dojox.charting,
		clear = df.lambda("item.clear()");

	dojo.declare("dojox.charting.Chart2D", null, {
		constructor: function(node, kwArgs){
			// initialize parameters
			this.margins = kwArgs && kwArgs.margins ? kwArgs.margins : {l: 10, t: 10, r: 10, b: 10};

			// default initialization
			this.theme = null;
			this.axes = {};
			this.stack = [];
			this.renderers = {};
			this.series = [];
			this.runs = {};

			// create a surface
			this.node = dojo.byId(node);
			var box = dojo.marginBox(node);
			this.surface = dojox.gfx.createSurface(this.node, box.w, box.h);
		},
		setTheme: function(theme){
			this.theme = theme;
			return this;
		},
		addAxis: function(name, kwArgs){
			if(!kwArgs || !("type" in kwArgs)){
				this.axes[name] = new dc.plot2d.axes.Default(kwArgs, this);
			}else{
				this.axes[name] = typeof kwArgs.type == "string" ?
					new dc.plot2d.axes[kwArgs.type](kwArgs, this) :
					new kwArgs.type(kwArgs, this);
			}
			return this;
		},
		addPlot: function(name, kwArgs){
			var renderer;
			if(!kwArgs || !("type" in kwArgs)){
				renderer = new dc.plot2d.renderers.Default(kwArgs, this);
			}else{
				renderer = typeof kwArgs.type == "string" ?
					new dc.plot2d.renderers[kwArgs.type](kwArgs, this) :
					new kwArgs.type(kwArgs, this);
			}
			renderer.name = name;
			if(name in this.renderers){
				this.stack[this.renderers[name]] = renderer;
			}else{
				this.renderers[name] = this.stack.length;
				this.stack.push(renderer);
			}
			return this;
		},
		addSeries: function(name, data, kwArgs){
			var run = {name: name, data: data}, i;
			if(kwArgs){ dojo.mixin(run, kwArgs); }
			if(typeof run.plot != "string"){ run.plot = "default"; }
			if(name in this.runs){
				this.series[this.runs[name]] = run;
			}else{
				this.runs[name] = this.series.length;
				this.series.push(run);
			}
			return this;
		},
		resize: function(width, height){
			var box;
			switch(arguments.length){
				case 0:
					box = dojo.marginBox(this.node);
					break;
				case 1:
					box = width;
					break;
				default:
					box = {w: width, h: height};
					break;
			}
			dojo.marginBox(this.node, box);
			this.surface.setDimensions(box.w, box.h);
			return this.render();
		},
		render: function(){
			// clear old values

			dojo.forEach(this.stack, clear);
			this.surface.clear();
			//if(this.theme){ this.theme.clear(); }

			// rebuild new connections, and add defaults

			// assign series
			dojo.forEach(this.series, function(run){
				if(!(run.plot in this.renderers)){
					var plot = new dc.plot2d.renderers.Default({}, this);
					plot.name = run.plot;
					this.renderers[run.plot] = this.stack.length;
					this.stack.push(plot);
				}
				this.stack[this.renderers[run.plot]].addSeries(run);
			}, this);
			// assign axes
			dojo.forEach(this.stack, function(plot){
				if("hAxis" in plot){
					plot.setAxis(this.axes[plot.hAxis]);
				}
				if("vAxis" in plot){
					plot.setAxis(this.axes[plot.vAxis]);
				}
			}, this);
			// set up a theme
			if(!this.theme){
				this.theme = new dojox.charting.Theme(dojox.charting._def);
			}
			this.theme.defineColors({num: this.series.length, cache: false});

			// calculate geometry

			var dim = this.surface.getDimensions();
			dim.width  = dojox.gfx.normalizedLength(dim.width);
			dim.height = dojox.gfx.normalizedLength(dim.height);
			df.forIn(this.axes, clear);
			dojo.forEach(this.stack, function(plot){ plot.calculateAxes(dim); });
			// assumption: we don't have stacked axes yet
			var offsets = {l: 0, r: 0, t: 0, b: 0};
			df.forIn(this.axes, function(axis){
				df.forIn(axis.getOffsets(), function(o, i){ offsets[i] += o; });
			});
			// add margins
			df.forIn(this.margins, function(o, i){ offsets[i] += o; });
			// second run with realistic dimensions
			var plotArea = {width: dim.width - offsets.l - offsets.r, height: dim.height - offsets.t - offsets.b};
			df.forIn(this.axes, clear);
			dojo.forEach(this.stack, function(plot){ plot.calculateAxes(plotArea); });

			// generate shapes

			// draw a chart background
			var t = this.theme;
			if(("chart" in t)){
				if("fill" in t.chart){
					this.surface.createRect({
						width:  dim.width,
						height: dim.height
					}).setFill(t.chart.fill);
				}
				if("stroke" in t.chart){
					this.surface.createRect({
						width:  dim.width - 1,
						height: dim.height - 1
					}).setStroke(t.chart.stroke);
				}
			}
			// draw a plot background
			if(("plotarea" in t)){
				if("fill" in t.plotarea){
					this.surface.createRect({
						x: offsets.l, y: offsets.t,
						width:  dim.width  - offsets.l - offsets.r,
						height: dim.height - offsets.t - offsets.b
					}).setFill(t.plotarea.fill);
				}
				if("stroke" in t.plotarea){
					this.surface.createRect({
						x: offsets.l, y: offsets.t,
						width:  dim.width  - offsets.l - offsets.r - 1,
						height: dim.height - offsets.t - offsets.b - 1
					}).setStroke(t.plotarea.stroke);
				}
			}
			// go over the stack backwards
			df.foldr(this.stack, function(z, plot){ return plot.render(dim, offsets), 0; }, 0);
			// go over axes
			df.forIn(this.axes, function(axis){ axis.render(dim, offsets); });

			return this;
		}
	});
})();
<|endoftext|>