<commit_before>import {inject} from 'aurelia-dependency-injection';
import {ObserverLocator, calcSplices, getChangeRecords} from 'aurelia-binding';
import {BoundViewFactory, ViewSlot, customAttribute, bindable, templateController} from 'aurelia-templating';

@customAttribute('repeat')
@bindable('items')
@bindable('local')
@bindable('key')
@bindable('value')
@templateController
@inject(BoundViewFactory, ViewSlot, ObserverLocator)
export class Repeat {
  constructor(viewFactory, viewSlot, observerLocator){
    this.viewFactory = viewFactory;
    this.viewSlot = viewSlot;
    this.observerLocator = observerLocator;
    this.local = 'item';
    this.key = 'key';
    this.value = 'value';
  }

  bind(executionContext){
    var items = this.items,
      observer;

    this.executionContext = executionContext;

    if(!items){
      if(this.oldItems){
        this.viewSlot.removeAll();
      }

      return;
    }

    if (this.oldItems === items) {
      if (items instanceof Map) {
        var records = getChangeRecords(items);
        observer = this.observerLocator.getMapObserver(items);

        this.handleMapChangeRecords(items, records);

        this.disposeSubscription = observer.subscribe(records => {
          this.handleMapChangeRecords(items, records);
        });
      } else {
        var splices = calcSplices(items, 0, items.length, this.lastBoundItems, 0, this.lastBoundItems.length);
        observer = this.observerLocator.getArrayObserver(items);

        this.handleSplices(items, splices);
        this.lastBoundItems = this.oldItems = null;

        this.disposeSubscription = observer.subscribe(splices => {
          this.handleSplices(items, splices);
        });
      }
    } else {
      this.processItems();
    }
  }

  unbind(){
    this.oldItems = this.items;

    if(this.items instanceof Array){
      this.lastBoundItems = this.items.slice(0);
    }

    if(this.disposeSubscription){
      this.disposeSubscription();
      this.disposeSubscription = null;
    }
  }

  itemsChanged() {
    this.processItems();
  }

  processItems() {
    var items = this.items,
      viewSlot = this.viewSlot;

    if (this.disposeSubscription) {
      this.disposeSubscription();
      viewSlot.removeAll();
    }

    if(!items){
      return;
    }

    if (items instanceof Array) {
      this.processArrayItems(items);
    } else if(items instanceof Map) {
      this.processMapEntries(items);
    } else if((typeof items === 'number')){
      this.processNumber(items);
    }else{
      throw new Error('Object in "repeat" must be of type Array, Map or Number');
    }
  }

  processArrayItems(items){
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      i, ii, row, view, observer;

    observer = this.observerLocator.getArrayObserver(items);

    for(i = 0, ii = items.length; i < ii; ++i){
      row = this.createFullExecutionContext(items[i], i, ii);
      view = viewFactory.create(row);
      viewSlot.add(view);
    }

    this.disposeSubscription = observer.subscribe(splices => {
      this.handleSplices(items, splices);
    });
  }

  processMapEntries(items) {
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      index = 0,
      row, view, observer;

    observer = this.observerLocator.getMapObserver(items);

    items.forEach((value, key) => {
      row = this.createFullExecutionKvpContext(key, value, index, items.size);
      view = viewFactory.create(row);
      viewSlot.add(view);
      ++index;
    });

    this.disposeSubscription = observer.subscribe(record => {
      this.handleMapChangeRecords(items, record);
    });
  }

  processNumber(value){
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      i, ii, row, view;

    for(i = 0, ii = Math.floor(value); i < ii; ++i){
      row = this.createFullExecutionContext(i, i, ii);
      view = viewFactory.create(row);
      viewSlot.add(view);
    }
  }

  createBaseExecutionContext(data){
    var context = {};
    context[this.local] = data;
    context.$parent = this.executionContext;
    return context;
  }

  createBaseExecutionKvpContext(key, value){
    var context = {};
    context[this.key] = key;
    context[this.value] = value;
    context.$parent = this.executionContext;
    return context;
  }

  createFullExecutionContext(data, index, length){
    var context = this.createBaseExecutionContext(data);
    return this.updateExecutionContext(context, index, length);
  }

  createFullExecutionKvpContext(key, value, index, length){
    var context = this.createBaseExecutionKvpContext(key, value);
    return this.updateExecutionContext(context, index, length);
  }

  updateExecutionContext(context, index, length){
    var first = (index === 0),
        last = (index === length - 1),
        even = index % 2 === 0;

    context.$index = index;
    context.$first = first;
    context.$last = last;
    context.$middle = !(first || last);
    context.$odd = !even;
    context.$even = even;

    return context;
  }

  handleSplices(array, splices) {
    var viewLookup = new Map(),
      viewSlot = this.viewSlot,
      spliceIndexLow, view, i, ii, j, jj, row, splice,
      addIndex, end, itemsLeftToAdd,
      removed, model, children, length;

    for (i = 0, ii = splices.length; i < ii; ++i) {
      splice = splices[i];
      addIndex = splice.index;
      itemsLeftToAdd = splice.addedCount;
      end = splice.index + splice.addedCount;
      removed = splice.removed;
      if(typeof spliceIndexLow === 'undefined' || spliceIndexLow === null || spliceIndexLow > splice.index){
        spliceIndexLow = splice.index;
      }

      for (j = 0, jj = removed.length; j < jj; ++j) {
        if (itemsLeftToAdd > 0) {
          view = viewSlot.children[splice.index + j];
          view.executionContext[this.local] = array[addIndex + j];
          --itemsLeftToAdd;
        } else {
          view = viewSlot.removeAt(addIndex + splice.addedCount);
          if(view){
            viewLookup.set(removed[j], view);
          }
        }
      }

      addIndex += removed.length;

      for (; 0 < itemsLeftToAdd; ++addIndex) {
        model = array[addIndex];
        view = viewLookup.get(model);
        if(view){
          viewLookup.delete(model);
          viewSlot.insert(addIndex, view);
        }else{
          row = this.createBaseExecutionContext(model);
          view = this.viewFactory.create(row);
          viewSlot.insert(addIndex, view);
        }
        --itemsLeftToAdd;
      }
    }

    children = this.viewSlot.children;
    length = children.length;

    if(spliceIndexLow > 0){
      spliceIndexLow = spliceIndexLow - 1;
    }

    for(; spliceIndexLow < length; ++spliceIndexLow){
      this.updateExecutionContext(children[spliceIndexLow].executionContext, spliceIndexLow, length);
    }

    viewLookup.forEach(x => x.unbind());
  }

  handleMapChangeRecords(map, records) {
    var viewSlot = this.viewSlot,
      key, i, ii, view, children, length, row, removeIndex, record;

    for (i = 0, ii = records.length; i < ii; ++i) {
      record = records[i];
      key = record.key;
      switch (record.type) {
        case 'update':
          removeIndex = this.getViewIndexByKey(key);
          viewSlot.removeAt(removeIndex);
          row = this.createBaseExecutionKvpContext(key, map.get(key));
          view = this.viewFactory.create(row);
          viewSlot.insert(removeIndex, view);
          break;
        case 'add':
          row = this.createBaseExecutionKvpContext(key, map.get(key));
          view = this.viewFactory.create(row);
          viewSlot.insert(map.size, view);
          break;
        case 'delete':
          if (!record.oldValue) { return; }
          removeIndex = this.getViewIndexByKey(key);
          viewSlot.removeAt(removeIndex);
          break;
        case 'clear':
          viewSlot.removeAll();
      }
    }

    children = viewSlot.children;
    length = children.length;

    for (i = 0; i < length; i++) {
      this.updateExecutionContext(children[i].executionContext, i, length);
    }
  }

  getViewIndexByKey(key) {
    var viewSlot = this.viewSlot,
      i, ii, child;

    for (i = 0, ii = viewSlot.children.length; i < ii; ++i) { // TODO (martingust) better way to get index?
      child = viewSlot.children[i];
      if (child.bindings[0].source[this.key] === key) {
        return i;
      }
    }
  }
}
<commit_msg>fix(repeat): handle promises returned by view-slot<commit_after>import {inject} from 'aurelia-dependency-injection';
import {ObserverLocator, calcSplices, getChangeRecords} from 'aurelia-binding';
import {BoundViewFactory, ViewSlot, customAttribute, bindable, templateController} from 'aurelia-templating';

@customAttribute('repeat')
@bindable('items')
@bindable('local')
@bindable('key')
@bindable('value')
@templateController
@inject(BoundViewFactory, ViewSlot, ObserverLocator)
export class Repeat {
  constructor(viewFactory, viewSlot, observerLocator){
    this.viewFactory = viewFactory;
    this.viewSlot = viewSlot;
    this.observerLocator = observerLocator;
    this.local = 'item';
    this.key = 'key';
    this.value = 'value';
  }

  bind(executionContext){
    var items = this.items,
      observer;

    this.executionContext = executionContext;

    if(!items){
      if(this.oldItems){
        this.viewSlot.removeAll();
      }

      return;
    }

    if (this.oldItems === items) {
      if (items instanceof Map) {
        var records = getChangeRecords(items);
        observer = this.observerLocator.getMapObserver(items);

        this.handleMapChangeRecords(items, records);

        this.disposeSubscription = observer.subscribe(records => {
          this.handleMapChangeRecords(items, records);
        });
      } else {
        var splices = calcSplices(items, 0, items.length, this.lastBoundItems, 0, this.lastBoundItems.length);
        observer = this.observerLocator.getArrayObserver(items);

        this.handleSplices(items, splices);
        this.lastBoundItems = this.oldItems = null;

        this.disposeSubscription = observer.subscribe(splices => {
          this.handleSplices(items, splices);
        });
      }
    } else {
      this.processItems();
    }
  }

  unbind(){
    this.oldItems = this.items;

    if(this.items instanceof Array){
      this.lastBoundItems = this.items.slice(0);
    }

    if(this.disposeSubscription){
      this.disposeSubscription();
      this.disposeSubscription = null;
    }
  }

  itemsChanged() {
    this.processItems();
  }

  processItems() {
    var items = this.items,
      viewSlot = this.viewSlot;

    if (this.disposeSubscription) {
      this.disposeSubscription();
      viewSlot.removeAll();
    }

    if(!items){
      return;
    }

    if (items instanceof Array) {
      this.processArrayItems(items);
    } else if(items instanceof Map) {
      this.processMapEntries(items);
    } else if((typeof items === 'number')){
      this.processNumber(items);
    }else{
      throw new Error('Object in "repeat" must be of type Array, Map or Number');
    }
  }

  processArrayItems(items){
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      i, ii, row, view, observer;

    observer = this.observerLocator.getArrayObserver(items);

    for(i = 0, ii = items.length; i < ii; ++i){
      row = this.createFullExecutionContext(items[i], i, ii);
      view = viewFactory.create(row);
      viewSlot.add(view);
    }

    this.disposeSubscription = observer.subscribe(splices => {
      this.handleSplices(items, splices);
    });
  }

  processMapEntries(items) {
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      index = 0,
      row, view, observer;

    observer = this.observerLocator.getMapObserver(items);

    items.forEach((value, key) => {
      row = this.createFullExecutionKvpContext(key, value, index, items.size);
      view = viewFactory.create(row);
      viewSlot.add(view);
      ++index;
    });

    this.disposeSubscription = observer.subscribe(record => {
      this.handleMapChangeRecords(items, record);
    });
  }

  processNumber(value){
    var viewFactory = this.viewFactory,
      viewSlot = this.viewSlot,
      i, ii, row, view;

    for(i = 0, ii = Math.floor(value); i < ii; ++i){
      row = this.createFullExecutionContext(i, i, ii);
      view = viewFactory.create(row);
      viewSlot.add(view);
    }
  }

  createBaseExecutionContext(data){
    var context = {};
    context[this.local] = data;
    context.$parent = this.executionContext;
    return context;
  }

  createBaseExecutionKvpContext(key, value){
    var context = {};
    context[this.key] = key;
    context[this.value] = value;
    context.$parent = this.executionContext;
    return context;
  }

  createFullExecutionContext(data, index, length){
    var context = this.createBaseExecutionContext(data);
    return this.updateExecutionContext(context, index, length);
  }

  createFullExecutionKvpContext(key, value, index, length){
    var context = this.createBaseExecutionKvpContext(key, value);
    return this.updateExecutionContext(context, index, length);
  }

  updateExecutionContext(context, index, length){
    var first = (index === 0),
        last = (index === length - 1),
        even = index % 2 === 0;

    context.$index = index;
    context.$first = first;
    context.$last = last;
    context.$middle = !(first || last);
    context.$odd = !even;
    context.$even = even;

    return context;
  }

  handleSplices(array, splices) {
    var viewLookup = new Map(),
      viewSlot = this.viewSlot,
      spliceIndexLow, viewOrPromise, view,
      i, ii, j, jj, row, splice,
      addIndex, end, itemsLeftToAdd,
      removed, model, children, length;

    for (i = 0, ii = splices.length; i < ii; ++i) {
      splice = splices[i];
      addIndex = splice.index;
      itemsLeftToAdd = splice.addedCount;
      end = splice.index + splice.addedCount;
      removed = splice.removed;
      if(typeof spliceIndexLow === 'undefined' || spliceIndexLow === null || spliceIndexLow > splice.index){
        spliceIndexLow = splice.index;
      }

      for (j = 0, jj = removed.length; j < jj; ++j) {
        if (itemsLeftToAdd > 0) {
          view = viewSlot.children[splice.index + j];
          view.executionContext[this.local] = array[addIndex + j];
          --itemsLeftToAdd;
        } else {
          viewOrPromise = viewSlot.removeAt(addIndex + splice.addedCount);
          if(viewOrPromise){
            viewLookup.set(removed[j], viewOrPromise);
          }
        }
      }

      addIndex += removed.length;

      for (; 0 < itemsLeftToAdd; ++addIndex) {
        model = array[addIndex];
        viewOrPromise = viewLookup.get(model);
        if(viewOrPromise instanceof Promise){
          ((localAddIndex, localModel) => {
            viewOrPromise.then(view => {
              viewLookup.delete(localModel);
              viewSlot.insert(localAddIndex, view);
            });
          })(addIndex, model);
        }else if(viewOrPromise){
          viewLookup.delete(model);
          viewSlot.insert(addIndex, viewOrPromise);
        }else{
          row = this.createBaseExecutionContext(model);
          view = this.viewFactory.create(row);
          viewSlot.insert(addIndex, view);
        }
        --itemsLeftToAdd;
      }
    }

    children = this.viewSlot.children;
    length = children.length;

    if(spliceIndexLow > 0){
      spliceIndexLow = spliceIndexLow - 1;
    }

    for(; spliceIndexLow < length; ++spliceIndexLow){
      this.updateExecutionContext(children[spliceIndexLow].executionContext, spliceIndexLow, length);
    }

    viewLookup.forEach(x => {
      if(x instanceof Promise){
        x.then(y => y.unbind());
      }else{
        x.unbind();
      }
    });
  }

  handleMapChangeRecords(map, records) {
    var viewSlot = this.viewSlot,
      key, i, ii, view, children, length, row, removeIndex, record;

    for (i = 0, ii = records.length; i < ii; ++i) {
      record = records[i];
      key = record.key;
      switch (record.type) {
        case 'update':
          removeIndex = this.getViewIndexByKey(key);
          viewSlot.removeAt(removeIndex);
          row = this.createBaseExecutionKvpContext(key, map.get(key));
          view = this.viewFactory.create(row);
          viewSlot.insert(removeIndex, view);
          break;
        case 'add':
          row = this.createBaseExecutionKvpContext(key, map.get(key));
          view = this.viewFactory.create(row);
          viewSlot.insert(map.size, view);
          break;
        case 'delete':
          if (!record.oldValue) { return; }
          removeIndex = this.getViewIndexByKey(key);
          viewSlot.removeAt(removeIndex);
          break;
        case 'clear':
          viewSlot.removeAll();
      }
    }

    children = viewSlot.children;
    length = children.length;

    for (i = 0; i < length; i++) {
      this.updateExecutionContext(children[i].executionContext, i, length);
    }
  }

  getViewIndexByKey(key) {
    var viewSlot = this.viewSlot,
      i, ii, child;

    for (i = 0, ii = viewSlot.children.length; i < ii; ++i) { // TODO (martingust) better way to get index?
      child = viewSlot.children[i];
      if (child.bindings[0].source[this.key] === key) {
        return i;
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * UI-router metadata provider
 * @copyright @murwa 2017
 */
(function (angular) {
    angular.module('ui-router-metadata', [
        'ui.router',
        'angular-utility-filters.uc-words'
    ])
        .provider('$metadata', [function MetadataProvider() {
            // Define defaults
            var defaults = {
                title: null,
                description: null,
                image: null,
                descriptionLength: 160
            };
            // For optional use in config phase
            this.setDefaults = function (values) {
                if (angular.isObject(values)) {
                    angular.extend(defaults, values);
                    return this;
                }

                throw 'Defaults must be an object';
            }
            this.$get = ['$state', '$filter', function ($state, $filter) {
                return new MetadataService($state, defaults, $filter);
            }];

            /**
             * Error service
             *
             * @param {*} $state
             * @param {*} config
             */
            function MetadataService($state, config, $filter) {
                // Init
                var self = this;

                /**
                 * Get metadata value
                 * @param {*}
                 * @return {*|string}
                 */
                self.get = function (key) {
                    var meta = resolveMeta();
                    return key ? meta[key] : meta;
                }
                /**
                 * Get metadata title.
                 * @return {string}
                 */
                self.getTitle = function () {
                    return $filter('ucWords')(self.get('title'));
                }
                /**
                 * Get metadata description
                 * @return {string}
                 */
                self.getDescription = function () {
                    return $filter('limitTo')(self.get('description'), config.descriptionLength, '');
                }

                /**
                 * Resolve metadata
                 * @return {*}
                 */
                function resolveMeta() {
                    return $state.$current.locals.globals['$meta'] || config;
                }
            }
        }])
        .run(['$metadata', '$rootScope', '$timeout', function ($metadata, $rootScope, $timeout) {
            console.log('Binding event listeners')
            $rootScope.$on('$stateChangeSuccess', function () {
                console.log('State changed')
                $timeout(function () {
                    console.log('Title set' - $metadata.getTitle());
                    window.title = $metadata.getTitle();
                });
            });
        }]);
})(angular);<commit_msg>Add debug<commit_after>/**
 * UI-router metadata provider
 * @copyright @murwa 2017
 */
(function (angular) {
    angular.module('ui-router-metadata', [
        'ui.router',
        'angular-utility-filters.uc-words'
    ])
        .provider('$metadata', [function MetadataProvider() {
            // Define defaults
            var defaults = {
                title: null,
                description: null,
                image: null,
                descriptionLength: 160
            };
            // For optional use in config phase
            this.setDefaults = function (values) {
                if (angular.isObject(values)) {
                    angular.extend(defaults, values);
                    return this;
                }

                throw 'Defaults must be an object';
            }
            this.$get = ['$state', '$filter', function ($state, $filter) {
                return new MetadataService($state, defaults, $filter);
            }];

            /**
             * Error service
             *
             * @param {*} $state
             * @param {*} config
             */
            function MetadataService($state, config, $filter) {
                // Init
                var self = this;

                /**
                 * Get metadata value
                 * @param {*}
                 * @return {*|string}
                 */
                self.get = function (key) {
                    var meta = resolveMeta();
                    return key ? meta[key] : meta;
                }
                /**
                 * Get metadata title.
                 * @return {string}
                 */
                self.getTitle = function () {
                    return $filter('ucWords')(self.get('title'));
                }
                /**
                 * Get metadata description
                 * @return {string}
                 */
                self.getDescription = function () {
                    return $filter('limitTo')(self.get('description'), config.descriptionLength, '');
                }

                /**
                 * Resolve metadata
                 * @return {*}
                 */
                function resolveMeta() {
                    return $state.$current.locals.globals['$meta'] || config;
                }
            }
        }])
        .run(['$metadata', '$rootScope', '$timeout', function ($metadata, $rootScope, $timeout) {
            console.log('Binding event listeners')
            $rootScope.$on('$stateChangeSuccess', function () {
                console.log('State changed')
                $timeout(function () {
                    console.log('Title set - ' + $metadata.getTitle());
                    window.title = $metadata.getTitle();
                });
            });
        }]);
})(angular);<|endoftext|>


<commit_before>goog.provide('ol.FeatureOverlay');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol.Collection');
goog.require('ol.CollectionEventType');
goog.require('ol.Feature');
goog.require('ol.feature');
goog.require('ol.render.EventType');

/**
 * @constructor
 * @param {olx.FeatureOverlayOptions=} opt_options Options.
 * @todo stability experimental
 */
ol.FeatureOverlay = function(opt_options) {

  var options = goog.isDef(opt_options) ? opt_options : {};

  /**
   * @private
   * @type {ol.Collection}
   */
  this.features_ = null;

  /**
   * @private
   * @type {Array.<goog.events.Key>}
   */
  this.featuresListenerKeys_ = null;

  /**
   * @private
   * @type {Object.<string, goog.events.Key>}
   */
  this.featureChangeListenerKeys_ = null;

  /**
   * @private
   * @type {ol.Map}
   */
  this.map_ = null;

  /**
   * @private
   * @type {goog.events.Key}
   */
  this.postComposeListenerKey_ = null;

  /**
   * @private
   * @type {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction}
   */
  this.style_ = null;

  /**
   * @private
   * @type {ol.feature.StyleFunction|undefined}
   */
  this.styleFunction_ = goog.isDef(options.style) ?
      ol.feature.createStyleFunction(options.style) : undefined;

  if (goog.isDef(options.features)) {
    if (goog.isArray(options.features)) {
      this.setFeatures(new ol.Collection(goog.array.clone(options.features)));
    } else {
      goog.asserts.assertInstanceof(options.features, ol.Collection);
      this.setFeatures(options.features);
    }
  } else {
    this.setFeatures(new ol.Collection());
  }

  if (goog.isDef(options.map)) {
    this.setMap(options.map);
  }

};

/**
 * @param {ol.Feature} feature Feature.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.addFeature = function(feature) {
  this.features_.push(feature);
};

/**
 * @return {ol.Collection} Features collection.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.getFeatures = function() {
  return this.features_;
};

/**
 * @private
 */
ol.FeatureOverlay.prototype.handleFeatureChange_ = function() {
  this.render_();
};

/**
 * @private
 * @param {ol.CollectionEvent} collectionEvent Collection event.
 */
ol.FeatureOverlay.prototype.handleFeaturesAdd_ = function(collectionEvent) {
  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
  this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
      goog.events.listen(feature, goog.events.EventType.CHANGE,
      this.handleFeatureChange_, false, this);
  this.render_();
};

/**
 * @private
 * @param {ol.CollectionEvent} collectionEvent Collection event.
 */
ol.FeatureOverlay.prototype.handleFeaturesRemove_ = function(collectionEvent) {
  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
  var key = goog.getUid(feature).toString();
  goog.events.unlistenByKey(this.featureChangeListenerKeys_[key]);
  delete this.featureChangeListenerKeys_[key];
  this.render_();
};

/**
 * @param {ol.render.Event} event Event.
 * @private
 */
ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) {
  if (goog.isNull(this.features_)) {
    return;
  }
  var styleFunction = this.styleFunction_;
  if (!goog.isDef(styleFunction)) {
    styleFunction = ol.feature.defaultStyleFunction;
  }
  var resolution = event.frameState.view2DState.resolution;
  var vectorContext = event.vectorContext;
  var i, ii, styles;
  this.features_.forEach(function(feature) {
    styles = styleFunction(feature, resolution);
    if (!goog.isDefAndNotNull(styles)) {
      return;
    }
    ii = styles.length;
    for (i = 0; i < ii; ++i) {
      vectorContext.drawFeature(feature, styles[i]);
    }
  });
};

/**
 * @param {ol.Feature} feature Feature.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.removeFeature = function(feature) {
  this.features_.remove(feature);
};

/**
 * @private
 */
ol.FeatureOverlay.prototype.render_ = function() {
  if (!goog.isNull(this.map_)) {
    this.map_.render();
  }
};

/**
 * @param {ol.Collection} features Features collection.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setFeatures = function(features) {
  if (!goog.isNull(this.featuresListenerKeys_)) {
    goog.array.forEach(this.featuresListenerKeys_, goog.events.unlistenByKey);
    this.featuresListenerKeys_ = null;
  }
  if (!goog.isNull(this.featureChangeListenerKeys_)) {
    goog.array.forEach(
        goog.object.getValues(this.featureChangeListenerKeys_),
        goog.events.unlistenByKey);
    this.featureChangeListenerKeys_ = null;
  }
  this.features_ = features;
  if (!goog.isNull(features)) {
    this.featuresListenerKeys_ = [
      goog.events.listen(features, ol.CollectionEventType.ADD,
          this.handleFeaturesAdd_, false, this),
      goog.events.listen(features, ol.CollectionEventType.REMOVE,
          this.handleFeaturesRemove_, false, this)
    ];
    this.featureChangeListenerKeys_ = {};
    var featuresArray = features.getArray();
    var i, ii = featuresArray.length;
    var feature;
    for (i = 0; i < ii; ++i) {
      feature = featuresArray[i];
      this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
          goog.events.listen(feature, goog.events.EventType.CHANGE,
          this.handleFeatureChange_, false, this);
    }
  }
  this.render_();
};

/**
 * @param {ol.Map} map Map.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setMap = function(map) {
  if (!goog.isNull(this.postComposeListenerKey_)) {
    goog.events.unlistenByKey(this.postComposeListenerKey_);
    this.postComposeListenerKey_ = null;
  }
  this.render_();
  this.map_ = map;
  if (!goog.isNull(map)) {
    this.postComposeListenerKey_ = goog.events.listen(
        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
        this);
    map.render();
  }
};

/**
 * Set the style for features.  This can be a single style object, an array
 * of styles, or a function that takes a feature and resolution and returns
 * an array of styles.
 * @param {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction} style
 *     Overlay style.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setStyle = function(style) {
  this.style_ = style;
  this.styleFunction_ = ol.feature.createStyleFunction(style);
  this.render_();
};

/**
 * Get the style for features.  This returns whatever was passed to the `style`
 * option at construction or to the `setStyle` method.
 * @return {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction}
 *     Overlay style.
 */
ol.FeatureOverlay.prototype.getStyle = function() {
  return this.style_;
};

/**
 * Get the style function.
 * @return {ol.feature.StyleFunction|undefined} Style function.
 */
ol.FeatureOverlay.prototype.getStyleFunction = function() {
  return this.styleFunction_;
};
<commit_msg>Use features.forEach instead of for loop<commit_after>goog.provide('ol.FeatureOverlay');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol.Collection');
goog.require('ol.CollectionEventType');
goog.require('ol.Feature');
goog.require('ol.feature');
goog.require('ol.render.EventType');

/**
 * @constructor
 * @param {olx.FeatureOverlayOptions=} opt_options Options.
 * @todo stability experimental
 */
ol.FeatureOverlay = function(opt_options) {

  var options = goog.isDef(opt_options) ? opt_options : {};

  /**
   * @private
   * @type {ol.Collection}
   */
  this.features_ = null;

  /**
   * @private
   * @type {Array.<goog.events.Key>}
   */
  this.featuresListenerKeys_ = null;

  /**
   * @private
   * @type {Object.<string, goog.events.Key>}
   */
  this.featureChangeListenerKeys_ = null;

  /**
   * @private
   * @type {ol.Map}
   */
  this.map_ = null;

  /**
   * @private
   * @type {goog.events.Key}
   */
  this.postComposeListenerKey_ = null;

  /**
   * @private
   * @type {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction}
   */
  this.style_ = null;

  /**
   * @private
   * @type {ol.feature.StyleFunction|undefined}
   */
  this.styleFunction_ = goog.isDef(options.style) ?
      ol.feature.createStyleFunction(options.style) : undefined;

  if (goog.isDef(options.features)) {
    if (goog.isArray(options.features)) {
      this.setFeatures(new ol.Collection(goog.array.clone(options.features)));
    } else {
      goog.asserts.assertInstanceof(options.features, ol.Collection);
      this.setFeatures(options.features);
    }
  } else {
    this.setFeatures(new ol.Collection());
  }

  if (goog.isDef(options.map)) {
    this.setMap(options.map);
  }

};

/**
 * @param {ol.Feature} feature Feature.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.addFeature = function(feature) {
  this.features_.push(feature);
};

/**
 * @return {ol.Collection} Features collection.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.getFeatures = function() {
  return this.features_;
};

/**
 * @private
 */
ol.FeatureOverlay.prototype.handleFeatureChange_ = function() {
  this.render_();
};

/**
 * @private
 * @param {ol.CollectionEvent} collectionEvent Collection event.
 */
ol.FeatureOverlay.prototype.handleFeaturesAdd_ = function(collectionEvent) {
  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
  this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
      goog.events.listen(feature, goog.events.EventType.CHANGE,
      this.handleFeatureChange_, false, this);
  this.render_();
};

/**
 * @private
 * @param {ol.CollectionEvent} collectionEvent Collection event.
 */
ol.FeatureOverlay.prototype.handleFeaturesRemove_ = function(collectionEvent) {
  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
  var key = goog.getUid(feature).toString();
  goog.events.unlistenByKey(this.featureChangeListenerKeys_[key]);
  delete this.featureChangeListenerKeys_[key];
  this.render_();
};

/**
 * @param {ol.render.Event} event Event.
 * @private
 */
ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) {
  if (goog.isNull(this.features_)) {
    return;
  }
  var styleFunction = this.styleFunction_;
  if (!goog.isDef(styleFunction)) {
    styleFunction = ol.feature.defaultStyleFunction;
  }
  var resolution = event.frameState.view2DState.resolution;
  var vectorContext = event.vectorContext;
  var i, ii, styles;
  this.features_.forEach(function(feature) {
    styles = styleFunction(feature, resolution);
    if (!goog.isDefAndNotNull(styles)) {
      return;
    }
    ii = styles.length;
    for (i = 0; i < ii; ++i) {
      vectorContext.drawFeature(feature, styles[i]);
    }
  });
};

/**
 * @param {ol.Feature} feature Feature.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.removeFeature = function(feature) {
  this.features_.remove(feature);
};

/**
 * @private
 */
ol.FeatureOverlay.prototype.render_ = function() {
  if (!goog.isNull(this.map_)) {
    this.map_.render();
  }
};

/**
 * @param {ol.Collection} features Features collection.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setFeatures = function(features) {
  if (!goog.isNull(this.featuresListenerKeys_)) {
    goog.array.forEach(this.featuresListenerKeys_, goog.events.unlistenByKey);
    this.featuresListenerKeys_ = null;
  }
  if (!goog.isNull(this.featureChangeListenerKeys_)) {
    goog.array.forEach(
        goog.object.getValues(this.featureChangeListenerKeys_),
        goog.events.unlistenByKey);
    this.featureChangeListenerKeys_ = null;
  }
  this.features_ = features;
  if (!goog.isNull(features)) {
    this.featuresListenerKeys_ = [
      goog.events.listen(features, ol.CollectionEventType.ADD,
          this.handleFeaturesAdd_, false, this),
      goog.events.listen(features, ol.CollectionEventType.REMOVE,
          this.handleFeaturesRemove_, false, this)
    ];
    this.featureChangeListenerKeys_ = {};
    features.forEach(function(feature) {
      this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
          goog.events.listen(feature, goog.events.EventType.CHANGE,
          this.handleFeatureChange_, false, this);
    }, this);
  }
  this.render_();
};

/**
 * @param {ol.Map} map Map.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setMap = function(map) {
  if (!goog.isNull(this.postComposeListenerKey_)) {
    goog.events.unlistenByKey(this.postComposeListenerKey_);
    this.postComposeListenerKey_ = null;
  }
  this.render_();
  this.map_ = map;
  if (!goog.isNull(map)) {
    this.postComposeListenerKey_ = goog.events.listen(
        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
        this);
    map.render();
  }
};

/**
 * Set the style for features.  This can be a single style object, an array
 * of styles, or a function that takes a feature and resolution and returns
 * an array of styles.
 * @param {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction} style
 *     Overlay style.
 * @todo stability experimental
 */
ol.FeatureOverlay.prototype.setStyle = function(style) {
  this.style_ = style;
  this.styleFunction_ = ol.feature.createStyleFunction(style);
  this.render_();
};

/**
 * Get the style for features.  This returns whatever was passed to the `style`
 * option at construction or to the `setStyle` method.
 * @return {ol.style.Style|Array.<ol.style.Style>|ol.feature.StyleFunction}
 *     Overlay style.
 */
ol.FeatureOverlay.prototype.getStyle = function() {
  return this.style_;
};

/**
 * Get the style function.
 * @return {ol.feature.StyleFunction|undefined} Style function.
 */
ol.FeatureOverlay.prototype.getStyleFunction = function() {
  return this.styleFunction_;
};
<|endoftext|>


<commit_before>
/*
load forcast of current location on load. Then upon submit/enter return forecast for searched city.
Use Autocomplete API for search results. This will return a Json. list the city names from the Json,
and attach the link associated with each city so it will load the forecast from that city. Populate new
forecast into cards.
*/

"use strict";

angular.module('myapp')
  .controller("WeatherController", function($scope, $http){
    //$scope.forecastDay = [];
    //$scope.forecastNight = [];
    $scope.forecast = [];
    if (navigator.geolocation) navigator.geolocation.getCurrentPosition(onPositionUpdate);

    function onPositionUpdate(position) {
      var lati = position.coords.latitude;
      var longi = position.coords.longitude;
      var url = 'https://api.wunderground.com/api/4a82154c39d11213/forecast/q/' + lati + ',' + longi + '.json';

      $http.get(url)
        .then(function(info) {
          //console.log(info);
          for(var i = 0; i < 8; i++){
            var item = {
              title: info.data.forecast.txt_forecast.forecastday[i].title,
              icon: info.data.forecast.txt_forecast.forecastday[i].icon_url,
              conditionsI: info.data.forecast.txt_forecast.forecastday[i].fcttext,
              conditionsM: info.data.forecast.txt_forecast.forecastday[i].fcttext_metric,
            };
            var temp = item.icon;
            temp = temp.replace("http", "https");
            item.icon = temp;
            $scope.forecast.push(item);

            //if(i%2 === 0){
            //  $scope.forecastDay.push(item);
            //}else{
            //  $scope.forecastNight.push(item);
            //}

            }
            //console.log($scope.forecastDay);
            //console.log($scope.forecastNight);
            //console.log($scope.forecast);
            for(var i = 0; i < $scope.forecast.length; i++){
              var el = $scope.forecast.i.icon;
              console.log(el);
            }
        });
      //$scope.search = function(e){
        //e.preventDefault();

      //};

   }

  });
<commit_msg>Update controller1.js<commit_after>
/*
load forcast of current location on load. Then upon submit/enter return forecast for searched city.
Use Autocomplete API for search results. This will return a Json. list the city names from the Json,
and attach the link associated with each city so it will load the forecast from that city. Populate new
forecast into cards.
*/

"use strict";

angular.module('myapp')
  .controller("WeatherController", function($scope, $http){
    //$scope.forecastDay = [];
    //$scope.forecastNight = [];
    $scope.forecast = [];
    if (navigator.geolocation) navigator.geolocation.getCurrentPosition(onPositionUpdate);

    function onPositionUpdate(position) {
      var lati = position.coords.latitude;
      var longi = position.coords.longitude;
      var url = 'https://api.wunderground.com/api/4a82154c39d11213/forecast/q/' + lati + ',' + longi + '.json';

      $http.get(url)
        .then(function(info) {
          //console.log(info);
          for(var i = 0; i < 8; i++){
            var item = {
              title: info.data.forecast.txt_forecast.forecastday[i].title,
              icon: info.data.forecast.txt_forecast.forecastday[i].icon_url,
              conditionsI: info.data.forecast.txt_forecast.forecastday[i].fcttext,
              conditionsM: info.data.forecast.txt_forecast.forecastday[i].fcttext_metric,
            };
            var temp = item.icon;
            temp = temp.replace("http", "https");
            item.icon = temp;
            $scope.forecast.push(item);
            console.log(temp);

            //if(i%2 === 0){
            //  $scope.forecastDay.push(item);
            //}else{
            //  $scope.forecastNight.push(item);
            //}

            }
            //console.log($scope.forecastDay);
            //console.log($scope.forecastNight);
            //console.log($scope.forecast);

      //$scope.search = function(e){
        //e.preventDefault();

      //};

   }

  });
<|endoftext|>


<commit_before>/*
    MOLECULE @VERSION

    HTML5 game development library by Francisco Santos Belmonte and Christian Alfoni Jørgensen

 */

(function (window) {

    var definedModules = [];
    var moleculeModules = [];
    var initializedModules = [];
    var onceCallbacks = [];
    var logArgs = {};
    var isTest = false;
    var timeoutLimit = 1000;
    var game = null;

    var p = {
        Module: function Module(name, func) {
            this.name = name;
            this.func = func;
        },
        isModule: function (module) {
            return module instanceof p.Module;
        },
        'throw': function (message) {
            throw Error('Molecule Error: ' + message);
        },
        getSinon: function () {
            return window.sinon;
        },
        getModule: function (name, modules) {
            var module;
            for (var x = 0; x < modules.length; x++) {
                if (modules[x].name === name) {
                    module = modules[x];
                    break;
                }
            }
            if (!module) p.throw('Could not require module: "' + name + '". The name does not exist or loading it causes a loop.');
            return module;
        },
        addModule: function (array, name, func) {
            array.push(new p.Module(name, func));
        },
        registerModules: function (defined, initialized) {
            var module,
                context,
                initializeModule = true,
                startTime = new Date().getTime(),
                depExceptions = [];
            while (defined.length && !p.timeout(startTime)) {
                initializeModule = true;
                module = p.getLast(defined);
                context = p.createContext(initialized);
                try {
                    context.exports = module.func.apply(context, p.contextToArray(context));
                } catch (e) {
                    // Dependency not ready
                    if (e.message.match(/Molecule Error/)) {
                        p.addDepException(depExceptions, e.message);
                        p.moveLastToFirst(defined);
                        initializeModule = false;
                    } else {
                        throw e;
                    }
                }
                if (initializeModule && typeof context.exports === 'undefined') {
                    p.throw('Module ' + module.name + ' is returning undefined, it has to return something');
                }
                if (initializeModule) {
                    module.exports = context.exports;
                    p.moveLastToTarget(defined, initialized);
                }
            }

            if (p.timeout(startTime)) {
                p.throw('Timeout, could not load modules. The following dependencies gave errors: ' +
                    (depExceptions.length ? depExceptions.join(', ') : '') +
                    '. They do not exist or has caused a loop.');
            }
        },
        contextToArray: function (context) {
            if (game) {
                return [game, context.require, context.privates];
            } else {
                return [context.require, context.privates];
            }

        },
        registerTestModule: function (name, defined) {
            var module,
                context,
                testModule,
                startTime = new Date().getTime(),
                depExceptions = [];
            while (!testModule && !p.timeout(startTime)) {
                module = p.getLast(defined);
                context = p.createTestContext(defined);
                if (module.name === name) {
                    try {
                        context.exports = module.func.apply(context, p.contextToArray(context));
                    } catch (e) {
                        if (e.message.match(/Molecule Error/)) {
                            p.addDepException(depExceptions, e.message);
                            p.moveLastToFirst(defined);
                        } else {
                            throw e;
                        }
                    }
                    testModule = module;
                } else {
                    p.moveLastToFirst(defined);
                }
            }
            if (p.timeout(startTime)) {
                p.throw('Timeout, could not load modules. The following dependencies gave errors: ' +
                    (depExceptions.length ? depExceptions.join(', ') : name) +
                    '. They do not exist or has caused a loop.');
            }

            if (!context.exports && !depExceptions.length) {
                p.throw('Module ' + testModule.name + ' is returning undefined, it has to return something');
            } else if (depExceptions.length) {
                p.throw('The following dependencies gave errors: ' + depExceptions.join(', ') +
                    '. They do not exist or has caused a loop.');
            }

            return context;

        },
        timeout: function (startTime) {
            return new Date().getTime() - startTime >= timeoutLimit;
        },
        addDepException: function (array, message) {
            message = message.match(/"(.*)"/)[1];
            if (array.indexOf(message) === -1) {
                array.push(message);
            }
        },
        createGame: function (options) {
            var Game = p.getModule('Molecule.Game', initializedModules).exports;
            game = new Game(options);
            game.once = Molecule.once;
            game.log = Molecule.log;
        },
        createContext: function (modules) {
            var context = {
                privates: {},
                require: function (name) {
                    var module = p.getModule(name, modules);
                    return p.isModule(module) ? module.exports : module; // Return exports only if it is a module-loader module
                },
                game: game
            };
            return context;
        },
        createTestContext: function (modules) {
            var context = {
                privates: {},
                deps: {}
            };
            context.require = p.createTestRequireMethod(context, modules);

            return context;
        },
        createTestRequireMethod: function (context, modules) {
            return function (name) {
                var depExceptions = [];
                var depModule = p.getModule(name, modules),
                    depContext = {
                        privates: {},
                        require: function (name) { // TODO: Make this more general with registerModule

                            var module = p.getModule(name, modules);

                            try {
                                module = module.func.apply(context, p.contextToArray(context));
                            } catch (e) {
                                if (e.message.match(/Molecule Error/)) {
                                    p.addDepException(depExceptions, e.message);
                                } else {
                                    throw e;
                                }
                            }

                            return p.isModule(module) ? module.exports : module; // Return exports only if it is a module-loader module

                        }
                    };

                depContext.exports = p.isModule(depModule) ? depModule.func.apply(depContext, p.contextToArray(depContext)) : depModule;

                // Adds the dependency exports to the main context
                // which lets you edit the stubs in the test
                depModule.exports = p.stubDepExports(depContext.exports);
                context.deps[name] = depModule.exports;

                return depModule.exports;
            };
        },
        stubDepExports: function (exports) {
            var sinon = p.getSinon();
            if (sinon) {
                var stubbedMethods = {};

                if (typeof exports === 'function') {
                    return sinon.spy();
                } else {
                    for (var depMethod in exports) {
                        if (!exports.hasOwnProperty(depMethod)) {
                            continue;
                        }
                        if (typeof exports[depMethod] === 'function') {
                            stubbedMethods[depMethod] = exports[depMethod];
                            sinon.stub(stubbedMethods, depMethod);
                        }
                    }
                }

                return stubbedMethods;
            }
            return exports;
        },
        getLast: function (modules) {
            return modules[modules.length - 1];
        },
        moveLastToFirst: function (modules) {
            modules.unshift(modules.pop());
        },
        moveLastToTarget: function (sourceArray, targetArray) {
            targetArray.push(sourceArray.pop());
        },
        extractBrowserArgs: function (args) {
            return {
                name: args[0],
                func: args[1]
            };
        }
    };

    var Molecule = function (options) {
        p.registerModules(moleculeModules, initializedModules);
        p.createGame(options);
        return Molecule;
    };

    Molecule.module = function () {
        var args = p.extractBrowserArgs(arguments);
        if (!args.name || typeof args.name !== 'string' || !args.func || typeof args.func !== 'function') {
            p.throw('Invalid arguments for module creation, you have to pass a string and a function');
        }
        if (args.name.match(/Molecule/)) {
            p.addModule(moleculeModules, args.name, args.func);
        } else {
            p.addModule(definedModules, args.name, args.func);
        }

        return this;

    };

    Molecule.init = function (callback) {
        var initializeModules = function () {
            p.registerModules(definedModules, initializedModules);
        };
        game.init(initializeModules, callback);
        game.start();
        return this;

    };

    Molecule.update = function (callback) {
        game.update(callback);
        return this;
    };

    Molecule.ready = function (callback) {
        var initializeModules = function () {
            p.registerModules(definedModules, initializedModules);
        };
        game.init(initializeModules, callback);
        game.start();
        return game;
    };

    Molecule.sprite = function (id, spriteSrc, frameWidth, frameHeight) {
        game.imageFile.load(id, spriteSrc, frameWidth, frameHeight);
        return this;
    };

    Molecule.audio = function (id, soundSrc) {
        game.audioFile.load(id, soundSrc);
        return this;
    };

    Molecule.webaudio = function (id, soundSrc) {
        game.webAudioFile.load(id, soundSrc);
        return this;
    };

    Molecule.tilemap = function (id, mapSrc) {
        game.mapFile.load(id, mapSrc);
        return this;
    };

    Molecule.spritesheet = function (id, sprites) {
        game.spriteSheetFile.load(id, sprites);
        return this;
    };

    Molecule.test = function (name, callback) {
        isTest = true;
        var context = p.registerTestModule(name, definedModules);
        callback.apply(context, [context.exports, context.privates, context.deps]);
    };

    Molecule.once = function (func, context) {
        var funcString = func.toString();
        if (onceCallbacks.indexOf(funcString) === -1) {
            func.call(context);
            onceCallbacks.push(funcString);
        }
    };

    Molecule.log = function (id) {
        var args = Array.prototype.slice.call(arguments, 0),
            argsString;

        if (typeof id !== 'string' || arguments.length < 2) {
            throw new Error('You have to pass a string identifier and your arguments to log');
        }

        args.splice(0, 1);
        argsString = JSON.stringify(args);

        if (!logArgs[id] || logArgs[id] !== argsString) {
            args.unshift(id.toUpperCase() + ': ');
            args.forEach(function (arg, index) {
                if (arg instanceof Array || (typeof arg === 'object' && arg !== null)) {
                    args[index] = JSON.stringify(arg, null, 4);
                }
            });
            console.log.apply(console, args);
            logArgs[id] = argsString;
        }

    };

    window.Molecule = Molecule;

}(window));<commit_msg>Increased module loader timeout<commit_after>/*
    MOLECULE @VERSION

    HTML5 game development library by Francisco Santos Belmonte and Christian Alfoni Jørgensen

 */

(function (window) {

    var definedModules = [];
    var moleculeModules = [];
    var initializedModules = [];
    var onceCallbacks = [];
    var logArgs = {};
    var isTest = false;
    var timeoutLimit = 2000;
    var game = null;

    var p = {
        Module: function Module(name, func) {
            this.name = name;
            this.func = func;
        },
        isModule: function (module) {
            return module instanceof p.Module;
        },
        'throw': function (message) {
            throw Error('Molecule Error: ' + message);
        },
        getSinon: function () {
            return window.sinon;
        },
        getModule: function (name, modules) {
            var module;
            for (var x = 0; x < modules.length; x++) {
                if (modules[x].name === name) {
                    module = modules[x];
                    break;
                }
            }
            if (!module) p.throw('Could not require module: "' + name + '". The name does not exist or loading it causes a loop.');
            return module;
        },
        addModule: function (array, name, func) {
            array.push(new p.Module(name, func));
        },
        registerModules: function (defined, initialized) {
            var module,
                context,
                initializeModule = true,
                startTime = new Date().getTime(),
                depExceptions = [];
            while (defined.length && !p.timeout(startTime)) {
                initializeModule = true;
                module = p.getLast(defined);
                context = p.createContext(initialized);
                try {
                    context.exports = module.func.apply(context, p.contextToArray(context));
                } catch (e) {
                    // Dependency not ready
                    if (e.message.match(/Molecule Error/)) {
                        p.addDepException(depExceptions, e.message);
                        p.moveLastToFirst(defined);
                        initializeModule = false;
                    } else {
                        throw e;
                    }
                }
                if (initializeModule && typeof context.exports === 'undefined') {
                    p.throw('Module ' + module.name + ' is returning undefined, it has to return something');
                }
                if (initializeModule) {
                    module.exports = context.exports;
                    p.moveLastToTarget(defined, initialized);
                }
            }

            if (p.timeout(startTime)) {
                p.throw('Timeout, could not load modules. The following dependencies gave errors: ' +
                    (depExceptions.length ? depExceptions.join(', ') : '') +
                    '. They do not exist or has caused a loop.');
            }
        },
        contextToArray: function (context) {
            if (game) {
                return [game, context.require, context.privates];
            } else {
                return [context.require, context.privates];
            }

        },
        registerTestModule: function (name, defined) {
            var module,
                context,
                testModule,
                startTime = new Date().getTime(),
                depExceptions = [];
            while (!testModule && !p.timeout(startTime)) {
                module = p.getLast(defined);
                context = p.createTestContext(defined);
                if (module.name === name) {
                    try {
                        context.exports = module.func.apply(context, p.contextToArray(context));
                    } catch (e) {
                        if (e.message.match(/Molecule Error/)) {
                            p.addDepException(depExceptions, e.message);
                            p.moveLastToFirst(defined);
                        } else {
                            throw e;
                        }
                    }
                    testModule = module;
                } else {
                    p.moveLastToFirst(defined);
                }
            }
            if (p.timeout(startTime)) {
                p.throw('Timeout, could not load modules. The following dependencies gave errors: ' +
                    (depExceptions.length ? depExceptions.join(', ') : name) +
                    '. They do not exist or has caused a loop.');
            }

            if (!context.exports && !depExceptions.length) {
                p.throw('Module ' + testModule.name + ' is returning undefined, it has to return something');
            } else if (depExceptions.length) {
                p.throw('The following dependencies gave errors: ' + depExceptions.join(', ') +
                    '. They do not exist or has caused a loop.');
            }

            return context;

        },
        timeout: function (startTime) {
            return new Date().getTime() - startTime >= timeoutLimit;
        },
        addDepException: function (array, message) {
            message = message.match(/"(.*)"/)[1];
            if (array.indexOf(message) === -1) {
                array.push(message);
            }
        },
        createGame: function (options) {
            var Game = p.getModule('Molecule.Game', initializedModules).exports;
            game = new Game(options);
            game.once = Molecule.once;
            game.log = Molecule.log;
        },
        createContext: function (modules) {
            var context = {
                privates: {},
                require: function (name) {
                    var module = p.getModule(name, modules);
                    return p.isModule(module) ? module.exports : module; // Return exports only if it is a module-loader module
                },
                game: game
            };
            return context;
        },
        createTestContext: function (modules) {
            var context = {
                privates: {},
                deps: {}
            };
            context.require = p.createTestRequireMethod(context, modules);

            return context;
        },
        createTestRequireMethod: function (context, modules) {
            return function (name) {
                var depExceptions = [];
                var depModule = p.getModule(name, modules),
                    depContext = {
                        privates: {},
                        require: function (name) { // TODO: Make this more general with registerModule

                            var module = p.getModule(name, modules);

                            try {
                                module = module.func.apply(context, p.contextToArray(context));
                            } catch (e) {
                                if (e.message.match(/Molecule Error/)) {
                                    p.addDepException(depExceptions, e.message);
                                } else {
                                    throw e;
                                }
                            }

                            return p.isModule(module) ? module.exports : module; // Return exports only if it is a module-loader module

                        }
                    };

                depContext.exports = p.isModule(depModule) ? depModule.func.apply(depContext, p.contextToArray(depContext)) : depModule;

                // Adds the dependency exports to the main context
                // which lets you edit the stubs in the test
                depModule.exports = p.stubDepExports(depContext.exports);
                context.deps[name] = depModule.exports;

                return depModule.exports;
            };
        },
        stubDepExports: function (exports) {
            var sinon = p.getSinon();
            if (sinon) {
                var stubbedMethods = {};

                if (typeof exports === 'function') {
                    return sinon.spy();
                } else {
                    for (var depMethod in exports) {
                        if (!exports.hasOwnProperty(depMethod)) {
                            continue;
                        }
                        if (typeof exports[depMethod] === 'function') {
                            stubbedMethods[depMethod] = exports[depMethod];
                            sinon.stub(stubbedMethods, depMethod);
                        }
                    }
                }

                return stubbedMethods;
            }
            return exports;
        },
        getLast: function (modules) {
            return modules[modules.length - 1];
        },
        moveLastToFirst: function (modules) {
            modules.unshift(modules.pop());
        },
        moveLastToTarget: function (sourceArray, targetArray) {
            targetArray.push(sourceArray.pop());
        },
        extractBrowserArgs: function (args) {
            return {
                name: args[0],
                func: args[1]
            };
        }
    };

    var Molecule = function (options) {
        p.registerModules(moleculeModules, initializedModules);
        p.createGame(options);
        return Molecule;
    };

    Molecule.module = function () {
        var args = p.extractBrowserArgs(arguments);
        if (!args.name || typeof args.name !== 'string' || !args.func || typeof args.func !== 'function') {
            p.throw('Invalid arguments for module creation, you have to pass a string and a function');
        }
        if (args.name.match(/Molecule/)) {
            p.addModule(moleculeModules, args.name, args.func);
        } else {
            p.addModule(definedModules, args.name, args.func);
        }

        return this;

    };

    Molecule.init = function (callback) {
        var initializeModules = function () {
            p.registerModules(definedModules, initializedModules);
        };
        game.init(initializeModules, callback);
        game.start();
        return this;

    };

    Molecule.update = function (callback) {
        game.update(callback);
        return this;
    };

    Molecule.ready = function (callback) {
        var initializeModules = function () {
            p.registerModules(definedModules, initializedModules);
        };
        game.init(initializeModules, callback);
        game.start();
        return game;
    };

    Molecule.sprite = function (id, spriteSrc, frameWidth, frameHeight) {
        game.imageFile.load(id, spriteSrc, frameWidth, frameHeight);
        return this;
    };

    Molecule.audio = function (id, soundSrc) {
        game.audioFile.load(id, soundSrc);
        return this;
    };

    Molecule.webaudio = function (id, soundSrc) {
        game.webAudioFile.load(id, soundSrc);
        return this;
    };

    Molecule.tilemap = function (id, mapSrc) {
        game.mapFile.load(id, mapSrc);
        return this;
    };

    Molecule.spritesheet = function (id, sprites) {
        game.spriteSheetFile.load(id, sprites);
        return this;
    };

    Molecule.test = function (name, callback) {
        isTest = true;
        var context = p.registerTestModule(name, definedModules);
        callback.apply(context, [context.exports, context.privates, context.deps]);
    };

    Molecule.once = function (func, context) {
        var funcString = func.toString();
        if (onceCallbacks.indexOf(funcString) === -1) {
            func.call(context);
            onceCallbacks.push(funcString);
        }
    };

    Molecule.log = function (id) {
        var args = Array.prototype.slice.call(arguments, 0),
            argsString;

        if (typeof id !== 'string' || arguments.length < 2) {
            throw new Error('You have to pass a string identifier and your arguments to log');
        }

        args.splice(0, 1);
        argsString = JSON.stringify(args);

        if (!logArgs[id] || logArgs[id] !== argsString) {
            args.unshift(id.toUpperCase() + ': ');
            args.forEach(function (arg, index) {
                if (arg instanceof Array || (typeof arg === 'object' && arg !== null)) {
                    args[index] = JSON.stringify(arg, null, 4);
                }
            });
            console.log.apply(console, args);
            logArgs[id] = argsString;
        }

    };

    window.Molecule = Molecule;

}(window));<|endoftext|>