<commit_before>/*/////////////////////////////////////////////////////////////////////////////////

 MO5.js - JavaScript Library For Building Modern DOM And Canvas Applications

 Copyright (c) 2013 Jonathan Steinbeck
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

 * Neither the name MO5.js nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/////////////////////////////////////////////////////////////////////////////////*/

/* global MO5, document, console */

MO5("MO5.CoreObject", "MO5.transform", "MO5.TimerWatcher", "MO5.dom.effects.typewriter").
define("MO5.dom.Element", function (CoreObject, transform, TimerWatcher, typewriter) {

    function Element (args) {

        var self = this;

        args = args || {};

        CoreObject.call(this);

        this.parent = args.parent || document.body;
        this.nodeType = args.nodeType || "div";
        this.element = args.element || document.createElement(this.nodeType);

        wrapElement(this, this.element);

        Element.propertiesToExclude.forEach(function (property) {
            delete self[property];
        });
    }

    // Properties that should not shadow the DOM element's properties.
    // If you want to add a method with the same name as a DOM element's
    // property to the prototype, you need to add the method's name to this array.
    Element.propertiesToExclude = [
        "appendChild"
    ];

    Element.prototype = new CoreObject();
    Element.prototype.constructor = Element;

    Element.prototype.appendTo = function (element) {
        return element.appendChild(this.element);
    };

    Element.prototype.remove = function () {
        return this.element.parentNode.removeChild(this.element);
    };

    Element.prototype.appendChild = function (child) {
        var node = child instanceof Element ? child.element : child;
        return this.element.appendChild(node);
    };

    Element.prototype.fadeIn = function (args) {

        args = args || {};

        var element = this.element;

        return transform(
            function (v) {
                element.style.opacity = v;
            },
            0,
            1,
            args
        );
    };

    Element.prototype.fadeOut = function (args) {

        args = args || {};

        var element = this.element;

        return transform(
            function (v) {
                element.style.opacity = v;
            },
            1,
            0,
            args
        );
    };

    Element.prototype.moveTo = function (x, y, args) {

        args = args || {};

        var element = this.element,
            ox = element.offsetLeft,
            oy = element.offsetTop,
            t0, t1;

        t0 = transform(
            function (v) {
                element.style.left = v + "px";
            },
            ox,
            x,
            args
        );

        t1 = transform(
            function (v) {
                element.style.top = v + "px";
            },
            oy,
            y,
            args
        );

        return new TimerWatcher().addTimer(t0).addTimer(t1);
    };

    Element.prototype.move = function (x, y, args) {

        args = args || {};

        var element = this.element,
            dx = element.offsetLeft + x,
            dy = element.offsetTop + y;

        return this.moveTo(dx, dy, args);
    };

    Element.prototype.display = function () {

        var parent;

        try {
            parent = this.parent || document.body;
            parent.appendChild(this.element);
        }
        catch (e) {}
    };

    Element.prototype.show = Element.prototype.display;

    Element.prototype.hide = function () {

        var parent;

        try {
            parent = this.parent || document.body;
            parent.removeChild(this.element);
        }
        catch (e) {}
    };

    Element.prototype.typewriter = function (args) {
        args = args || {};
        typewriter(this.element, args);
    };

    Element.prototype.addCssClass = function (classToAdd) {

        var classes;

        if (this.element.classList) {
            this.element.classList.add(classToAdd);
            return this;
        }

        classes = this.getCssClasses();

        if (!contains(classes, classToAdd)) {
            classes.push(classToAdd);
            this.element.setAttribute("class", classes.join(" "));
        }

        return this;
    };

    Element.prototype.removeCssClass = function (classToRemove) {

        var classes;

        if (this.element.classList) {
            this.element.classList.remove(classToRemove);
        }

        classes = this.getCssClasses();

        if (contains(classes, classToRemove)) {
            this.element.setAttribute("class", classes.filter(function (item) {
                return item !== classToRemove;
            }).join(" "));
        }

        return this;
    };

    Element.prototype.getCssClasses = function () {
        return (this.element.getAttribute("class") || "").split(" ");
    };

    Element.prototype.hasCssClass = function (classToCheckFor) {
        return this.element.classList ?
            this.element.classList.contains(classToCheckFor) :
            contains(this.getCssClasses(), classToCheckFor);
    };

    Element.prototype.clearCssClasses = function () {
        this.element.setAttribute("class", "");
        return this;
    };

    Element.prototype.setCssId = function (cssId) {
        this.element.setAttribute("id", cssId);
        return this;
    };

    Element.prototype.destroy = function () {

        try {
            this.element.parentNode.removeChild(this.element);
        }
        catch (e) {
            console.log(e);
        }

        this.element = null;

        this.destroyed = true;
        this.trigger("destroyed", null, false);

        for (var key in this) {
            if (this.hasOwnProperty(key)) {
                delete this[key];
            }
        }

        this.destroyed = true;
    };

    ////////////////////////////////////////
    // dom.Element helper functions
    ////////////////////////////////////////

    function wrapElement (element, domElement) {
        for (var key in domElement) {
            (function (currentProperty, key) {
                if (typeof currentProperty === "function") {
                    element[key] = function () {
                        return domElement[key].apply(domElement, arguments);
                    };
                }
                else {
                    element[key] = function (content) {
                        if (content) {
                            domElement[key] = content;
                            return element;
                        }

                        return domElement[key];
                    };
                }
            }(domElement[key], key));
        }
    }

    function contains (array, item) {
        return array.indexOf(item) !== -1;
    }

    return Element;

});<commit_msg>Added .opacity(), .width(), .height(), .size() and .position() methods to MO5.dom.Element. These methods are getter-setters, that is, given an input, they change something, and they always return their associated value.<commit_after>/*/////////////////////////////////////////////////////////////////////////////////

 MO5.js - JavaScript Library For Building Modern DOM And Canvas Applications

 Copyright (c) 2013 Jonathan Steinbeck
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

 * Neither the name MO5.js nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/////////////////////////////////////////////////////////////////////////////////*/

/* global MO5, document, console */

MO5("MO5.CoreObject", "MO5.transform", "MO5.TimerWatcher", "MO5.dom.effects.typewriter",
        "MO5.types", "MO5.Point", "MO5.Size").
define("MO5.dom.Element", function (CoreObject, transform, TimerWatcher,
        typewriter, types, Point, Size) {

    function Element (args) {

        var self = this;

        args = args || {};

        CoreObject.call(this);

        this.parent = args.parent || document.body;
        this.nodeType = args.nodeType || "div";
        this.element = args.element || document.createElement(this.nodeType);

        wrapElement(this, this.element);

        Element.propertiesToExclude.forEach(function (property) {
            delete self[property];
        });
    }

    // Properties that should not shadow the DOM element's properties.
    // If you want to add a method with the same name as a DOM element's
    // property to the prototype, you need to add the method's name to this array.
    Element.propertiesToExclude = [
        "appendChild"
    ];

    Element.prototype = new CoreObject();
    Element.prototype.constructor = Element;

    Element.prototype.appendTo = function (element) {
        return element.appendChild(this.element);
    };

    Element.prototype.remove = function () {
        return this.element.parentNode.removeChild(this.element);
    };

    Element.prototype.appendChild = function (child) {
        var node = child instanceof Element ? child.element : child;
        return this.element.appendChild(node);
    };

    Element.prototype.fadeIn = function (args) {

        args = args || {};

        var element = this.element;

        return transform(
            function (v) {
                element.style.opacity = v;
            },
            0,
            1,
            args
        );
    };

    Element.prototype.fadeOut = function (args) {

        args = args || {};

        var element = this.element;

        return transform(
            function (v) {
                element.style.opacity = v;
            },
            1,
            0,
            args
        );
    };

    Element.prototype.opacity = function (value) {

        if (typeof value === "number") {
            this.element.style.opacity = value;
        }

        return this.element.style.opacity;
    };

    Element.prototype.position = function (point) {

        if (types.isObject(point)) {
            this.element.style.left = "" + (+point.x) + "px";
            this.element.style.top = "" + (+point.y) + "px";
        }

        return new Point(this.element.offsetLeft, this.element.offsetTop);

    };

    Element.prototype.size = function (size) {

        if (types.isObject(size)) {
            this.element.style.width = "" + size.width + "px";
            this.element.style.height = "" + size.height + "px";
        }

        return new Size(this.element.offsetWidth, this.element.offsetHeight);
    };

    Element.prototype.width = function (width) {

        if (types.isNumber(width)) {
            this.element.style.width = "" + width + "px";
        }

        return this.element.offsetWidth;
    };

    Element.prototype.height = function (height) {

        if (types.isNumber(height)) {
            this.element.style.height = "" + height + "px";
        }

        return this.element.offsetHeight;
    };

    Element.prototype.moveTo = function (x, y, args) {

        args = args || {};

        var element = this.element,
            ox = element.offsetLeft,
            oy = element.offsetTop,
            t0, t1;

        t0 = transform(
            function (v) {
                element.style.left = v + "px";
            },
            ox,
            x,
            args
        );

        t1 = transform(
            function (v) {
                element.style.top = v + "px";
            },
            oy,
            y,
            args
        );

        return new TimerWatcher().addTimer(t0).addTimer(t1);
    };

    Element.prototype.move = function (x, y, args) {

        args = args || {};

        var element = this.element,
            dx = element.offsetLeft + x,
            dy = element.offsetTop + y;

        return this.moveTo(dx, dy, args);
    };

    Element.prototype.display = function () {

        var parent;

        try {
            parent = this.parent || document.body;
            parent.appendChild(this.element);
        }
        catch (e) {}

        this.element.style.visibility = "";
    };

    Element.prototype.show = Element.prototype.display;

    Element.prototype.hide = function () {

        var parent;

        try {
            parent = this.parent || document.body;
            parent.removeChild(this.element);
        }
        catch (e) {}

        this.element.style.visibility = "hidden";
    };

    Element.prototype.typewriter = function (args) {
        args = args || {};
        typewriter(this.element, args);
    };

    Element.prototype.addCssClass = function (classToAdd) {

        var classes;

        if (this.element.classList) {
            this.element.classList.add(classToAdd);
            return this;
        }

        classes = this.getCssClasses();

        if (!contains(classes, classToAdd)) {
            classes.push(classToAdd);
            this.element.setAttribute("class", classes.join(" "));
        }

        return this;
    };

    Element.prototype.removeCssClass = function (classToRemove) {

        var classes;

        if (this.element.classList) {
            this.element.classList.remove(classToRemove);
        }

        classes = this.getCssClasses();

        if (contains(classes, classToRemove)) {
            this.element.setAttribute("class", classes.filter(function (item) {
                return item !== classToRemove;
            }).join(" "));
        }

        return this;
    };

    Element.prototype.getCssClasses = function () {
        return (this.element.getAttribute("class") || "").split(" ");
    };

    Element.prototype.hasCssClass = function (classToCheckFor) {
        return this.element.classList ?
            this.element.classList.contains(classToCheckFor) :
            contains(this.getCssClasses(), classToCheckFor);
    };

    Element.prototype.clearCssClasses = function () {
        this.element.setAttribute("class", "");
        return this;
    };

    Element.prototype.setCssId = function (cssId) {
        this.element.setAttribute("id", cssId);
        return this;
    };

    Element.prototype.destroy = function () {

        try {
            this.element.parentNode.removeChild(this.element);
        }
        catch (e) {
            console.log(e);
        }

        this.element = null;

        this.destroyed = true;
        this.trigger("destroyed", null, false);

        for (var key in this) {
            if (this.hasOwnProperty(key)) {
                delete this[key];
            }
        }

        this.destroyed = true;
    };

    ////////////////////////////////////////
    // dom.Element helper functions
    ////////////////////////////////////////

    function wrapElement (element, domElement) {
        for (var key in domElement) {
            (function (currentProperty, key) {
                if (typeof currentProperty === "function") {
                    element[key] = function () {
                        return domElement[key].apply(domElement, arguments);
                    };
                }
                else {
                    element[key] = function (content) {
                        if (content) {
                            domElement[key] = content;
                            return element;
                        }

                        return domElement[key];
                    };
                }
            }(domElement[key], key));
        }
    }

    function contains (array, item) {
        return array.indexOf(item) !== -1;
    }

    return Element;

});<|endoftext|>


<commit_before>describe("b-hello-world", function() {
    it("should XXX", function() {

    });
});<commit_msg>Add test<commit_after>describe("b-hello-world", function() {
    var hello = document.createElement('b-hello-world');
    document.body.appendChild(hello);

    it("have 'Hello World!' content", function() {
        expect(hello.innerHTML).to.contain("Hello world!");
    });
});<|endoftext|>


<commit_before>// Copyright 2013-2016, University of Colorado Boulder

/**
 * An observable array of items.
 *
 * Because the array is observable, we must be careful about the possibility of concurrent-modification errors.
 * Any time we iterate over the array, we must iterate over a copy, because callback may be modifying the array.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Property = require( 'AXON/Property' );
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Emitter = require( 'AXON/Emitter' );
  var TObservableArray = require( 'AXON/TObservableArray' );
  var Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object[]} [array]
   * @param {Object} [options]
   * @constructor
   */
  function ObservableArray( array, options ) {

    var self = this;

    // Special case that the user supplied options but no array
    if ( array instanceof Object && !(array instanceof Array) ) {
      options = array;
      array = null;
    }

    options = _.extend( {
      allowDuplicates: false, // are duplicate items allowed in the array?
      tandem: Tandem.tandemOptional(),
      phetioValueType: null
    }, options );

    // TODO: Should we require tandems for all ObservableArrays?

    this.allowDuplicates = options.allowDuplicates; // @private

    this._array = array || []; // @private internal, do not access directly
    this._addedListeners = []; // @private listeners called when an item is added
    this._removedListeners = []; // @private listeners called when an item is removed

    this.lengthProperty = new Property( this._array.length ); // @public (read-only) observe this, but don't set it

    // @private Store the initial array, if any, for resetting, see #4
    this.initialArray = array ? array.slice() : [];

    // @private Event stream for signifying begin/end of callbacks
    this.startedCallbacksForItemAddedEmitter = new Emitter( { indicateCallbacks: false } );
    this.endedCallbacksForItemAddedEmitter = new Emitter( { indicateCallbacks: false } );
    this.startedCallbacksForItemRemovedEmitter = new Emitter( { indicateCallbacks: false } );
    this.endedCallbacksForItemRemovedEmitter = new Emitter( { indicateCallbacks: false } );

    options.tandem.supplied && options.tandem.addInstance( this, TObservableArray( options.phetioValueType ) );
    this.disposeObservableArray = function() {
      options.tandem.supplied && options.tandem.removeInstance( self );
    };
  }

  axon.register( 'ObservableArray', ObservableArray );

  return inherit( Object, ObservableArray, {

    // @public
    dispose: function() {
      this.disposeObservableArray();
    },

    /**
     * Restore the array back to its initial state
     * Note: if an item is in the current array and original array, it is removed and added back
     * This may or may not change in the future, see #4
     * @public
     */
    reset: function() {
      for ( var i = 0; i < this._array.length; i++ ) {
        this._fireItemRemoved( this._array[ i ] );
      }
      this._array = this.initialArray.slice();
      for ( i = 0; i < this._array.length; i++ ) {
        this._fireItemAdded( this._array[ i ] );
      }
    },

    // @public
    get length() { return this._array.length; },

    /**
     * Adds a listener that will be notified when an item is added to the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemAddedListener: function( listener ) {
      assert && assert( this._addedListeners.indexOf( listener ) === -1 ); // listener is not already registered
      this._addedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemAddedListener.
     * @param listener
     * @public
     */
    removeItemAddedListener: function( listener ) {
      var index = this._addedListeners.indexOf( listener );
      assert && assert( index !== -1 ); // listener is registered
      this._addedListeners.splice( index, 1 );
    },

    /**
     * Adds a listener that will be notified when an item is removed from the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemRemovedListener: function( listener ) {
      assert && assert( this._removedListeners.indexOf( listener ) === -1, 'Listener was already registered' ); // listener is not already registered
      this._removedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemRemovedListener.
     * @param listener
     * @public
     */
    removeItemRemovedListener: function( listener ) {
      var index = this._removedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is still registered after removal' ); // listener is registered
      this._removedListeners.splice( index, 1 );
    },

    // @private called when an item is added.
    _fireItemAdded: function( item ) {
      this.startedCallbacksForItemAddedEmitter.emit1( item );

      //Signify that an item was added to the list
      var copy = this._addedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( var i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.endedCallbacksForItemAddedEmitter.emit();
    },

    // @private called when an item is removed.
    _fireItemRemoved: function( item ) {

      this.startedCallbacksForItemRemovedEmitter.emit1( item );

      //Signify that an item was removed from the list
      var copy = this._removedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( var i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.endedCallbacksForItemRemovedEmitter.emit();
    },

    /**
     * Adds an item to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param item
     * @public
     */
    add: function( item ) {
      this.push( item );
    },

    /**
     * Add items to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param {Array} items
     * @public
     */
    addAll: function( items ) {
      for ( var i = 0; i < items.length; i++ ) {
        this.add( items[ i ] );
      }
    },

    /**
     * Removes the first occurrence of an item from the array.
     * If duplicates are allowed (see options.allowDuplicates) you may need to call this multiple
     * times to totally purge item from the array.
     * @param item
     * @public
     */
    remove: function( item ) {
      var index = this._array.indexOf( item );
      if ( index !== -1 ) {
        this._array.splice( index, 1 );
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
    },

    /**
     * Removes the first occurrence of each item in the specified array.
     * @param {Array} array a list of items to remove
     * @see ObservableArray.remove
     * @public
     */
    removeAll: function( array ) {
      assert && assert( _.isArray( array ), 'array should be an array' );
      for ( var i = 0; i < array.length; i++ ) {
        this.remove( array[ i ] );
      }
    },

    /**
     * Pushes an item onto the end of the array.
     * @param item
     * @throws Error if duplicates are not allowed (see options.allowDuplicates) and item is already in the array
     * @public
     */
    push: function( item ) {
      if ( !this.allowDuplicates && this.contains( item ) ) {
        throw new Error( 'duplicates are not allowed' );
      }
      this._array.push( item );
      this.lengthProperty.set( this._array.length );
      this._fireItemAdded( item );
    },

    /**
     * Removes an item from the end of the array and returns it.
     * @returns {*}
     * @public
     */
    pop: function() {
      var item = this._array.pop();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Removes an item from the beginning of the array and returns it.
     * @returns {*}
     * @public
     */
    shift: function() {
      var item = this._array.shift();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Does the array contain the specified item?
     * @param item
     * @returns {boolean}
     * @public
     */
    contains: function( item ) {
      return this.indexOf( item ) !== -1;
    },

    /**
     * Gets an item at the specified index.
     * @param index
     * @returns {*} the item, or undefined if there is no item at the specified index
     * @public
     */
    get: function( index ) {
      return this._array[ index ];
    },

    /**
     * Gets the index of a specified item.
     * @param item
     * @returns {*} -1 if item is not in the array
     * @public
     */
    indexOf: function( item ) {
      return this._array.indexOf( item );
    },

    /**
     * Removes all items from the array.
     * @public
     */
    clear: function() {
      while ( this.length > 0 ) {
        this.pop();
      }
    },

    /**
     * Applies a callback function to each item in the array
     * @param callback function(item)
     * @public
     */
    forEach: function( callback ) {
      this._array.slice().forEach( callback ); // do this on a copy of the array, in case callbacks involve array modification
    },

    /**
     * Maps the values in this ObservableArray using the specified function, and returns a new ObservableArray for chaining.
     * @param mapFunction
     * @returns {ObservableArray}
     * @public
     */
    map: function( mapFunction ) {
      return new ObservableArray( this._array.map( mapFunction ) );
    },

    /**
     * Filters the values in this ObservableArray using the predicate function, and returns a new ObservableArray for chaining.
     * @param predicate
     * @returns {ObservableArray}
     * @public
     */
    filter: function( predicate ) {
      return new ObservableArray( this._array.filter( predicate ) );
    },

    /**
     * Count the number of items in this ObservableArray that satisfy the given Predicate.
     * @param {function} predicate
     * @returns {number}
     * @public
     */
    count: function( predicate ) {
      var count = 0;
      for ( var i = 0; i < this._array.length; i++ ) {
        if ( predicate( this._array[ i ] ) ) {
          count++;
        }
      }
      return count;
    },

    /**
     * Find the first element that matches the given predicate.
     * @param {function} predicate
     * @param {number} [fromIndex] - optional start index for the search
     */
    find: function( predicate, fromIndex ) {
      return _.find( this._array, predicate, fromIndex );
    },

    /**
     * Starting with the initial value, combine values from this ObservableArray to come up with a composite result.
     * Same as foldLeft.  In underscore this is called _.reduce aka _.fold or _.inject
     * @param value
     * @param combiner
     * @returns {*}
     * @public
     */
    reduce: function( value, combiner ) {
      for ( var i = 0; i < this._array.length; i++ ) {
        value = combiner( value, this._array[ i ] );
      }
      return value;
    },

    /**
     * Return the underlying array
     * @returns {Array}
     * @public
     */
    getArray: function() {
      return this._array;
    },

    /**
     * Add/remove elements from any point in the array
     * @param {number} start - the index to start adding/removing items
     * @param {number} deleteCount - the number of items to delete
     * @param {Object} [item1] - an item to add
     * @param {Object} [item2] - an item to add
     * @param {Object} [etc] - varargs items to add etc.
     * @returns {Object[]} the items that were deleted.
     * @public
     */
    splice: function( start, deleteCount, item1, item2, etc ) {
      var deleted = this._array.splice.apply( this._array, arguments );
      var args = Array.prototype.slice.call( arguments );
      for ( var i = 0; i < deleted.length; i++ ) {
        this._fireItemRemoved( deleted[ i ] );
      }

      for ( var k = 2; k < args.length; k++ ) {
        this._fireItemAdded( args[ k ] );
      }
      return deleted;
    },

    /**
     * Changes the ordering of elements in the array.  Requires a Random source so that shuffles can be reproducible.
     * No items are added or removed, and this method does not send out any notifications.
     * @param {Random} random - from dot
     */
    shuffle: function( random ) {
      assert && assert( random, 'random must be supplied' );

      // preserve the same _array reference in case any clients got a reference to it with getArray()
      var shuffled = random.shuffle( this._array );
      this._array.length = 0;
      Array.prototype.push.apply( this._array, shuffled );
    }
  } );
} );<commit_msg>add phetioIncludeInState to instance, https://github.com/phetsims/forces-and-motion-basics/issues/232<commit_after>// Copyright 2013-2016, University of Colorado Boulder

/**
 * An observable array of items.
 *
 * Because the array is observable, we must be careful about the possibility of concurrent-modification errors.
 * Any time we iterate over the array, we must iterate over a copy, because callback may be modifying the array.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Property = require( 'AXON/Property' );
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Emitter = require( 'AXON/Emitter' );
  var TObservableArray = require( 'AXON/TObservableArray' );
  var Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object[]} [array]
   * @param {Object} [options]
   * @constructor
   */
  function ObservableArray( array, options ) {

    var self = this;

    // Special case that the user supplied options but no array
    if ( array instanceof Object && !(array instanceof Array) ) {
      options = array;
      array = null;
    }

    options = _.extend( {
      allowDuplicates: false, // are duplicate items allowed in the array?
      tandem: Tandem.tandemOptional(),
      phetioValueType: null,
      phetioIncludeInState: false // keep ObservableArray out of the state unless they opt in.
    }, options );

    // TODO: Should we require tandems for all ObservableArrays?

    this.allowDuplicates = options.allowDuplicates; // @private

    this._array = array || []; // @private internal, do not access directly
    this._addedListeners = []; // @private listeners called when an item is added
    this._removedListeners = []; // @private listeners called when an item is removed

    this.lengthProperty = new Property( this._array.length ); // @public (read-only) observe this, but don't set it

    // @private Store the initial array, if any, for resetting, see #4
    this.initialArray = array ? array.slice() : [];

    // @private Event stream for signifying begin/end of callbacks
    this.startedCallbacksForItemAddedEmitter = new Emitter( { indicateCallbacks: false } );
    this.endedCallbacksForItemAddedEmitter = new Emitter( { indicateCallbacks: false } );
    this.startedCallbacksForItemRemovedEmitter = new Emitter( { indicateCallbacks: false } );
    this.endedCallbacksForItemRemovedEmitter = new Emitter( { indicateCallbacks: false } );

    // public (phet-io) (read-only)
    this.phetioIncludeInState = options.phetioIncludeInState;
    options.tandem.supplied && options.tandem.addInstance( this, TObservableArray( options.phetioValueType ) );
    this.disposeObservableArray = function() {
      options.tandem.supplied && options.tandem.removeInstance( self );
    };
  }

  axon.register( 'ObservableArray', ObservableArray );

  return inherit( Object, ObservableArray, {

    // @public
    dispose: function() {
      this.disposeObservableArray();
    },

    /**
     * Restore the array back to its initial state
     * Note: if an item is in the current array and original array, it is removed and added back
     * This may or may not change in the future, see #4
     * @public
     */
    reset: function() {
      for ( var i = 0; i < this._array.length; i++ ) {
        this._fireItemRemoved( this._array[ i ] );
      }
      this._array = this.initialArray.slice();
      for ( i = 0; i < this._array.length; i++ ) {
        this._fireItemAdded( this._array[ i ] );
      }
    },

    // @public
    get length() { return this._array.length; },

    /**
     * Adds a listener that will be notified when an item is added to the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemAddedListener: function( listener ) {
      assert && assert( this._addedListeners.indexOf( listener ) === -1 ); // listener is not already registered
      this._addedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemAddedListener.
     * @param listener
     * @public
     */
    removeItemAddedListener: function( listener ) {
      var index = this._addedListeners.indexOf( listener );
      assert && assert( index !== -1 ); // listener is registered
      this._addedListeners.splice( index, 1 );
    },

    /**
     * Adds a listener that will be notified when an item is removed from the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemRemovedListener: function( listener ) {
      assert && assert( this._removedListeners.indexOf( listener ) === -1, 'Listener was already registered' ); // listener is not already registered
      this._removedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemRemovedListener.
     * @param listener
     * @public
     */
    removeItemRemovedListener: function( listener ) {
      var index = this._removedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is still registered after removal' ); // listener is registered
      this._removedListeners.splice( index, 1 );
    },

    // @private called when an item is added.
    _fireItemAdded: function( item ) {
      this.startedCallbacksForItemAddedEmitter.emit1( item );

      //Signify that an item was added to the list
      var copy = this._addedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( var i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.endedCallbacksForItemAddedEmitter.emit();
    },

    // @private called when an item is removed.
    _fireItemRemoved: function( item ) {

      this.startedCallbacksForItemRemovedEmitter.emit1( item );

      //Signify that an item was removed from the list
      var copy = this._removedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( var i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.endedCallbacksForItemRemovedEmitter.emit();
    },

    /**
     * Adds an item to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param item
     * @public
     */
    add: function( item ) {
      this.push( item );
    },

    /**
     * Add items to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param {Array} items
     * @public
     */
    addAll: function( items ) {
      for ( var i = 0; i < items.length; i++ ) {
        this.add( items[ i ] );
      }
    },

    /**
     * Removes the first occurrence of an item from the array.
     * If duplicates are allowed (see options.allowDuplicates) you may need to call this multiple
     * times to totally purge item from the array.
     * @param item
     * @public
     */
    remove: function( item ) {
      var index = this._array.indexOf( item );
      if ( index !== -1 ) {
        this._array.splice( index, 1 );
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
    },

    /**
     * Removes the first occurrence of each item in the specified array.
     * @param {Array} array a list of items to remove
     * @see ObservableArray.remove
     * @public
     */
    removeAll: function( array ) {
      assert && assert( _.isArray( array ), 'array should be an array' );
      for ( var i = 0; i < array.length; i++ ) {
        this.remove( array[ i ] );
      }
    },

    /**
     * Pushes an item onto the end of the array.
     * @param item
     * @throws Error if duplicates are not allowed (see options.allowDuplicates) and item is already in the array
     * @public
     */
    push: function( item ) {
      if ( !this.allowDuplicates && this.contains( item ) ) {
        throw new Error( 'duplicates are not allowed' );
      }
      this._array.push( item );
      this.lengthProperty.set( this._array.length );
      this._fireItemAdded( item );
    },

    /**
     * Removes an item from the end of the array and returns it.
     * @returns {*}
     * @public
     */
    pop: function() {
      var item = this._array.pop();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Removes an item from the beginning of the array and returns it.
     * @returns {*}
     * @public
     */
    shift: function() {
      var item = this._array.shift();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Does the array contain the specified item?
     * @param item
     * @returns {boolean}
     * @public
     */
    contains: function( item ) {
      return this.indexOf( item ) !== -1;
    },

    /**
     * Gets an item at the specified index.
     * @param index
     * @returns {*} the item, or undefined if there is no item at the specified index
     * @public
     */
    get: function( index ) {
      return this._array[ index ];
    },

    /**
     * Gets the index of a specified item.
     * @param item
     * @returns {*} -1 if item is not in the array
     * @public
     */
    indexOf: function( item ) {
      return this._array.indexOf( item );
    },

    /**
     * Removes all items from the array.
     * @public
     */
    clear: function() {
      while ( this.length > 0 ) {
        this.pop();
      }
    },

    /**
     * Applies a callback function to each item in the array
     * @param callback function(item)
     * @public
     */
    forEach: function( callback ) {
      this._array.slice().forEach( callback ); // do this on a copy of the array, in case callbacks involve array modification
    },

    /**
     * Maps the values in this ObservableArray using the specified function, and returns a new ObservableArray for chaining.
     * @param mapFunction
     * @returns {ObservableArray}
     * @public
     */
    map: function( mapFunction ) {
      return new ObservableArray( this._array.map( mapFunction ) );
    },

    /**
     * Filters the values in this ObservableArray using the predicate function, and returns a new ObservableArray for chaining.
     * @param predicate
     * @returns {ObservableArray}
     * @public
     */
    filter: function( predicate ) {
      return new ObservableArray( this._array.filter( predicate ) );
    },

    /**
     * Count the number of items in this ObservableArray that satisfy the given Predicate.
     * @param {function} predicate
     * @returns {number}
     * @public
     */
    count: function( predicate ) {
      var count = 0;
      for ( var i = 0; i < this._array.length; i++ ) {
        if ( predicate( this._array[ i ] ) ) {
          count++;
        }
      }
      return count;
    },

    /**
     * Find the first element that matches the given predicate.
     * @param {function} predicate
     * @param {number} [fromIndex] - optional start index for the search
     */
    find: function( predicate, fromIndex ) {
      return _.find( this._array, predicate, fromIndex );
    },

    /**
     * Starting with the initial value, combine values from this ObservableArray to come up with a composite result.
     * Same as foldLeft.  In underscore this is called _.reduce aka _.fold or _.inject
     * @param value
     * @param combiner
     * @returns {*}
     * @public
     */
    reduce: function( value, combiner ) {
      for ( var i = 0; i < this._array.length; i++ ) {
        value = combiner( value, this._array[ i ] );
      }
      return value;
    },

    /**
     * Return the underlying array
     * @returns {Array}
     * @public
     */
    getArray: function() {
      return this._array;
    },

    /**
     * Add/remove elements from any point in the array
     * @param {number} start - the index to start adding/removing items
     * @param {number} deleteCount - the number of items to delete
     * @param {Object} [item1] - an item to add
     * @param {Object} [item2] - an item to add
     * @param {Object} [etc] - varargs items to add etc.
     * @returns {Object[]} the items that were deleted.
     * @public
     */
    splice: function( start, deleteCount, item1, item2, etc ) {
      var deleted = this._array.splice.apply( this._array, arguments );
      var args = Array.prototype.slice.call( arguments );
      for ( var i = 0; i < deleted.length; i++ ) {
        this._fireItemRemoved( deleted[ i ] );
      }

      for ( var k = 2; k < args.length; k++ ) {
        this._fireItemAdded( args[ k ] );
      }
      return deleted;
    },

    /**
     * Changes the ordering of elements in the array.  Requires a Random source so that shuffles can be reproducible.
     * No items are added or removed, and this method does not send out any notifications.
     * @param {Random} random - from dot
     */
    shuffle: function( random ) {
      assert && assert( random, 'random must be supplied' );

      // preserve the same _array reference in case any clients got a reference to it with getArray()
      var shuffled = random.shuffle( this._array );
      this._array.length = 0;
      Array.prototype.push.apply( this._array, shuffled );
    }
  } );
} );<|endoftext|>


<commit_before>"use strict";

// console color
const red = "\u001b[31m";
const reset = "\u001b[0m";

// environment
const { TARGET_URL } = process.env;

/** @type {import("nightwatch").NightwatchTestFunctions} */
module.exports = {
  /** @param {import("nightwatch").NightwatchBrowser} client */
  async ["Browser Test"](client) {
    const elements = await client.url(
      TARGET_URL || "http://127.0.0.1:8000/power.html",
    )
      .waitForElementPresent("#mocha-report .suite:nth-of-type(4)", 30000)
      .findElements("#mocha-report .test .error");

    const result = elements.value;
    const success = result == null;

    client.verify.ok(success, "Check error elements");
    if (success) {
      return;
    }

    // show error log
    for (const element of result) {
      const id = element.getId();
      const { value: error } = await client.elementIdText(id);
      client.verify.ok(false, `\n\n${red}${error}${reset}\n\n`);
    }
  },

  afterEach(client, done) {
    client.end();
    client.endSauce(done);
  },
};
<commit_msg>Catch NoSuchElementError<commit_after>"use strict";

// console color
const red = "\u001b[31m";
const reset = "\u001b[0m";

// environment
const { TARGET_URL } = process.env;

/** @type {import("nightwatch").NightwatchTestFunctions} */
module.exports = {
  /** @param {import("nightwatch").NightwatchBrowser} client */
  async ["Browser Test"](client) {
    let elements = null;
    let success = false;

    try {
      elements = await client.url(
        TARGET_URL || "http://127.0.0.1:8000/power.html",
      )
        .waitForElementPresent("#mocha-report .suite:nth-of-type(4)", 30000)
        .findElements("#mocha-report .test .error");
    } catch (e) {
      if (e.name === "NoSuchElementError") {
        success = true;
      } else {
        // unexpected error
        throw e;
      }
    }

    client.verify.ok(success, "Check error elements");
    if (success) {
      return;
    }

    // show error log
    const result = elements.value;
    for (const element of result) {
      const id = element.getId();
      const { value: error } = await client.elementIdText(id);
      client.verify.ok(false, `\n\n${red}${error}${reset}\n\n`);
    }
  },

  afterEach(client, done) {
    client.end();
    client.endSauce(done);
  },
};
<|endoftext|>


<commit_before>// Copyright 2019, University of Colorado Boulder

/**
 * IO type for PhetioCapsule.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ObjectIO = require( 'TANDEM/types/ObjectIO' );
  const tandemNamespace = require( 'TANDEM/tandemNamespace' );

  // constants
  const GROUP_VALIDATOR = {
    isValidValue: v => {
      const PhetioGroup = window.phet ? phet.tandem.PhetioCapsule : tandemNamespace.PhetioCapsule;
      return v instanceof PhetioGroup;
    }
  };

  // {Object.<parameterTypeName:string, function(new:ObjectIO)>} - cache each parameterized PropertyIO so that it is
  // only created once.
  const cache = {};

  /**
   * Parametric IO type constructor.  Given an element type, this function returns a PhetioCapsule IO type.
   * @param {function(new:ObjectIO)} parameterType
   * @returns {function(new:ObjectIO)}
   * @constructor
   */
  function PhetioCapsuleIO( parameterType ) {

    assert && assert( typeof parameterType === 'function', 'element type should be defined' );

    if ( !cache.hasOwnProperty( parameterType.typeName ) ) {
      cache[ parameterType.typeName ] = create( parameterType );
    }

    return cache[ parameterType.typeName ];
  }

  /**
   * Creates a PhetioCapsuleIOImpl
   * @param {function(new:ObjectIO)} parameterType
   * @returns {function(new:ObjectIO)}
   */
  const create = parameterType => {

    class PhetioCapsuleIOImpl extends ObjectIO {

      /**
       * Creates the singleton's instance.
       * @param {PhetioCapsule} singleton
       * @param {string} componentName
       * @param {Object} stateObject
       * @returns {PhetioObject}
       * @throws CouldNotYetDeserializeError - if it could not yet deserialize
       */
      static addChildInstance( singleton, componentName, stateObject ) {

        // should throw CouldNotYetDeserializeError if it can't be created yet. Likely that would be because another
        // element in the state needs to be created first, so we will try again on the next iteration of the state
        // setting engine.
        const state = parameterType.fromStateObject( stateObject );
        const args = parameterType.stateToArgs( state );

        return singleton.create( ...args );
      }

      /**
       * @public (phet-io state)
       * @param {PhetioCapsule} singleton
       */
      static clearChildInstances( singleton ) {
        if ( singleton.instance ) {
          singleton.disposeInstance();
        }
      }
    }

    PhetioCapsuleIOImpl.documentation = 'An array that sends notifications when its values have changed.';
    PhetioCapsuleIOImpl.validator = GROUP_VALIDATOR;
    PhetioCapsuleIOImpl.typeName = `PhetioCapsuleIO<${parameterType.typeName}>`;
    PhetioCapsuleIOImpl.parameterTypes = [ parameterType ];
    ObjectIO.validateSubtype( PhetioCapsuleIOImpl );

    return PhetioCapsuleIOImpl;
  };

  return tandemNamespace.register( 'PhetioCapsuleIO', PhetioCapsuleIO );
} );

<commit_msg>rename singleton -> capsule, https://github.com/phetsims/phet-io/issues/1567<commit_after>// Copyright 2019, University of Colorado Boulder

/**
 * IO type for PhetioCapsule.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ObjectIO = require( 'TANDEM/types/ObjectIO' );
  const tandemNamespace = require( 'TANDEM/tandemNamespace' );

  // constants
  const GROUP_VALIDATOR = {
    isValidValue: v => {
      const PhetioGroup = window.phet ? phet.tandem.PhetioCapsule : tandemNamespace.PhetioCapsule;
      return v instanceof PhetioGroup;
    }
  };

  // {Object.<parameterTypeName:string, function(new:ObjectIO)>} - cache each parameterized PropertyIO so that it is
  // only created once.
  const cache = {};

  /**
   * Parametric IO type constructor.  Given an element type, this function returns a PhetioCapsule IO type.
   * @param {function(new:ObjectIO)} parameterType
   * @returns {function(new:ObjectIO)}
   * @constructor
   */
  function PhetioCapsuleIO( parameterType ) {

    assert && assert( typeof parameterType === 'function', 'element type should be defined' );

    if ( !cache.hasOwnProperty( parameterType.typeName ) ) {
      cache[ parameterType.typeName ] = create( parameterType );
    }

    return cache[ parameterType.typeName ];
  }

  /**
   * Creates a PhetioCapsuleIOImpl
   * @param {function(new:ObjectIO)} parameterType
   * @returns {function(new:ObjectIO)}
   */
  const create = parameterType => {

    class PhetioCapsuleIOImpl extends ObjectIO {

      /**
       * Creates the capsule's instance.
       * @param {PhetioCapsule} capsule
       * @param {string} componentName
       * @param {Object} stateObject
       * @returns {PhetioObject}
       * @throws CouldNotYetDeserializeError - if it could not yet deserialize
       */
      static addChildInstance( capsule, componentName, stateObject ) {

        // should throw CouldNotYetDeserializeError if it can't be created yet. Likely that would be because another
        // element in the state needs to be created first, so we will try again on the next iteration of the state
        // setting engine.
        const state = parameterType.fromStateObject( stateObject );
        const args = parameterType.stateToArgs( state );

        return capsule.create( ...args );
      }

      /**
       * @public (phet-io state)
       * @param {PhetioCapsule} capsule
       */
      static clearChildInstances( capsule ) {
        if ( capsule.instance ) {
          capsule.disposeInstance();
        }
      }
    }

    PhetioCapsuleIOImpl.documentation = 'An array that sends notifications when its values have changed.';
    PhetioCapsuleIOImpl.validator = GROUP_VALIDATOR;
    PhetioCapsuleIOImpl.typeName = `PhetioCapsuleIO<${parameterType.typeName}>`;
    PhetioCapsuleIOImpl.parameterTypes = [ parameterType ];
    ObjectIO.validateSubtype( PhetioCapsuleIOImpl );

    return PhetioCapsuleIOImpl;
  };

  return tandemNamespace.register( 'PhetioCapsuleIO', PhetioCapsuleIO );
} );

<|endoftext|>


<commit_before>var tape = require("tape"),
    polygon = require("../");

tape("polygonCentroid(points) returns the expected value for closed counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), [.5, .5]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for closed clockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]), [.5, .5]);
  test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3], [1, 1]]), [2, 2]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for open counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0]]), [.5, .5]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for closed counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1]]), [.5, .5]);
  test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3]]), [2, 2]);
  test.end();
});

tape("polygonCentroid(polygon) returns the expected value for a very large polygon", function(test) {
  var stop = 1e8,
      step = 1e4,
      points = [],
      value;
  for (value = 0; value < stop; value += step) points.push([0, value]);
  for (value = 0; value < stop; value += step) points.push([value, stop]);
  for (value = stop - step; value >= 0; value -= step) points.push([stop, value]);
  for (value = stop - step; value >= 0; value -= step) points.push([value, 0]);
  test.deepEqual(polygon.polygonCentroid(points), [49999999.75000187, 49999999.75001216]);
  test.end();
});
<commit_msg>fix test description<commit_after>var tape = require("tape"),
    polygon = require("../");

tape("polygonCentroid(points) returns the expected value for closed counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), [.5, .5]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for closed clockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]), [.5, .5]);
  test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3], [1, 1]]), [2, 2]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for open counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0]]), [.5, .5]);
  test.end();
});

tape("polygonCentroid(points) returns the expected value for open counterclockwise polygons", function(test) {
  test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1]]), [.5, .5]);
  test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3]]), [2, 2]);
  test.end();
});

tape("polygonCentroid(polygon) returns the expected value for a very large polygon", function(test) {
  var stop = 1e8,
      step = 1e4,
      points = [],
      value;
  for (value = 0; value < stop; value += step) points.push([0, value]);
  for (value = 0; value < stop; value += step) points.push([value, stop]);
  for (value = stop - step; value >= 0; value -= step) points.push([stop, value]);
  for (value = stop - step; value >= 0; value -= step) points.push([value, 0]);
  test.deepEqual(polygon.polygonCentroid(points), [49999999.75000187, 49999999.75001216]);
  test.end();
});
<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * Deploys a dev version after incrementing the test version number.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
/* eslint-env node */
'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line
const assert = require( 'assert' );
const booleanPrompt = require( '../common/booleanPrompt' );
const build = require( '../common/build' );
const buildLocal = require( '../common/buildLocal' );
const devDirectoryExists = require( '../common/devDirectoryExists' );
const devScp = require( '../common/devScp' );
const devSsh = require( '../common/devSsh' );
const getBranch = require( '../common/getBranch' );
const getRepoVersion = require( '../common/getRepoVersion' );
const gitIsClean = require( '../common/gitIsClean' );
const gitPush = require( '../common/gitPush' );
const grunt = require( 'grunt' );
const lintAllRunnable = require( '../common/lintAllRunnable' );
const npmUpdate = require( '../common/npmUpdate' );
const setRepoVersion = require( '../common/setRepoVersion' );
const SimVersion = require( '../common/SimVersion' );
const updateDependenciesJSON = require( '../common/updateDependenciesJSON' );
const writePhetioHtaccess = require( '../common/writePhetioHtaccess' );

/**
 * Deploys a dev version after incrementing the test version number.
 * @public
 *
 * @param {string} repo
 * @param {Array.<string>} brands
 * @param {boolean} noninteractive
 * @param {string} branch - 'master' for normal dev deploys, otherwise is the name of a one-off branch
 * @param {string} [message] - Optional message to append to the version-increment commit.
 * @returns {Promise}
 */
module.exports = async function( repo, brands, noninteractive, branch, message ) {
  const isOneOff = branch !== 'master';
  const testType = isOneOff ? branch : 'dev';
  if ( isOneOff ) {
    assert( !branch.includes( '-' ), 'One-off versions should be from branches that do not include hyphens' );
  }

  const currentBranch = await getBranch( repo );
  if ( currentBranch !== branch ) {
    grunt.fail.fatal( `${testType} deployment should be on the branch ${branch}, not: ` + ( currentBranch ? currentBranch : '(detached head)' ) );
  }

  const previousVersion = await getRepoVersion( repo );

  if ( previousVersion.testType !== testType ) {
    if ( isOneOff ) {
      grunt.fail.fatal( `The current version identifier is not a one-off version (should be something like ${previousVersion.major}.${previousVersion.minor}.${previousVersion.maintenance}-${testType}.${previousVersion.testNumber === null ? '0' : previousVersion.testNumber}), aborting.` );
    }
    else {
      grunt.fail.fatal( 'The current version identifier is not a dev version, aborting.' );
    }
  }

  const isClean = await gitIsClean( repo );
  if ( !isClean ) {
    throw new Error( `Unclean status in ${repo}, cannot deploy` );
  }

  // Ensure we don't try to request an unsupported brand
  const supportedBrands = grunt.file.readJSON( `../${repo}/package.json` ).phet.supportedBrands;
  brands.forEach( brand => assert( supportedBrands.includes( brand ), `Brand ${brand} not included in ${repo}'s supported brands: ${supportedBrands.join( ',' )}` ) );

  // Ensure that the repository and its dependencies pass lint before continuing.
  // See https://github.com/phetsims/perennial/issues/76
  await lintAllRunnable( repo );

  // Bump the version
  const version = new SimVersion( previousVersion.major, previousVersion.minor, previousVersion.maintenance, {
    testType: testType,
    testNumber: previousVersion.testNumber + 1
  } );

  const versionString = version.toString();
  const simPath = buildLocal.devDeployPath + repo;
  const versionPath = simPath + '/' + versionString;

  const simPathExists = await devDirectoryExists( simPath );
  const versionPathExists = await devDirectoryExists( versionPath );

  if ( versionPathExists ) {
    grunt.fail.fatal( `Directory ${versionPath} already exists.  If you intend to replace the content then remove the directory manually from ${buildLocal.devDeployServer}.` );
  }

  if ( !await booleanPrompt( `Deploy ${versionString} to ${buildLocal.devDeployServer}`, noninteractive ) ) {
    grunt.fail.fatal( `Aborted ${testType} deploy` );
  }

  await setRepoVersion( repo, version, message );
  await gitPush( repo, branch );

  // Make sure our correct npm dependencies are set
  await npmUpdate( repo );
  await npmUpdate( 'chipper' );

  grunt.log.writeln( await build( repo, {
    brands,
    allHTML: true,
    debugHTML: true
  } ) );

  // Create (and fix permissions for) the main simulation directory, if it didn't already exist
  if ( !simPathExists ) {
    await devSsh( `mkdir -p "${simPath}" && echo "IndexOrderDefault Descending Date\n" > "${simPath}/.htaccess"` );
    // await devSsh( `chmod -R g+w "${simPath}"` );
  }

  // Create the version-specific directory
  await devSsh( `mkdir -p "${versionPath}"` );

  // Copy the build contents into the version-specific directory
  for ( let brand of brands ) {
    await devScp( `../${repo}/build/${brand}`, `${versionPath}/` );
  }

  // If there is a protected directory and we are copying to the dev server, include the .htaccess file
  // This is for PhET-iO simulations, to protected the password protected wrappers, see
  // https://github.com/phetsims/phet-io/issues/641
  if ( brands.includes( 'phet-io' ) && buildLocal.devDeployServer === 'bayes.colorado.edu' ) {
    const htaccessLocation = `../${repo}/build/phet-io/.htaccess`;
    await writePhetioHtaccess( htaccessLocation );
    await devScp( htaccessLocation, `${versionPath}/phet-io/wrappers/.htaccess` );
  }

  // Permissions fixes so others can write over it later
  await devSsh( `chmod -R g+w "${versionPath}"` );
  await devSsh( `chown -R :phet "${versionPath}"` );

  // Move over dependencies.json and commit/push
  await updateDependenciesJSON( repo, brands, versionString, branch );

  const versionURL = `https://phet-dev.colorado.edu/html/${repo}/${versionString}`;

  if ( brands.includes( 'phet' ) ) {
    grunt.log.writeln( `Deployed: ${versionURL}/phet/${repo}_en_phet.html` );
  }
  if ( brands.includes( 'phet-io' ) ) {
    grunt.log.writeln( `Deployed: ${versionURL}/phet-io/wrappers/index` );
  }
};
<commit_msg>Added a network prompt, see https://github.com/phetsims/perennial/issues/98<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Deploys a dev version after incrementing the test version number.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
/* eslint-env node */
'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line
const assert = require( 'assert' );
const booleanPrompt = require( '../common/booleanPrompt' );
const build = require( '../common/build' );
const buildLocal = require( '../common/buildLocal' );
const devDirectoryExists = require( '../common/devDirectoryExists' );
const devScp = require( '../common/devScp' );
const devSsh = require( '../common/devSsh' );
const getBranch = require( '../common/getBranch' );
const getRepoVersion = require( '../common/getRepoVersion' );
const gitIsClean = require( '../common/gitIsClean' );
const gitPush = require( '../common/gitPush' );
const grunt = require( 'grunt' );
const lintAllRunnable = require( '../common/lintAllRunnable' );
const npmUpdate = require( '../common/npmUpdate' );
const setRepoVersion = require( '../common/setRepoVersion' );
const SimVersion = require( '../common/SimVersion' );
const updateDependenciesJSON = require( '../common/updateDependenciesJSON' );
const writePhetioHtaccess = require( '../common/writePhetioHtaccess' );

/**
 * Deploys a dev version after incrementing the test version number.
 * @public
 *
 * @param {string} repo
 * @param {Array.<string>} brands
 * @param {boolean} noninteractive
 * @param {string} branch - 'master' for normal dev deploys, otherwise is the name of a one-off branch
 * @param {string} [message] - Optional message to append to the version-increment commit.
 * @returns {Promise}
 */
module.exports = async function( repo, brands, noninteractive, branch, message ) {
  const isOneOff = branch !== 'master';
  const testType = isOneOff ? branch : 'dev';
  if ( isOneOff ) {
    assert( !branch.includes( '-' ), 'One-off versions should be from branches that do not include hyphens' );
  }

  const currentBranch = await getBranch( repo );
  if ( currentBranch !== branch ) {
    grunt.fail.fatal( `${testType} deployment should be on the branch ${branch}, not: ` + ( currentBranch ? currentBranch : '(detached head)' ) );
  }

  const previousVersion = await getRepoVersion( repo );

  if ( previousVersion.testType !== testType ) {
    if ( isOneOff ) {
      grunt.fail.fatal( `The current version identifier is not a one-off version (should be something like ${previousVersion.major}.${previousVersion.minor}.${previousVersion.maintenance}-${testType}.${previousVersion.testNumber === null ? '0' : previousVersion.testNumber}), aborting.` );
    }
    else {
      grunt.fail.fatal( 'The current version identifier is not a dev version, aborting.' );
    }
  }

  const isClean = await gitIsClean( repo );
  if ( !isClean ) {
    throw new Error( `Unclean status in ${repo}, cannot deploy` );
  }

  // Ensure we don't try to request an unsupported brand
  const supportedBrands = grunt.file.readJSON( `../${repo}/package.json` ).phet.supportedBrands;
  brands.forEach( brand => assert( supportedBrands.includes( brand ), `Brand ${brand} not included in ${repo}'s supported brands: ${supportedBrands.join( ',' )}` ) );

  // Ensure that the repository and its dependencies pass lint before continuing.
  // See https://github.com/phetsims/perennial/issues/76
  await lintAllRunnable( repo );

  // Bump the version
  const version = new SimVersion( previousVersion.major, previousVersion.minor, previousVersion.maintenance, {
    testType: testType,
    testNumber: previousVersion.testNumber + 1
  } );

  const versionString = version.toString();
  const simPath = buildLocal.devDeployPath + repo;
  const versionPath = simPath + '/' + versionString;

  const simPathExists = await devDirectoryExists( simPath );
  const versionPathExists = await devDirectoryExists( versionPath );

  if ( versionPathExists ) {
    grunt.fail.fatal( `Directory ${versionPath} already exists.  If you intend to replace the content then remove the directory manually from ${buildLocal.devDeployServer}.` );
  }

  if ( !await booleanPrompt( `Deploy ${versionString} to ${buildLocal.devDeployServer}`, noninteractive ) ) {
    grunt.fail.fatal( `Aborted ${testType} deploy` );
  }

  if ( !await booleanPrompt( 'Are you using the CU Boulder network (on campus or via VPN)?', noninteractive ) ) {
    grunt.fail.fatal( 'CU Boulder network required for dev deploy' );
  }

  await setRepoVersion( repo, version, message );
  await gitPush( repo, branch );

  // Make sure our correct npm dependencies are set
  await npmUpdate( repo );
  await npmUpdate( 'chipper' );

  grunt.log.writeln( await build( repo, {
    brands,
    allHTML: true,
    debugHTML: true
  } ) );

  // Create (and fix permissions for) the main simulation directory, if it didn't already exist
  if ( !simPathExists ) {
    await devSsh( `mkdir -p "${simPath}" && echo "IndexOrderDefault Descending Date\n" > "${simPath}/.htaccess"` );
    // await devSsh( `chmod -R g+w "${simPath}"` );
  }

  // Create the version-specific directory
  await devSsh( `mkdir -p "${versionPath}"` );

  // Copy the build contents into the version-specific directory
  for ( let brand of brands ) {
    await devScp( `../${repo}/build/${brand}`, `${versionPath}/` );
  }

  // If there is a protected directory and we are copying to the dev server, include the .htaccess file
  // This is for PhET-iO simulations, to protected the password protected wrappers, see
  // https://github.com/phetsims/phet-io/issues/641
  if ( brands.includes( 'phet-io' ) && buildLocal.devDeployServer === 'bayes.colorado.edu' ) {
    const htaccessLocation = `../${repo}/build/phet-io/.htaccess`;
    await writePhetioHtaccess( htaccessLocation );
    await devScp( htaccessLocation, `${versionPath}/phet-io/wrappers/.htaccess` );
  }

  // Permissions fixes so others can write over it later
  await devSsh( `chmod -R g+w "${versionPath}"` );
  await devSsh( `chown -R :phet "${versionPath}"` );

  // Move over dependencies.json and commit/push
  await updateDependenciesJSON( repo, brands, versionString, branch );

  const versionURL = `https://phet-dev.colorado.edu/html/${repo}/${versionString}`;

  if ( brands.includes( 'phet' ) ) {
    grunt.log.writeln( `Deployed: ${versionURL}/phet/${repo}_en_phet.html` );
  }
  if ( brands.includes( 'phet-io' ) ) {
    grunt.log.writeln( `Deployed: ${versionURL}/phet-io/wrappers/index` );
  }
};
<|endoftext|>


<commit_before>//MODEL

/**
 * Base class for JBrowse data backends that hold sequences and
 * features.  Some aspects reminiscent of Lincoln Stein's
 * Bio::DB::SeqFeature::Store.
  *
 * @class
 * @extends Store
 * @constructor
 */

function SeqFeatureStore(args) {
    Store.call(this, args);

    if( !args ) return;

    this.loaded  = args.loaded;
    this.changed = args.changeCallback;
};

SeqFeatureStore.prototype = new Store('');
<commit_msg>make SeqFeatureStore tolerant of not being passed a change callback<commit_after>//MODEL

/**
 * Base class for JBrowse data backends that hold sequences and
 * features.  Some aspects reminiscent of Lincoln Stein's
 * Bio::DB::SeqFeature::Store.
  *
 * @class
 * @extends Store
 * @constructor
 */

function SeqFeatureStore(args) {
    Store.call(this, args);

    if( !args ) return;

    this.loaded  = args.loaded;
    this.changed = args.changeCallback || function() {};
};

SeqFeatureStore.prototype = new Store('');
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    igv.createGtexBrowser = function (parentDiv, options) {

        if (!options) options = {};

        options.type = "GTEX";

        options.showKaryo = false;

        if (!options.fastaUrl) {
            options.fastaURL = "//dn7ywbm9isq8j.cloudfront.net/genomes/seq/hg19/hg19.fasta";
        }

        if (!options.cytobandURL) {
            options.cytobandURL = "//dn7ywbm9isq8j.cloudfront.net/genomes/seq/hg19/cytoBand.txt";
        }

        if (!options.track) {
            options.tracks = [
                {
                    type: "sequence",
                    order: 9999
                },
                {
                    url: "//dn7ywbm9isq8j.cloudfront.net/annotations/hg19/genes/gencode.v18.collapsed.bed",
                    label: "Genes",
                    order: 10000
                }
            ]
        }

        if (!options.createControls) {
            options.createControls = createGtexControls;
        }

        if (!options.tissueEqtlMappingURL) {
            options.tissueEqtlMappingURL = "http://www.gtexportal.org/igv/assets/eqtl/tissueEqtlMappings.txt";
        }

        return igv.createBrowser(parentDiv, options);

    }

    function createGtexControls(browser, options) {

        var controlDiv = $('
')[0],
            tissueEqtlMappingURL = options.tissueEqtlMappingURL,
            selectionDiv = $('
')[0],
            widgetDiv = $('
')[0],
            zoomDiv = $('
')[0],
            zoomOutButton = $('<button style="margin-right:5px">Zoom Out  </button>')[0],
            zoomInButton = $('<button>Zoom In  </button>')[0],
            trackHeightDiv = $('
Track height:
')[0],
            heightBoxInput = $('<input type="text" id="igvTrackHeightInput" value="100"/>')[0],
            goBoxDiv, goBoxInput, goBoxButton;

        zoomOutButton.onclick = function () {
            igv.browser.zoomOut();
        }

        zoomInButton.onclick = function () {
            igv.browser.zoomIn();
        }

        /**
         * Change height of all tracks
         */
        heightBoxInput.onchange = function () {
            igv.browser.setTrackHeight(heightBoxInput.value);
        }

        $(trackHeightDiv).append(heightBoxInput);
        $(zoomDiv).append(zoomOutButton);
        $(zoomDiv).append(zoomInButton);

        $(widgetDiv).append(trackHeightDiv);
        $(widgetDiv).append(zoomDiv);

        $(controlDiv).append(selectionDiv);
        $(controlDiv).append(widgetDiv);

        if (options.dev) {
            goBoxDiv = $('
')[0];
            goBoxInput = $('<input type="text" id="goBox" value="PDE8B"/>')[0];
            goBoxButton = $('<button name="goButton"">Go</button>')[0];
            goBoxInput.onChange = function () {
                igv.browser.search(goBoxInput.value);
            }
            goBoxButton.onclick = function () {
                igv.browser.search(goBoxInput.value);
            }
            $(goBoxDiv).append(goBoxInput);
            $(goBoxDiv).append(goBoxButton);
            $(controlDiv).append(goBoxDiv);
        }

        loadGtexTissueMappings(tissueEqtlMappingURL, function (records) {

            records.forEach(function (record) {

                var containerDiv = $('')[0]; // style="float:left">');

                var cb = $('<input type="checkbox"">')[0];
                cb.tissueRecord = record;
                cb.onclick = function (e) {
                    var record = e.currentTarget.tissueRecord,
                        track;

                    if (e.currentTarget.checked) {

                        browser.loadTrack(
                            {
                                type: "eqtl",
                                sourceType: 'gtex',
                                url: record.url,
                                label: record.label,
                                disableButtons: true
                            }
                        );
                    }
                    else {
                        track = findTrackWithURL(browser, record.url);
                        if (track) {
                            browser.removeTrack(track);
                        }
                    }
                };
                $(containerDiv).append(cb);

                var span = $('' + record.label + '')[0];
                $(containerDiv).append(span);

                $(selectionDiv).append(containerDiv);
            });

            //igv.gtexBrowser.activeTracks(igv.gtexBrowser.tracksToInitialize);
        });

        return controlDiv;
    }

    function findTrackWithURL(browser, url) {

        var i, len = browser.trackViews.length;

        for (i = 0; i < len; i++) {
            if (browser.trackViews[i].track.url === url) {
                return browser.trackViews[i].track;
            }
        }
        return null;

    }

    function loadGtexTissueMappings(url, continuation) {

        var dataLoader = new igv.DataLoader(url);

        dataLoader.loadBinaryString(function (data) {

                var lines = data.splitLines(),
                    len = lines.length,
                    i,
                    records = [],
                    tokens;

                for (i = 0; i < len; i++) {
                    tokens = lines[i].split('\t');
                    if (tokens.length == 2) {
                        records.push({label: tokens[0], url: tokens[1]});
                    }
                }

                continuation(records);

            },
            function (errorEvent) {
                continuation(null);
            });

    }

    igv.GtexSelection = function (selection) {

        this.geneColors = {};
        this.gene = null;
        this.snp = null;
        this.genesCount = 0;

        if (selection.gene) {
            this.gene = selection.gene.toUpperCase();
            this.geneColors[this.gene] = brewer[this.genesCount++];

        }
        if (selection.snp) {
            this.snp = selection.snp.toUpperCase();
        }

    }

    igv.GtexSelection.prototype.addGene = function (geneName) {
        if (!this.geneColors[geneName.toUpperCase()]) {
            this.geneColors[geneName.toUpperCase()] = brewer[this.genesCount++];
        }
    }

    igv.GtexSelection.prototype.colorForGene = function (geneName) {
        return this.geneColors[geneName.toUpperCase()];
    }

    var brewer = new Array();
// Set +!
    brewer.push("rgb(228,26,28)");
    brewer.push("rgb(55,126,184)");
    brewer.push("rgb(77,175,74)");
    brewer.push("rgb(166,86,40)");
    brewer.push("rgb(152,78,163)");
    brewer.push("rgb(255,127,0)");
    brewer.push("rgb(247,129,191)");
    brewer.push("rgb(153,153,153)");
    brewer.push("rgb(255,255,51)");

// #Set 2
    brewer.push("rgb(102, 194, 165");
    brewer.push("rgb(252, 141, 98");
    brewer.push("rgb(141, 160, 203");
    brewer.push("rgb(231, 138, 195");
    brewer.push("rgb(166, 216, 84");
    brewer.push("rgb(255, 217, 47");
    brewer.push("rgb(229, 196, 148");
    brewer.push("rgb(179, 179, 179");

//#Set 3
    brewer.push("rgb( 141, 211, 199");
    brewer.push("rgb(255, 255, 179");
    brewer.push("rgb(190, 186, 218");
    brewer.push("rgb(251, 128, 114");
    brewer.push("rgb(128, 177, 211");
    brewer.push("rgb(253, 180, 98");
    brewer.push("rgb(179, 222, 105");
    brewer.push("rgb(252, 205, 229");
    brewer.push("rgb(217, 217, 217");
    brewer.push("rgb(188, 128, 189");
    brewer.push("rgb(204, 235, 197");
    brewer.push("rgb(255, 237, 111");

    return igv;

})
(igv || {});
<commit_msg>Tracks added using the track height setting instead of the default.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    igv.createGtexBrowser = function (parentDiv, options) {

        if (!options) options = {};

        options.type = "GTEX";

        options.showKaryo = false;

        if (!options.fastaUrl) {
            options.fastaURL = "//dn7ywbm9isq8j.cloudfront.net/genomes/seq/hg19/hg19.fasta";
        }

        if (!options.cytobandURL) {
            options.cytobandURL = "//dn7ywbm9isq8j.cloudfront.net/genomes/seq/hg19/cytoBand.txt";
        }

        if (!options.track) {
            options.tracks = [
                {
                    type: "sequence",
                    order: 9999
                },
                {
                    url: "//dn7ywbm9isq8j.cloudfront.net/annotations/hg19/genes/gencode.v18.collapsed.bed",
                    label: "Genes",
                    order: 10000
                }
            ]
        }

        if (!options.createControls) {
            options.createControls = createGtexControls;
        }

        if (!options.tissueEqtlMappingURL) {
            options.tissueEqtlMappingURL = "http://www.gtexportal.org/igv/assets/eqtl/tissueEqtlMappings.txt";
        }

        return igv.createBrowser(parentDiv, options);

    }

    function createGtexControls(browser, options) {

        var controlDiv = $('
')[0],
            tissueEqtlMappingURL = options.tissueEqtlMappingURL,
            selectionDiv = $('
')[0],
            widgetDiv = $('
')[0],
            zoomDiv = $('
')[0],
            zoomOutButton = $('<button style="margin-right:5px">Zoom Out  </button>')[0],
            zoomInButton = $('<button>Zoom In  </button>')[0],
            trackHeightDiv = $('
Track height:
')[0],
            heightBoxInput = $('<input type="text" id="igvTrackHeightInput" value="100"/>')[0],
            goBoxDiv, goBoxInput, goBoxButton;

        zoomOutButton.onclick = function () {
            igv.browser.zoomOut();
        }

        zoomInButton.onclick = function () {
            igv.browser.zoomIn();
        }

        /**
         * Change height of all tracks
         */
        heightBoxInput.onchange = function () {
            igv.browser.setTrackHeight(heightBoxInput.value);
        }

        $(trackHeightDiv).append(heightBoxInput);
        $(zoomDiv).append(zoomOutButton);
        $(zoomDiv).append(zoomInButton);

        $(widgetDiv).append(trackHeightDiv);
        $(widgetDiv).append(zoomDiv);

        $(controlDiv).append(selectionDiv);
        $(controlDiv).append(widgetDiv);

        if (options.dev) {
            goBoxDiv = $('
')[0];
            goBoxInput = $('<input type="text" id="goBox" value="PDE8B"/>')[0];
            goBoxButton = $('<button name="goButton"">Go</button>')[0];
            goBoxInput.onChange = function () {
                igv.browser.search(goBoxInput.value);
            }
            goBoxButton.onclick = function () {
                igv.browser.search(goBoxInput.value);
            }
            $(goBoxDiv).append(goBoxInput);
            $(goBoxDiv).append(goBoxButton);
            $(controlDiv).append(goBoxDiv);
        }

        loadGtexTissueMappings(tissueEqtlMappingURL, function (records) {

            records.forEach(function (record) {

                var containerDiv = $('')[0]; // style="float:left">');

                var cb = $('<input type="checkbox"">')[0];
                cb.tissueRecord = record;
                cb.onclick = function (e) {
                    var record = e.currentTarget.tissueRecord,
                        track;

                    if (e.currentTarget.checked) {

                        browser.loadTrack(
                            {
                                type: "eqtl",
                                sourceType: 'gtex',
                                url: record.url,
                                label: record.label,
                                disableButtons: true,
                                height: browser.trackHeight
                            }
                        );
                    }
                    else {
                        track = findTrackWithURL(browser, record.url);
                        if (track) {
                            browser.removeTrack(track);
                        }
                    }
                };
                $(containerDiv).append(cb);

                var span = $('' + record.label + '')[0];
                $(containerDiv).append(span);

                $(selectionDiv).append(containerDiv);
            });

            //igv.gtexBrowser.activeTracks(igv.gtexBrowser.tracksToInitialize);
        });

        return controlDiv;
    }

    function findTrackWithURL(browser, url) {

        var i, len = browser.trackViews.length;

        for (i = 0; i < len; i++) {
            if (browser.trackViews[i].track.url === url) {
                return browser.trackViews[i].track;
            }
        }
        return null;

    }

    function loadGtexTissueMappings(url, continuation) {

        var dataLoader = new igv.DataLoader(url);

        dataLoader.loadBinaryString(function (data) {

                var lines = data.splitLines(),
                    len = lines.length,
                    i,
                    records = [],
                    tokens;

                for (i = 0; i < len; i++) {
                    tokens = lines[i].split('\t');
                    if (tokens.length == 2) {
                        records.push({label: tokens[0], url: tokens[1]});
                    }
                }

                continuation(records);

            },
            function (errorEvent) {
                continuation(null);
            });

    }

    igv.GtexSelection = function (selection) {

        this.geneColors = {};
        this.gene = null;
        this.snp = null;
        this.genesCount = 0;

        if (selection.gene) {
            this.gene = selection.gene.toUpperCase();
            this.geneColors[this.gene] = brewer[this.genesCount++];

        }
        if (selection.snp) {
            this.snp = selection.snp.toUpperCase();
        }

    }

    igv.GtexSelection.prototype.addGene = function (geneName) {
        if (!this.geneColors[geneName.toUpperCase()]) {
            this.geneColors[geneName.toUpperCase()] = brewer[this.genesCount++];
        }
    }

    igv.GtexSelection.prototype.colorForGene = function (geneName) {
        return this.geneColors[geneName.toUpperCase()];
    }

    var brewer = new Array();
// Set +!
    brewer.push("rgb(228,26,28)");
    brewer.push("rgb(55,126,184)");
    brewer.push("rgb(77,175,74)");
    brewer.push("rgb(166,86,40)");
    brewer.push("rgb(152,78,163)");
    brewer.push("rgb(255,127,0)");
    brewer.push("rgb(247,129,191)");
    brewer.push("rgb(153,153,153)");
    brewer.push("rgb(255,255,51)");

// #Set 2
    brewer.push("rgb(102, 194, 165");
    brewer.push("rgb(252, 141, 98");
    brewer.push("rgb(141, 160, 203");
    brewer.push("rgb(231, 138, 195");
    brewer.push("rgb(166, 216, 84");
    brewer.push("rgb(255, 217, 47");
    brewer.push("rgb(229, 196, 148");
    brewer.push("rgb(179, 179, 179");

//#Set 3
    brewer.push("rgb( 141, 211, 199");
    brewer.push("rgb(255, 255, 179");
    brewer.push("rgb(190, 186, 218");
    brewer.push("rgb(251, 128, 114");
    brewer.push("rgb(128, 177, 211");
    brewer.push("rgb(253, 180, 98");
    brewer.push("rgb(179, 222, 105");
    brewer.push("rgb(252, 205, 229");
    brewer.push("rgb(217, 217, 217");
    brewer.push("rgb(188, 128, 189");
    brewer.push("rgb(204, 235, 197");
    brewer.push("rgb(255, 237, 111");

    return igv;

})
(igv || {});
<|endoftext|>