<commit_before>/*global define*/
define(['../Core/createGuid',
        '../Core/Cartographic',
        '../Core/ClockRange',
        '../Core/ClockStep',
        '../Core/DeveloperError',
        '../Core/RuntimeError',
        '../Core/Ellipsoid',
        '../Core/Event',
        '../Core/Iso8601',
        '../Core/loadXML',
        './DynamicClock',
        './DynamicObjectCollection',
        './DynamicPoint',
        './DynamicPolyline',
        './DynamicPolygon',
        './DynamicLabel',
        './DynamicBillboard'
        ], function(
                createGuid,
                Cartographic,
                ClockRange,
                ClockStep,
                DeveloperError,
                RuntimeError,
                Ellipsoid,
                Event,
                Iso8601,
                loadXML,
                DynamicClock,
                DynamicObjectCollection,
                DynamicPoint,
                DynamicPolyline,
                DynamicPolygon,
                DynamicLabel,
                DynamicBillboard) {
    "use strict";

    //Copied from GeoJsonDataSource
    var ConstantPositionProperty = function(value) {
        this._value = value;
    };

    ConstantPositionProperty.prototype.getValueCartesian = function(time, result) {
        var value = this._value;
        if (typeof value.clone === 'function') {
            return value.clone(result);
        }
        return value;
    };

    ConstantPositionProperty.prototype.setValue = function(value) {
        this._value = value;
    };

    function createObject(kml, dynamicObjectCollection) {
        var id = kml.id;
        if (typeof id === 'undefined') {
            id = createGuid();
        } else {
            var i = 2;
            var finalId = id;
            while (typeof dynamicObjectCollection.getObject(finalId) !== 'undefined') {
                finalId = id + "_" + i;
                i++;
            }
            id = finalId;
        }
        var dynamicObject = dynamicObjectCollection.getOrCreateObject(id);
        dynamicObject.kml = kml;
        return dynamicObject;
    }

    function readCoordinates(el) {
        var text = "", coords = [], i;
        for (i = 0; i < el.childNodes.length; i++) {
            text = text + el.childNodes[i].nodeValue;
        }
        text = text.split(/[\s\n]+/);
        for (i = 0; i < text.length; i++) {
            var ll = text[i].split(',');
            if (ll.length < 2) {
                continue;
            }
            coords.push(ll[0]);
            coords.push(ll[1]);
            if(typeof ll[2] === 'undefined'){
                coords.push(ll[2]); //defaultValue will handle it
            } else {
                coords.push(parseInt(ll[2], 10));
            }
        }
        return coords;
    }

    // KML processing functions
    function processPlacemark(dataSource, placemark, dynamicObjectCollection) {
        var objectId = placemark.id;
        if (typeof objectId === 'undefined') {
            objectId = createGuid();
        }
        dynamicObjectCollection.getOrCreateObject(objectId); //dataSource._dynamicObjectCollection...?

        // I want to iterate over every placemark
        for(var i = 0, len = placemark.childNodes.length; i < len; i++){
            var node = placemark.childNodes.item(i);
            //Checking if the node holds a supported Geometry type
            if(geometryTypes.hasOwnProperty(node.nodeName)){
                placemark.geometry = node.nodeName;
                var geometryType = placemark.geometry;
                var geometryHandler = geometryTypes[geometryType];
                if (typeof geometryHandler === 'undefined') {
                    throw new RuntimeError('Unknown geometry type: ' + geometryType);
                }
                geometryHandler(dataSource, placemark, node);
            }
        }
    }

    function processPoint(dataSource, kml, node) {
        var el = node.getElementsByTagName('coordinates');
        var coordinates = [];
        for (var j = 0; j < el.length; j++) {
        // text might span many childnodes
            coordinates = coordinates.concat(readCoordinates(el[j]));
        }

        var cartographic = Cartographic.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
        var cartesian3 = Ellipsoid.WGS84.cartographicToCartesian(cartographic);
        var dynamicObject = createObject(node, dataSource._dynamicObjectCollection);
        //dynamicObject.merge(dataSource.defaultPoint);  What are the defaults for KML?
        dynamicObject.position = new ConstantPositionProperty(cartesian3);

        //add the new dynamicObject to dataSource._dynamicObjectCollection?
    }

    function processLineString(dataSource, kml, node){

    }

    function processLinearRing(dataSource, kml, node){

    }

    function processPolygon(dataSource, kml, node){

    }

    function processMultiGeometry(dataSource, kml, node){

    }

    function processModel(dataSource, kml, node){

    }

    function processGxTrack(dataSource, kml, node){

    }

    function processGxMultiTrack(dataSource, kml, node){

    }
    //Object that holds all supported Geometry
    var geometryTypes = {
            Point : processPoint,
            LineString : processLineString,
            LinearRing : processLinearRing,
            Polygon : processPolygon,
            MultiGeometry : processMultiGeometry,
            Model : processModel,
            gxTrack : processGxTrack,
            gxMultitrack : processGxMultiTrack
        };

    //First, create a function that takes a styleNode and creates equivalent dynamic object properties.
    function processStyle(styleNode, dynamicObject) {
        if(styleNode.hasOwnProperty("IconStyle")){
            dynamicObject.billboard = new DynamicBillboard();

         //Map style to billboard properties
        }
        if(styleNode.hasOwnProperty("LabelStyle"))   {
          dynamicObject.label = new DynamicLabel();

         //Map style to label properties
        }
        if(styleNode.hasOwnProperty("LineStyle"))   {
          dynamicObject.polyline = new DynamicPolyline();

          //Map style to line properties
        }
        if(styleNode.hasOwnProperty("PolyStyle"))   {
          dynamicObject.polygon = new DynamicPolygon();
         //Map style to polygon properties
       }

//
//
//
//    //On load, iterate over all styles
//
//    //Keep a special collection just for styles.
//
//    var  styleCollection = new DynamicObjectCollection();
//
//
//
//    For each (style in list of stylesNodes) {
//
//       var styleObject = styleCollection.getOrCreateObject(style.id);
//
//      processStyle(style, styleObject);
//
//    }
//
//
//
//    //Then, when iterating placemarks you do something like this
//
//    If(placemark node has embedded style) {
//
//       //process the style directly
//
//      processStyle(styleNode, placemarkDynamicObject);
//
//    } else {
//
//      //Shared style uri, so get the already processed style and merge it with this object.
//
//      var styleObject = styleCollection.getObject(style Uri);
//
//       placemarkDynamicObject.merge(styleObject);

    }

    function processFolder(){

    }

    function loadKML(dataSource, kml, sourceUri) {
        var dynamicObjectCollection = dataSource._dynamicObjectCollection;

        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }
        if (typeof dynamicObjectCollection === 'undefined') {
            throw new DeveloperError('dynamicObjectCollection is required.');
        }

        var array = kml.getElementsByTagName('Folder');
        for ( var i = 0, len = array.length; i < len; i++){
            processFolder(dataSource, array[i], dynamicObjectCollection);
        }

        array = kml.getElementsByTagName('Placemark');
        for (i = 0, len = array.length; i < len; i++){
            processPlacemark(dataSource, array[i], dynamicObjectCollection);
        }

        /*
        var availability = dynamicObjectCollection.computeAvailability();

        var clock;
        var documentObject = dynamicObjectCollection.getObject('document');
        if (typeof documentObject !== 'undefined' && typeof documentObject.clock !== 'undefined') {
            clock = new DynamicClock();
            clock.startTime = documentObject.clock.startTime;
            clock.stopTime = documentObject.clock.stopTime;
            clock.clockRange = documentObject.clock.clockRange;
            clock.clockStep = documentObject.clock.clockStep;
            clock.multiplier = documentObject.clock.multiplier;
            clock.currentTime = documentObject.clock.currentTime;
        } else if (!availability.start.equals(Iso8601.MINIMUM_VALUE)) {
            clock = new DynamicClock();
            clock.startTime = availability.start;
            clock.stopTime = availability.stop;
            clock.clockRange = ClockRange.LOOP_STOP;
            var totalSeconds = clock.startTime.getSecondsDifference(clock.stopTime);
            var multiplier = Math.round(totalSeconds / 120.0);
            clock.multiplier = multiplier;
            clock.currentTime = clock.startTime;
            clock.clockStep = ClockStep.SYSTEM_CLOCK_MULTIPLIER;
        }
        return clock;*/
    }

    /**
     * A {@link DataSource} which processes KML.
     * @alias KmlDataSource
     * @constructor
     */
    var KmlDataSource = function(){
        this._changed = new Event();
        this._error = new Event();
        this._clock = undefined;
        this._dynamicObjectCollection = new DynamicObjectCollection();
        this._timeVarying = true;
    };

    /* The following functions were copied from CzmlDataSource.js, just replaced CZML with KML */
    /**
     * Gets an event that will be raised when non-time-varying data changes
     * or if the return value of getIsTimeVarying changes.
     * @memberof DataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getChangedEvent = function() {
        return this._changed;
    };

    /**
     * Gets an event that will be raised if an error is encountered during processing.
     * @memberof KmlDataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getErrorEvent = function() {
        return this._error;
    };

    /**
     * Gets the top level clock defined in KML or the availability of the
     * underlying data if no clock is defined.  If the KML document only contains
     * infinite data, undefined will be returned.
     * @memberof KmlDataSource
     *
     * @returns {DynamicClock} The clock associated with the current KML data, or undefined if none exists.
     */
    KmlDataSource.prototype.getClock = function() {
        return undefined;
    };

    /**
     * Gets the DynamicObjectCollection generated by this data source.
     * @memberof DataSource
     *
     * @returns {DynamicObjectCollection} The collection of objects generated by this data source.
     */
    KmlDataSource.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Gets a value indicating if the data varies with simulation time.  If the return value of
     * this function changes, the changed event will be raised.
     * @memberof DataSource
     *
     * @returns {Boolean} True if the data is varies with simulation time, false otherwise.
     */
    KmlDataSource.prototype.getIsTimeVarying = function() {
        return false;
    };

    /**
     * Replaces any existing data with the provided KML.
     *
     * @param {Object} KML The KML to be processed.
     * @param {String} source The source of the KML.
     *
     * @exception {DeveloperError} KML is required.
     */
    KmlDataSource.prototype.load = function(kml, source) {
        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }

        this._dynamicObjectCollection.clear();
        loadKML(this, kml, source);
    };

    /**
     * Asynchronously loads the KML at the provided url, replacing any existing data.
     *
     * @param {Object} url The url to be processed.
     *
     * @returns {Promise} a promise that will resolve when the KML is processed.
     *
     * @exception {DeveloperError} url is required.
     */
    KmlDataSource.prototype.loadUrl = function(url) {
        if (typeof url === 'undefined') {
            throw new DeveloperError('url is required.');
        }

        var dataSource = this;
        return loadXML(url).then(function(kml) {
            dataSource.load(kml, url);
        }, function(error) {
            this._error.raiseEvent(this, error);
        });
    };

    return KmlDataSource;
});<commit_msg>Add Styles to loadKML<commit_after>/*global define*/
define(['../Core/createGuid',
        '../Core/Cartographic',
        '../Core/ClockRange',
        '../Core/ClockStep',
        '../Core/DeveloperError',
        '../Core/RuntimeError',
        '../Core/Ellipsoid',
        '../Core/Event',
        '../Core/Iso8601',
        '../Core/loadXML',
        './DynamicClock',
        './DynamicObjectCollection',
        './DynamicPoint',
        './DynamicPolyline',
        './DynamicPolygon',
        './DynamicLabel',
        './DynamicBillboard'
        ], function(
                createGuid,
                Cartographic,
                ClockRange,
                ClockStep,
                DeveloperError,
                RuntimeError,
                Ellipsoid,
                Event,
                Iso8601,
                loadXML,
                DynamicClock,
                DynamicObjectCollection,
                DynamicPoint,
                DynamicPolyline,
                DynamicPolygon,
                DynamicLabel,
                DynamicBillboard) {
    "use strict";

    //Copied from GeoJsonDataSource
    var ConstantPositionProperty = function(value) {
        this._value = value;
    };

    ConstantPositionProperty.prototype.getValueCartesian = function(time, result) {
        var value = this._value;
        if (typeof value.clone === 'function') {
            return value.clone(result);
        }
        return value;
    };

    ConstantPositionProperty.prototype.setValue = function(value) {
        this._value = value;
    };

    function createObject(kml, dynamicObjectCollection) {
        var id = kml.id;
        if (typeof id === 'undefined') {
            id = createGuid();
        } else {
            var i = 2;
            var finalId = id;
            while (typeof dynamicObjectCollection.getObject(finalId) !== 'undefined') {
                finalId = id + "_" + i;
                i++;
            }
            id = finalId;
        }
        var dynamicObject = dynamicObjectCollection.getOrCreateObject(id);
        dynamicObject.kml = kml;
        return dynamicObject;
    }

    function readCoordinates(el) {
        var text = "", coords = [], i;
        for (i = 0; i < el.childNodes.length; i++) {
            text = text + el.childNodes[i].nodeValue;
        }
        text = text.split(/[\s\n]+/);
        for (i = 0; i < text.length; i++) {
            var ll = text[i].split(',');
            if (ll.length < 2) {
                continue;
            }
            coords.push(ll[0]);
            coords.push(ll[1]);
            if(typeof ll[2] === 'undefined'){
                coords.push(ll[2]); //defaultValue will handle it
            } else {
                coords.push(parseInt(ll[2], 10));
            }
        }
        return coords;
    }

    // KML processing functions
    function processPlacemark(dataSource, placemark, dynamicObjectCollection) {
        var objectId = placemark.id;
        if (typeof objectId === 'undefined') {
            objectId = createGuid();
        }
        dynamicObjectCollection.getOrCreateObject(objectId); //dataSource._dynamicObjectCollection...?

        // I want to iterate over every placemark
        for(var i = 0, len = placemark.childNodes.length; i < len; i++){
            var node = placemark.childNodes.item(i);
            //Checking if the node holds a supported Geometry type
            if(geometryTypes.hasOwnProperty(node.nodeName)){
                placemark.geometry = node.nodeName;
                var geometryType = placemark.geometry;
                var geometryHandler = geometryTypes[geometryType];
                if (typeof geometryHandler === 'undefined') {
                    throw new RuntimeError('Unknown geometry type: ' + geometryType);
                }
                geometryHandler(dataSource, placemark, node);
            }
        }
    }

    function processPoint(dataSource, kml, node) {
        var el = node.getElementsByTagName('coordinates');
        var coordinates = [];
        for (var j = 0; j < el.length; j++) {
        // text might span many childnodes
            coordinates = coordinates.concat(readCoordinates(el[j]));
        }

        var cartographic = Cartographic.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
        var cartesian3 = Ellipsoid.WGS84.cartographicToCartesian(cartographic);
        var dynamicObject = createObject(node, dataSource._dynamicObjectCollection);
        //dynamicObject.merge(dataSource.defaultPoint);  What are the defaults for KML?
        dynamicObject.position = new ConstantPositionProperty(cartesian3);

        //add the new dynamicObject to dataSource._dynamicObjectCollection?
    }

    function processLineString(dataSource, kml, node){

    }

    function processLinearRing(dataSource, kml, node){

    }

    function processPolygon(dataSource, kml, node){

    }

    function processMultiGeometry(dataSource, kml, node){

    }

    function processModel(dataSource, kml, node){

    }

    function processGxTrack(dataSource, kml, node){

    }

    function processGxMultiTrack(dataSource, kml, node){

    }
    //Object that holds all supported Geometry
    var geometryTypes = {
            Point : processPoint,
            LineString : processLineString,
            LinearRing : processLinearRing,
            Polygon : processPolygon,
            MultiGeometry : processMultiGeometry,
            Model : processModel,
            gxTrack : processGxTrack,
            gxMultitrack : processGxMultiTrack
        };

    //First, create a function that takes a styleNode and creates equivalent dynamic object properties.
    function processStyle(styleNode, dynamicObject) {
        if(styleNode.hasOwnProperty("IconStyle")){
            dynamicObject.billboard = new DynamicBillboard();
            //Map style to billboard properties
        }
        if(styleNode.hasOwnProperty("LabelStyle"))   {
          dynamicObject.label = new DynamicLabel();
          //Map style to label properties
        }
        if(styleNode.hasOwnProperty("LineStyle"))   {
          dynamicObject.polyline = new DynamicPolyline();
          //Map style to line properties
        }
        if(styleNode.hasOwnProperty("PolyStyle"))   {
          dynamicObject.polygon = new DynamicPolygon();
          //Map style to polygon properties
       }
    }

    function processFolder(){

    }

    function loadKML(dataSource, kml, sourceUri) {
        var dynamicObjectCollection = dataSource._dynamicObjectCollection;

        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }
        if (typeof dynamicObjectCollection === 'undefined') {
            throw new DeveloperError('dynamicObjectCollection is required.');
        }

        var stylesArray = kml.getElementsByTagName('Style');
        var styleCollection = new DynamicObjectCollection();
        for ( var i = 0, len = stylesArray.length; i < len; i++){
            var styleNode = stylesArray.item(i);
            styleNode.id = stylesArray.item(i).attributes[0].nodeValue;
            var styleObject = styleCollection.getOrCreateObject(styleNode.id);

            processStyle(styleNode, styleObject);
        }

        var array = kml.getElementsByTagName('Folder');
        for (i = 0, len = array.length; i < len; i++){
            processFolder(dataSource, array[i], dynamicObjectCollection);
        }

        array = kml.getElementsByTagName('Placemark');
//      //Then, when iterating placemarks you do something like this
//
//            If(placemark node has embedded style) {
//
//               //process the style directly
//
//              processStyle(styleNode, placemarkDynamicObject);
//
//            } else {
//
//              //Shared style uri, so get the already processed style and merge it with this object.
//
//              var styleObject = styleCollection.getObject(style Uri);
//
//               placemarkDynamicObject.merge(styleObject);
        for (i = 0, len = array.length; i < len; i++){
            processPlacemark(dataSource, array[i], dynamicObjectCollection);
        }

        /*
        var availability = dynamicObjectCollection.computeAvailability();

        var clock;
        var documentObject = dynamicObjectCollection.getObject('document');
        if (typeof documentObject !== 'undefined' && typeof documentObject.clock !== 'undefined') {
            clock = new DynamicClock();
            clock.startTime = documentObject.clock.startTime;
            clock.stopTime = documentObject.clock.stopTime;
            clock.clockRange = documentObject.clock.clockRange;
            clock.clockStep = documentObject.clock.clockStep;
            clock.multiplier = documentObject.clock.multiplier;
            clock.currentTime = documentObject.clock.currentTime;
        } else if (!availability.start.equals(Iso8601.MINIMUM_VALUE)) {
            clock = new DynamicClock();
            clock.startTime = availability.start;
            clock.stopTime = availability.stop;
            clock.clockRange = ClockRange.LOOP_STOP;
            var totalSeconds = clock.startTime.getSecondsDifference(clock.stopTime);
            var multiplier = Math.round(totalSeconds / 120.0);
            clock.multiplier = multiplier;
            clock.currentTime = clock.startTime;
            clock.clockStep = ClockStep.SYSTEM_CLOCK_MULTIPLIER;
        }
        return clock;*/
    }

    /**
     * A {@link DataSource} which processes KML.
     * @alias KmlDataSource
     * @constructor
     */
    var KmlDataSource = function(){
        this._changed = new Event();
        this._error = new Event();
        this._clock = undefined;
        this._dynamicObjectCollection = new DynamicObjectCollection();
        this._timeVarying = true;
    };

    /* The following functions were copied from CzmlDataSource.js, just replaced CZML with KML */
    /**
     * Gets an event that will be raised when non-time-varying data changes
     * or if the return value of getIsTimeVarying changes.
     * @memberof DataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getChangedEvent = function() {
        return this._changed;
    };

    /**
     * Gets an event that will be raised if an error is encountered during processing.
     * @memberof KmlDataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getErrorEvent = function() {
        return this._error;
    };

    /**
     * Gets the top level clock defined in KML or the availability of the
     * underlying data if no clock is defined.  If the KML document only contains
     * infinite data, undefined will be returned.
     * @memberof KmlDataSource
     *
     * @returns {DynamicClock} The clock associated with the current KML data, or undefined if none exists.
     */
    KmlDataSource.prototype.getClock = function() {
        return undefined;
    };

    /**
     * Gets the DynamicObjectCollection generated by this data source.
     * @memberof DataSource
     *
     * @returns {DynamicObjectCollection} The collection of objects generated by this data source.
     */
    KmlDataSource.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Gets a value indicating if the data varies with simulation time.  If the return value of
     * this function changes, the changed event will be raised.
     * @memberof DataSource
     *
     * @returns {Boolean} True if the data is varies with simulation time, false otherwise.
     */
    KmlDataSource.prototype.getIsTimeVarying = function() {
        return false;
    };

    /**
     * Replaces any existing data with the provided KML.
     *
     * @param {Object} KML The KML to be processed.
     * @param {String} source The source of the KML.
     *
     * @exception {DeveloperError} KML is required.
     */
    KmlDataSource.prototype.load = function(kml, source) {
        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }

        this._dynamicObjectCollection.clear();
        loadKML(this, kml, source);
    };

    /**
     * Asynchronously loads the KML at the provided url, replacing any existing data.
     *
     * @param {Object} url The url to be processed.
     *
     * @returns {Promise} a promise that will resolve when the KML is processed.
     *
     * @exception {DeveloperError} url is required.
     */
    KmlDataSource.prototype.loadUrl = function(url) {
        if (typeof url === 'undefined') {
            throw new DeveloperError('url is required.');
        }

        var dataSource = this;
        return loadXML(url).then(function(kml) {
            dataSource.load(kml, url);
        }, function(error) {
            this._error.raiseEvent(this, error);
        });
    };

    return KmlDataSource;
});<|endoftext|>


<commit_before>(function () {
  "use strict";

  angular
    .module("mnPoll", [
      "mnTasksDetails",
      "mnPromiseHelper"
    ])
    .factory("mnPoller", mnPollerFactory)
    .factory("mnEtagPoller", mnEtagPollerFactory);

  function mnEtagPollerFactory(mnPoller) {
    function EtagPoller(scope, request) {
      mnPoller.call(this, scope, request);
    }

    EtagPoller.prototype = Object.create(mnPoller.prototype);
    EtagPoller.prototype.constructor = EtagPoller;
    EtagPoller.prototype.cycle = cycle;

    return EtagPoller;

    function cycle() {
      var self = this;
      var timestamp = new Date();
      self.doCallPromise = self.doCall(timestamp);
      self.doCallPromise.then(function () {
        if (self.isStopped(timestamp)) {
          return;
        }
        self.cycle();
      });
      return self;
    }
  }

  function mnPollerFactory($q, $timeout, mnTasksDetails, mnPromiseHelper) {

    function Poller(scope, request) {
      this.deferred = $q.defer();
      this.request = request;
      this.scope = scope;

      scope.$on('$destroy', this.stop.bind(this));

      this.latestResult = undefined;
      this.stopTimestamp = undefined;
      this.extractInterval = undefined;
      this.timeout = undefined;
      this.doCallPromise = undefined;
    }

    Poller.prototype.isStopped = isStopped;
    Poller.prototype.doCall = doCall;
    Poller.prototype.setInterval = setInterval;
    Poller.prototype.cycle = cycle;
    Poller.prototype.doCycle = doCycle;
    Poller.prototype.stop = stop;
    Poller.prototype.subscribe = subscribe;
    Poller.prototype.showSpinner = showSpinner;
    Poller.prototype.reload = reload;
    Poller.prototype.reloadOnScopeEvent = reloadOnScopeEvent;

    return Poller;

    function isStopped(startTimestamp) {
      return !(angular.isUndefined(this.stopTimestamp) || startTimestamp >= this.stopTimestamp);
    }
    function reloadOnScopeEvent(eventName, vm, spinnerName) {
      var self = this;
      function action() {
        self.reload();
        if (vm) {
          self.showSpinner(vm, spinnerName);
        }
      }
      if (angular.isArray(eventName)) {
        angular.forEach(eventName, function (event) {
          self.scope.$on(event, action);
        });
      } else {
        self.scope.$on(eventName, action);
      }
      return this;
    }
    function setInterval(interval) {
      this.extractInterval = interval;
      return this;
    }
    function reload(keepLatestResult) {
      if (!keepLatestResult) {
        delete this.latestResult;
      }
      this.stop();
      this.cycle();
      return this;
    }
    function showSpinner(vm, name) {
      var self = this;
      mnPromiseHelper(vm, self.doCallPromise).showSpinner(name);
      return self;
    }
    function doCall(timestamp) {
      var self = this;
      var query = angular.isFunction(self.request) ? self.request(self.latestResult) : self.request;
      query.then(function (result) {
        if (self.isStopped(timestamp)) {
          return;
        }
        self.deferred.notify(result);
      });
      return query;
    }
    function cycle() {
      this.doCycle();
      return this;
    }
    function doCycle() {
      var self = this;
      var timestamp = new Date();

      self.doCallPromise = self.doCall(timestamp);

      if (self.extractInterval) {
        if (angular.isFunction(self.extractInterval)) {
          self.doCallPromise.then(function (result) {
            if (self.isStopped(timestamp)) {
              return;
            }
            var interval = self.extractInterval(result);
            self.timeout = $timeout(self.doCycle.bind(self), interval);
          });
        } else {
          self.timeout = $timeout(self.doCycle.bind(self), self.extractInterval);
        }
      }
      self.doCallPromise.then(null, function (resp) {
        self.stop(); //stop cycle on any http error;
      });
      return this;
    }
    function stop() {
      var self = this;
      self.stopTimestamp = new Date();
      $timeout.cancel(self.timeout);
    }
    function subscribe(subscriber, keeper) {
      var self = this;
      self.deferred.promise.then(null, null, angular.isFunction(subscriber) ? function (value) {
        subscriber(value, self.latestResult);
        self.latestResult = value;
      } : function (value) {
        (keeper || self.scope)[subscriber] = value;
        self.latestResult = value;
      });
      return self;
    }
  }
})();
<commit_msg>MB-22702: mnPoll: call next doCycle after http response<commit_after>(function () {
  "use strict";

  angular
    .module("mnPoll", [
      "mnTasksDetails",
      "mnPromiseHelper"
    ])
    .factory("mnPoller", mnPollerFactory)
    .factory("mnEtagPoller", mnEtagPollerFactory);

  function mnEtagPollerFactory(mnPoller) {
    function EtagPoller(scope, request) {
      mnPoller.call(this, scope, request);
    }

    EtagPoller.prototype = Object.create(mnPoller.prototype);
    EtagPoller.prototype.constructor = EtagPoller;
    EtagPoller.prototype.cycle = cycle;

    return EtagPoller;

    function cycle() {
      var self = this;
      var timestamp = new Date();
      self.doCallPromise = self.doCall(timestamp);
      self.doCallPromise.then(function () {
        if (self.isStopped(timestamp)) {
          return;
        }
        self.cycle();
      });
      return self;
    }
  }

  function mnPollerFactory($q, $timeout, mnTasksDetails, mnPromiseHelper) {

    function Poller(scope, request) {
      this.deferred = $q.defer();
      this.request = request;
      this.scope = scope;

      scope.$on('$destroy', this.stop.bind(this));

      this.latestResult = undefined;
      this.stopTimestamp = undefined;
      this.extractInterval = undefined;
      this.timeout = undefined;
      this.doCallPromise = undefined;
    }

    Poller.prototype.isStopped = isStopped;
    Poller.prototype.doCall = doCall;
    Poller.prototype.setInterval = setInterval;
    Poller.prototype.cycle = cycle;
    Poller.prototype.doCycle = doCycle;
    Poller.prototype.stop = stop;
    Poller.prototype.subscribe = subscribe;
    Poller.prototype.showSpinner = showSpinner;
    Poller.prototype.reload = reload;
    Poller.prototype.reloadOnScopeEvent = reloadOnScopeEvent;

    return Poller;

    function isStopped(startTimestamp) {
      return !(angular.isUndefined(this.stopTimestamp) || startTimestamp >= this.stopTimestamp);
    }
    function reloadOnScopeEvent(eventName, vm, spinnerName) {
      var self = this;
      function action() {
        self.reload();
        if (vm) {
          self.showSpinner(vm, spinnerName);
        }
      }
      if (angular.isArray(eventName)) {
        angular.forEach(eventName, function (event) {
          self.scope.$on(event, action);
        });
      } else {
        self.scope.$on(eventName, action);
      }
      return this;
    }
    function setInterval(interval) {
      this.extractInterval = interval;
      return this;
    }
    function reload(keepLatestResult) {
      if (!keepLatestResult) {
        delete this.latestResult;
      }
      this.stop();
      this.cycle();
      return this;
    }
    function showSpinner(vm, name) {
      var self = this;
      mnPromiseHelper(vm, self.doCallPromise).showSpinner(name);
      return self;
    }
    function doCall(timestamp) {
      var self = this;
      var query = angular.isFunction(self.request) ? self.request(self.latestResult) : self.request;
      query.then(function (result) {
        if (self.isStopped(timestamp)) {
          return;
        }
        self.deferred.notify(result);
      });
      return query;
    }
    function cycle() {
      this.doCycle();
      return this;
    }
    function doCycle() {
      var self = this;
      var timestamp = new Date();

      self.doCallPromise = self.doCall(timestamp);

      if (self.extractInterval) {
        self.doCallPromise.then(function (result) {
          if (self.isStopped(timestamp)) {
            return;
          }
          var interval = angular.isFunction(self.extractInterval) ?
              self.extractInterval(result) :
              self.extractInterval;

          self.timeout = $timeout(self.doCycle.bind(self), interval);
        });
      }
      self.doCallPromise.then(null, function (resp) {
        self.stop(); //stop cycle on any http error;
      });
      return this;
    }
    function stop() {
      var self = this;
      self.stopTimestamp = new Date();
      $timeout.cancel(self.timeout);
    }
    function subscribe(subscriber, keeper) {
      var self = this;
      self.deferred.promise.then(null, null, angular.isFunction(subscriber) ? function (value) {
        subscriber(value, self.latestResult);
        self.latestResult = value;
      } : function (value) {
        (keeper || self.scope)[subscriber] = value;
        self.latestResult = value;
      });
      return self;
    }
  }
})();
<|endoftext|>


<commit_before>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

  /**
 * @ngdoc function
 * @name forms.function:Projects
 * @description This form is for adding/editing projects
*/

export default
angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
    .value('ProjectsFormObject', {

        addTitle: 'New Project',
        editTitle: '{{ name }}',
        name: 'project',
        forceListeners: true,
        tabs: true,
        subFormTitles: {
            sourceSubForm: 'Source Details',
        },

        fields: {
            name: {
                label: 'Name',
                type: 'text',
                addRequired: true,
                editRequired: true,
                capitalize: false
            },
            description: {
                label: 'Description',
                type: 'text',
                addRequired: false,
                editRequired: false
            },
            organization: {
                label: 'Organization',
                type: 'lookup',
                sourceModel: 'organization',
                sourceField: 'name',
                ngClick: 'lookUpOrganization()',
                awRequiredWhen: {
                    reqExpression: "organizationrequired",
                    init: "true"
                },
                awPopOver: '
A project must have at least one organization. Pick one organization now to create the project, and then after ' +
                    'the project is created you can add additional organizations.

Only super users and organization administrators are allowed ' +
                    'to make changes to projects. Associating one or more organizations to a project determins which organizations admins have ' +
                    'access to modify the project.',
                dataTitle: 'Organization',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            scm_type: {
                label: 'SCM Type',
                type: 'select',
                class: 'Form-dropDown--scmType',
                ngOptions: 'type.label for type in scm_type_options track by type.value',
                ngChange: 'scmChange()',
                addRequired: true,
                editRequired: true,
                hasSubForm: true,
            },
            missing_path_alert: {
                type: 'alertblock',
                //"class": 'alert-info',
                ngShow: "showMissingPlaybooksAlert && scm_type.value == 'manual'",
                alertTxt: '
WARNING: There are no available playbook directories in {{ base_dir }}.  ' +
                    'Either that directory is empty, or all of the contents are already assigned to other projects.  ' +
                    'Create a new directory there and make sure the playbook files can be read by the "awx" system user, ' +
                    'or have Tower directly retrieve your playbooks from source control using the SCM Type option above.
',
                closeable: false
            },
            base_dir: {
                label: 'Project Base Path',
                type: 'text',
                class: 'Form-textUneditable',
                showonly: true,
                ngShow: "scm_type.value == 'manual' " ,
                awPopOver: '
Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
                    'Together the base path and selected playbook directory provide the full path used to locate playbooks.
' +
                    '
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
                dataTitle: 'Project Base Path',
                dataContainer: 'body',
                dataPlacement: 'right',
            },
            local_path: {
                label: 'Playbook Directory',
                type: 'select',
                id: 'local-path-select',
                ngOptions: 'path.label for path in project_local_paths',
                awRequiredWhen: {
                    reqExpression: "pathRequired",
                    init: false
                },
                ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
                awPopOver: '
Select from the list of directories found in the base path.' +
                    'Together the base path and the playbook directory provide the full path used to locate playbooks.
' +
                    '
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
                dataTitle: 'Project Path',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            scm_url: {
                label: 'SCM URL',
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                awRequiredWhen: {
                    reqExpression: "scmRequired",
                    init: false
                },
                subForm: 'sourceSubForm',
                hideSubForm: "scm_type.value === 'manual'",
                awPopOverWatch: "urlPopover",
                awPopOver: "set in controllers/projects",
                dataTitle: 'SCM URL',
                dataContainer: 'body',
                dataPlacement: 'right',
            },
            scm_branch: {
                labelBind: "scmBranchLabel",
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                addRequired: false,
                editRequired: false,
                subForm: 'sourceSubForm'
            },
            credential: {
                label: 'SCM Credential',
                type: 'lookup',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                sourceModel: 'credential',
                sourceField: 'name',
                ngClick: 'lookUpCredential()',
                addRequired: false,
                editRequired: false,
                subForm: 'sourceSubForm'
            },
            checkbox_group: {
                label: 'SCM Update Options',
                type: 'checkbox_group',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                subForm: 'sourceSubForm',
                fields: [{
                    name: 'scm_clean',
                    label: 'Clean',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Remove any local modifications prior to performing an update.
',
                    dataTitle: 'SCM Clean',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }, {
                    name: 'scm_delete_on_update',
                    label: 'Delete on Update',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Delete the local repository in its entirety prior to performing an update.

Depending on the size of the ' +
                        'repository this may significantly increase the amount of time required to complete an update.
',
                    dataTitle: 'SCM Delete',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }, {
                    name: 'scm_update_on_launch',
                    label: 'Update on Launch',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Each time a job runs using this project, perform an update to the local repository prior to starting the job.
',
                    dataTitle: 'SCM Update',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }]
            },
            scm_update_cache_timeout: {
                label: 'Cache Timeout (seconds)',
                id: 'scm-cache-timeout',
                type: 'number',
                integer: true,
                min: 0,
                ngShow: "scm_update_on_launch && projectSelected",
                spinner: true,
                "default": '0',
                addRequired: false,
                editRequired: false,
                awPopOver: '
Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
                    'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
                    'and a new project update will be performed.
',
                dataTitle: 'Cache Timeout',
                dataPlacement: 'right',
                dataContainer: "body"
            }
        },

        buttons: {
            cancel: {
                ngClick: 'formCancel()'
            },
            save: {
                ngClick: 'formSave()',
                ngDisabled: true
            }
        },

        related: {
            permissions: {
                awToolTip: 'Please save before assigning permissions',
                dataPlacement: 'top',
                basePath: 'projects/:id/access_list/',
                type: 'collection',
                title: 'Permissions',
                iterator: 'permission',
                index: false,
                open: false,
                searchType: 'select',
                actions: {
                    add: {
                        ngClick: "addPermission",
                        label: 'Add',
                        awToolTip: 'Add a permission',
                        actionClass: 'btn List-buttonSubmit',
                        buttonContent: '+ ADD'
                    }
                },

                fields: {
                    username: {
                        key: true,
                        label: 'User',
                        linkBase: 'users',
                        class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
                    },
                    role: {
                        label: 'Role',
                        type: 'role',
                        noSort: true,
                        class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4'
                    },
                    team_roles: {
                        label: 'Team Roles',
                        type: 'team_roles',
                        noSort: true,
                        class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4'
                    }
                }
            },
            notifications: {
                include: "NotificationsList",
            }
        },

        relatedSets: function(urls) {
            return {
                permissions: {
                    iterator: 'permission',
                    url: urls.access_list
                },
                notifications: {
                    iterator: 'notification',
                    url: '/api/v1/notification_templates/'
                }
            };
        }

    })

    .factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList', function(ProjectsFormObject, NotificationsList) {
        return function() {
            var itm;
            for (itm in ProjectsFormObject.related) {
                if (ProjectsFormObject.related[itm].include === "NotificationsList") {
                    ProjectsFormObject.related[itm] = NotificationsList;
                    ProjectsFormObject.related[itm].generateList = true;   // tell form generator to call list generator and inject a list
                }
            }
            return ProjectsFormObject;
        };
    }]);
<commit_msg>PR feedback<commit_after>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

  /**
 * @ngdoc function
 * @name forms.function:Projects
 * @description This form is for adding/editing projects
*/

export default
angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
    .value('ProjectsFormObject', {

        addTitle: 'New Project',
        editTitle: '{{ name }}',
        name: 'project',
        forceListeners: true,
        tabs: true,
        subFormTitles: {
            sourceSubForm: 'Source Details',
        },

        fields: {
            name: {
                label: 'Name',
                type: 'text',
                addRequired: true,
                editRequired: true,
                capitalize: false
            },
            description: {
                label: 'Description',
                type: 'text',
                addRequired: false,
                editRequired: false
            },
            organization: {
                label: 'Organization',
                type: 'lookup',
                sourceModel: 'organization',
                sourceField: 'name',
                ngClick: 'lookUpOrganization()',
                awRequiredWhen: {
                    reqExpression: "organizationrequired",
                    init: "true"
                },
                awPopOver: '
A project must have at least one organization. Pick one organization now to create the project, and then after ' +
                    'the project is created you can add additional organizations.

Only super users and organization administrators are allowed ' +
                    'to make changes to projects. Associating one or more organizations to a project determins which organizations admins have ' +
                    'access to modify the project.',
                dataTitle: 'Organization',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            scm_type: {
                label: 'SCM Type',
                type: 'select',
                class: 'Form-dropDown--scmType',
                ngOptions: 'type.label for type in scm_type_options track by type.value',
                ngChange: 'scmChange()',
                addRequired: true,
                editRequired: true,
                hasSubForm: true,
            },
            missing_path_alert: {
                type: 'alertblock',
                ngShow: "showMissingPlaybooksAlert && scm_type.value == 'manual'",
                alertTxt: '
WARNING: There are no available playbook directories in {{ base_dir }}.  ' +
                    'Either that directory is empty, or all of the contents are already assigned to other projects.  ' +
                    'Create a new directory there and make sure the playbook files can be read by the "awx" system user, ' +
                    'or have Tower directly retrieve your playbooks from source control using the SCM Type option above.
',
                closeable: false
            },
            base_dir: {
                label: 'Project Base Path',
                type: 'text',
                class: 'Form-textUneditable',
                showonly: true,
                ngShow: "scm_type.value == 'manual' " ,
                awPopOver: '
Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
                    'Together the base path and selected playbook directory provide the full path used to locate playbooks.
' +
                    '
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
                dataTitle: 'Project Base Path',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            local_path: {
                label: 'Playbook Directory',
                type: 'select',
                id: 'local-path-select',
                ngOptions: 'path.label for path in project_local_paths',
                awRequiredWhen: {
                    reqExpression: "pathRequired",
                    init: false
                },
                ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
                awPopOver: '
Select from the list of directories found in the base path.' +
                    'Together the base path and the playbook directory provide the full path used to locate playbooks.
' +
                    '
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
                dataTitle: 'Project Path',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            scm_url: {
                label: 'SCM URL',
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                awRequiredWhen: {
                    reqExpression: "scmRequired",
                    init: false
                },
                subForm: 'sourceSubForm',
                hideSubForm: "scm_type.value === 'manual'",
                awPopOverWatch: "urlPopover",
                awPopOver: "set in controllers/projects",
                dataTitle: 'SCM URL',
                dataContainer: 'body',
                dataPlacement: 'right'
            },
            scm_branch: {
                labelBind: "scmBranchLabel",
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                addRequired: false,
                editRequired: false,
                subForm: 'sourceSubForm'
            },
            credential: {
                label: 'SCM Credential',
                type: 'lookup',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                sourceModel: 'credential',
                sourceField: 'name',
                ngClick: 'lookUpCredential()',
                addRequired: false,
                editRequired: false,
                subForm: 'sourceSubForm'
            },
            checkbox_group: {
                label: 'SCM Update Options',
                type: 'checkbox_group',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                subForm: 'sourceSubForm',
                fields: [{
                    name: 'scm_clean',
                    label: 'Clean',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Remove any local modifications prior to performing an update.
',
                    dataTitle: 'SCM Clean',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }, {
                    name: 'scm_delete_on_update',
                    label: 'Delete on Update',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Delete the local repository in its entirety prior to performing an update.

Depending on the size of the ' +
                        'repository this may significantly increase the amount of time required to complete an update.
',
                    dataTitle: 'SCM Delete',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }, {
                    name: 'scm_update_on_launch',
                    label: 'Update on Launch',
                    type: 'checkbox',
                    addRequired: false,
                    editRequired: false,
                    awPopOver: '
Each time a job runs using this project, perform an update to the local repository prior to starting the job.
',
                    dataTitle: 'SCM Update',
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline'
                }]
            },
            scm_update_cache_timeout: {
                label: 'Cache Timeout (seconds)',
                id: 'scm-cache-timeout',
                type: 'number',
                integer: true,
                min: 0,
                ngShow: "scm_update_on_launch && projectSelected",
                spinner: true,
                "default": '0',
                addRequired: false,
                editRequired: false,
                awPopOver: '
Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
                    'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
                    'and a new project update will be performed.
',
                dataTitle: 'Cache Timeout',
                dataPlacement: 'right',
                dataContainer: "body"
            }
        },

        buttons: {
            cancel: {
                ngClick: 'formCancel()'
            },
            save: {
                ngClick: 'formSave()',
                ngDisabled: true
            }
        },

        related: {
            permissions: {
                awToolTip: 'Please save before assigning permissions',
                dataPlacement: 'top',
                basePath: 'projects/:id/access_list/',
                type: 'collection',
                title: 'Permissions',
                iterator: 'permission',
                index: false,
                open: false,
                searchType: 'select',
                actions: {
                    add: {
                        ngClick: "addPermission",
                        label: 'Add',
                        awToolTip: 'Add a permission',
                        actionClass: 'btn List-buttonSubmit',
                        buttonContent: '+ ADD'
                    }
                },

                fields: {
                    username: {
                        key: true,
                        label: 'User',
                        linkBase: 'users',
                        class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
                    },
                    role: {
                        label: 'Role',
                        type: 'role',
                        noSort: true,
                        class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4'
                    },
                    team_roles: {
                        label: 'Team Roles',
                        type: 'team_roles',
                        noSort: true,
                        class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4'
                    }
                }
            },
            notifications: {
                include: "NotificationsList",
            }
        },

        relatedSets: function(urls) {
            return {
                permissions: {
                    iterator: 'permission',
                    url: urls.access_list
                },
                notifications: {
                    iterator: 'notification',
                    url: '/api/v1/notification_templates/'
                }
            };
        }

    })

    .factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList', function(ProjectsFormObject, NotificationsList) {
        return function() {
            var itm;
            for (itm in ProjectsFormObject.related) {
                if (ProjectsFormObject.related[itm].include === "NotificationsList") {
                    ProjectsFormObject.related[itm] = NotificationsList;
                    ProjectsFormObject.related[itm].generateList = true;   // tell form generator to call list generator and inject a list
                }
            }
            return ProjectsFormObject;
        };
    }]);
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var cp = require('child_process');
var util = require('./lib/util.js');
var mainWin = null;
var config = null;
var stepModule = null;

var GLOBAL_LASTCONFIGFILE = null;
var executeWorkFlow = function(wf, opts, donefn) {
	wf = util.clone(wf);
	if(typeof opts === 'undefined') var opts = {};
	var ctx = {};

	ctx.openFileWritesHandler = {};
	ctx.excelHandler = {};
	ctx.vars = {};
	ctx.mainWin = mainWin;
	ctx.executeWorkFlow = executeWorkFlow;
	ctx.config = config;
	ctx.opts = opts;

	if(typeof GLOBAL_LASTCONFIGFILE !== 'undefined') {
		ctx.vars['__dirname'] = path.dirname(GLOBAL_LASTCONFIGFILE);
	}
	else {
		ctx.vars['__dirname'] = __dirname;
	}

	if(typeof opts.inputVars !== 'undefined') {
		for(var i in opts.inputVars) {
			if(!Object.prototype.hasOwnProperty.call(opts.inputVars,i)) continue;
			ctx.vars[i] = opts.inputVars[i];
		}
	}
	var replaceVars = function(c) {
		if(util.isOnlyOneVariable(c)) {
			var varName = util.getStringBetween(c, '##','##')
			c = ctx.vars[varName]
		}
		else {
			for(var k in ctx.vars) {
				if(!Object.prototype.hasOwnProperty.call(ctx.vars,k)) continue;
				c = util.replaceAll(c, '##' + k + '##', ctx.vars[k]);
			}
		}
		return c;
	}
	var replaceVarsStep = function(step) {
		for(var i in step) {
			if(!Object.prototype.hasOwnProperty.call(step,i)) continue;
			if(typeof step[i] !== 'string') {
				step[i] = replaceVarsStep(step[i]);
			}
			else {
				step[i] = replaceVars(step[i]);
			}
		}
		return step;
	}
	var curStep = 0;
	var checkNext = function() {
		// execute next
		if(1 + curStep < wf.steps.length) {
			curStep++;
			process.nextTick(next);
		}
		else {
			var outputVars = {};
			if(typeof opts.outputVars !== 'undefined') {
				opts.outputVars.forEach(function(i) {
					outputVars[i] = ctx.vars[i];
				});
			}
			if(typeof opts.inputVars !== 'undefined' && typeof opts.inputVars.outputall !== 'undefined' &&  opts.inputVars.outputall) {
				for(var i in ctx.vars) {
					if(!Object.prototype.hasOwnProperty.call(ctx.vars,i)) continue;
					outputVars[i] = ctx.vars[i];
				}
			}
			if(donefn)process.nextTick(function() {
				donefn({outputVars:outputVars});
			});
			// garbage collection
			ctx.openFileWritesHandler = {};
			ctx.excelHandler = {};
			ctx.vars = {};
		}
	}
	var next = function() {
		var step = wf.steps[curStep];
		step = replaceVarsStep(step);

		// search available work flow
		if(typeof config.workFlows[step.type] !== 'undefined') {
			var inputVars = step;
			if(typeof step.inputall !== 'undefined' && step.inputall) {
				for(var i in ctx.vars) {
					if(!Object.prototype.hasOwnProperty.call(ctx.vars,i)) continue;
					inputVars[i] = ctx.vars[i];
				}
			}
			executeWorkFlow(config.workFlows[step.type], {inputVars:inputVars,outputVars:step.outputVars,assert:ctx.opts.assert}, function(outputOpts) {
				if(outputOpts.outputVars) {
					for(var i in outputOpts.outputVars) {
						if(!Object.prototype.hasOwnProperty.call(outputOpts.outputVars,i)) continue;
						ctx.vars[i] = outputOpts.outputVars[i];
					}
				}
				process.nextTick(checkNext);
			});
		}
		else {
			stepModule.processStep(ctx, step, checkNext);
		}
	} // end next

	process.nextTick(next);
}
function setConfig(cfg) {
	config = cfg;
}
module.exports.executeWorkFlow = executeWorkFlow;
module.exports.setWindow = function(win) {
	mainWin = win;
}
module.exports.setConfig = setConfig;
module.exports.setStepModule = function(mod) {
	stepModule = mod;
}
module.exports.importConfig = function(configFile) {
	delete require.cache[require.resolve(configFile)]; // delete require cache
	config = require(configFile); // require again
	setConfig(config);
	GLOBAL_LASTCONFIGFILE = path.resolve(configFile);
	return config;
}<commit_msg>Move checkWorkflowStep to workflow_engine but commented out first<commit_after>var fs = require('fs');
var path = require('path');
var cp = require('child_process');
var util = require('./lib/util.js');
var mainWin = null;
var config = null;
var stepModule = null;

var GLOBAL_LASTCONFIGFILE = null;
var executeWorkFlow = function(wf, opts, donefn) {
	wf = util.clone(wf);
	if(typeof opts === 'undefined') var opts = {};
	var ctx = {};

	ctx.openFileWritesHandler = {};
	ctx.excelHandler = {};
	ctx.vars = {};
	ctx.mainWin = mainWin;
	ctx.executeWorkFlow = executeWorkFlow;
	ctx.config = config;
	ctx.opts = opts;

	if(typeof GLOBAL_LASTCONFIGFILE !== 'undefined') {
		ctx.vars['__dirname'] = path.dirname(GLOBAL_LASTCONFIGFILE);
	}
	else {
		ctx.vars['__dirname'] = __dirname;
	}

	if(typeof opts.inputVars !== 'undefined') {
		for(var i in opts.inputVars) {
			if(!Object.prototype.hasOwnProperty.call(opts.inputVars,i)) continue;
			ctx.vars[i] = opts.inputVars[i];
		}
	}
	var replaceVars = function(c) {
		if(util.isOnlyOneVariable(c)) {
			var varName = util.getStringBetween(c, '##','##')
			c = ctx.vars[varName]
		}
		else {
			for(var k in ctx.vars) {
				if(!Object.prototype.hasOwnProperty.call(ctx.vars,k)) continue;
				c = util.replaceAll(c, '##' + k + '##', ctx.vars[k]);
			}
		}
		return c;
	}
	var replaceVarsStep = function(step) {
		for(var i in step) {
			if(!Object.prototype.hasOwnProperty.call(step,i)) continue;
			if(typeof step[i] !== 'string') {
				step[i] = replaceVarsStep(step[i]);
			}
			else {
				step[i] = replaceVars(step[i]);
			}
		}
		return step;
	}
	var curStep = 0;
	var checkNext = function() {
		// execute next
		if(1 + curStep < wf.steps.length) {
			curStep++;
			process.nextTick(next);
		}
		else {
			var outputVars = {};
			if(typeof opts.outputVars !== 'undefined') {
				opts.outputVars.forEach(function(i) {
					outputVars[i] = ctx.vars[i];
				});
			}
			if(typeof opts.inputVars !== 'undefined' && typeof opts.inputVars.outputall !== 'undefined' &&  opts.inputVars.outputall) {
				for(var i in ctx.vars) {
					if(!Object.prototype.hasOwnProperty.call(ctx.vars,i)) continue;
					outputVars[i] = ctx.vars[i];
				}
			}
			if(donefn)process.nextTick(function() {
				donefn({outputVars:outputVars});
			});
			// garbage collection
			ctx.openFileWritesHandler = {};
			ctx.excelHandler = {};
			ctx.vars = {};
		}
	}
	var next = function() {
		var step = wf.steps[curStep];
		step = replaceVarsStep(step);

		// search available work flow
		if(typeof config.workFlows[step.type] !== 'undefined') {
			var inputVars = step;
			if(typeof step.inputall !== 'undefined' && step.inputall) {
				for(var i in ctx.vars) {
					if(!Object.prototype.hasOwnProperty.call(ctx.vars,i)) continue;
					inputVars[i] = ctx.vars[i];
				}
			}
			executeWorkFlow(config.workFlows[step.type], {inputVars:inputVars,outputVars:step.outputVars,assert:ctx.opts.assert}, function(outputOpts) {
				if(outputOpts.outputVars) {
					for(var i in outputOpts.outputVars) {
						if(!Object.prototype.hasOwnProperty.call(outputOpts.outputVars,i)) continue;
						ctx.vars[i] = outputOpts.outputVars[i];
					}
				}
				process.nextTick(checkNext);
			});
		}
		else {
			stepModule.processStep(ctx, step, checkNext);
		}
	} // end next

	process.nextTick(next);
}
function setConfig(cfg) {
	config = cfg;
}
module.exports.executeWorkFlow = executeWorkFlow;
module.exports.setWindow = function(win) {
	mainWin = win;
}
module.exports.setConfig = setConfig;
module.exports.setStepModule = function(mod) {
	stepModule = mod;
}
module.exports.importConfig = function(configFile) {
	delete require.cache[require.resolve(configFile)]; // delete require cache
	config = require(configFile); // require again
	setConfig(config);
	// load plugins
	loadPlugins(config);
	/*
	if(!checkWorkflowStepSpec(config)) {
		util.alert("Error check step spec");
		return;
	}
	*/
	//console.log(configFile + "?" + config)
	GLOBAL_LASTCONFIGFILE = path.resolve(configFile);
	return config;
}<|endoftext|>


<commit_before>/**
 * a library to handle CSS-based filtering of the main timeline by swapping in different sets of CSS rules
 */
Spaz.cssFilters = {};

Spaz.cssFilters.filters = [
	{
		'label': 'All',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: '@replies and DMs',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: '@replies',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:none; }'
	},
	{
		label: 'DMs',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:none; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: 'Unread',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }\
				div.timeline-entry.read  { display:none;  }'
	},
	{
		label: 'No API RTs',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }\
				div.timeline-entry.retweet { display:none;}'

	}
];

Spaz.cssFilters.getCSS = function(label) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			return Spaz.cssFilters.filters[i].css;
		}
	}
	return false;
};

Spaz.cssFilters.applyFilter = function(label) {

	if (jQuery('#cssfilter').length < 1) {
		jQuery('head').append('<style id="cssfilter" type="text/css"></style>');
	}

	var css = Spaz.cssFilters.getCSS(label);

	jQuery('#cssfilter').text(css);
};

Spaz.cssFilters.clearFilter = function() {
	jQuery('#cssfilter').remove();
};

Spaz.cssFilters.addFilter = function(label, css) {
	if ( !(Spaz.cssFilters.setFilterCSS(label, css)) ) {
		Spaz.cssFilters.filters.push({
			'label':label,
			'css':css
		});
	}
};

Spaz.cssFilters.setFilterCSS = function(label, css) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			Spaz.cssFilters.filters[i].css = css;
			return true;
		}
	}
	return false;
};

Spaz.cssFilters.removeFilter = function(label) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			Spaz.cssFilters.filters[i] = null;
			return true;
		}
	}
	return false;
};
<commit_msg>Copy: "@replies" -> "@mentions"<commit_after>/**
 * a library to handle CSS-based filtering of the main timeline by swapping in different sets of CSS rules
 */
Spaz.cssFilters = {};

Spaz.cssFilters.filters = [
	{
		'label': 'All',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: '@mentions and DMs',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: '@mentions',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:none; }'
	},
	{
		label: 'DMs',
		'css': 'div.timeline-entry       { display:none; }\
				div.timeline-entry.reply { display:none; }\
				div.timeline-entry.dm    { display:block; }'
	},
	{
		label: 'Unread',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }\
				div.timeline-entry.read  { display:none;  }'
	},
	{
		label: 'No API RTs',
		'css': 'div.timeline-entry       { display:block; }\
				div.timeline-entry.reply { display:block; }\
				div.timeline-entry.dm    { display:block; }\
				div.timeline-entry.retweet { display:none;}'

	}
];

Spaz.cssFilters.getCSS = function(label) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			return Spaz.cssFilters.filters[i].css;
		}
	}
	return false;
};

Spaz.cssFilters.applyFilter = function(label) {

	if (jQuery('#cssfilter').length < 1) {
		jQuery('head').append('<style id="cssfilter" type="text/css"></style>');
	}

	var css = Spaz.cssFilters.getCSS(label);

	jQuery('#cssfilter').text(css);
};

Spaz.cssFilters.clearFilter = function() {
	jQuery('#cssfilter').remove();
};

Spaz.cssFilters.addFilter = function(label, css) {
	if ( !(Spaz.cssFilters.setFilterCSS(label, css)) ) {
		Spaz.cssFilters.filters.push({
			'label':label,
			'css':css
		});
	}
};

Spaz.cssFilters.setFilterCSS = function(label, css) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			Spaz.cssFilters.filters[i].css = css;
			return true;
		}
	}
	return false;
};

Spaz.cssFilters.removeFilter = function(label) {
	for (var i=0; i < Spaz.cssFilters.filters.length; i++) {
		if (Spaz.cssFilters.filters[i].label === label) {
			Spaz.cssFilters.filters[i] = null;
			return true;
		}
	}
	return false;
};
<|endoftext|>


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

var DB_KEY = 'token';
var DB_NAME = 'push-notification-updates';
var DEFAULT_ICON = 'images/touch/homescreen192.png';
var SESSIONS_ENDPOINT = 'api/v1/schedule';
var UPDATES_ENDPOINT = 'api/v1/user/updates';
var SESSION_DETAILS_URL_PREFIX = 'schedule?filters=#day';

/**
 * Loads a SW token value from IndexedDB.
 * @return {Promise} Resolves with the token, or null if there isn't one.
 */
function loadToken() {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.get(DB_KEY).then(function(token) {
      return token;
    });
  });
}

/**
 * Makes a request against the updates endpoint to determine which new notifications to display.
 * See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @param {string} token A SW token associated with the current user.
 * @return {Promise} Resolves with a Response, or rejects on a network error.
 */
function fetchUpdates(token) {
  return fetch(new Request(UPDATES_ENDPOINT, {Authorization: token}));
}

/**
 * Deserializes a JSON body from a Response object.
 * @param {Response} response An HTTP response object with a JSON body.
 * @return {Promise} Resolves with an object representing the JSON in the response body, or rejects
 *                   if the Response was an HTTP error or didn't contain JSON.
 */
function parseResponseJSON(response) {
  if (response.status >= 400) {
    throw Error('The request to ' + response.url + ' failed: ' +
                response.statusText + ' (' + response.status + ')');
  }
  return response.json();
}

/**
 * Processes the updates response to show notifications for each update.
 * @param {object} body See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves with a SW token that should be used the next time updates are fetched.
 *                   Rejects if there are any errors displaying notifications.
 */
function processResponse(body) {
  var notifications = generateSessionNotifications(body.sessions)
    .concat(generateVideoNotifications(body.videos))
    .concat(generateIOExtNotifications(body.ioext));

  return Promise.all(notifications.map(function(notification) {
    return self.registration.showNotification(notification.title, notification);
  })).then(function() {
    return body.token;
  });
}

/**
 * For each session that's passed in as a parameter, constructs the data needed to display a
 * notification.
 * Additionally, updates the cached session feed with the latest data from the update, to ensure
 * that if the session detail page is opened while offline, the details are up to date.
 * @param {array} updatedSessions One or more sessions.
 * @return {array} An array of objects with data that can be passed directly to
 *                   registration.showNotification()
 */
function generateSessionNotifications(updatedSessions) {
  // Ensure that we have an up-to-date sessions feed cached.
  // This will happen aysnchronously, independent from the notification creation, so it shouldn't be
  // necessary to wait on the promise resolutions.
  shed.helpers.openCache().then(function(cache) {
    cache.match(SESSIONS_ENDPOINT).then(function(response) {
      if (response) {
        // If there's a cached sessions feed, then update the changed fields and replace the cached
        // version with the updated version.
        parseResponseJSON(response).then(function(allSessions) {
          Object.keys(updatedSessions).forEach(function(sessionId) {
            allSessions[sessionId] = updatedSessions[sessionId];
          });

          cache.put(SESSIONS_ENDPOINT, new Response(JSON.stringify(allSessions)));
        });
      } else {
        // If there isn't anything already cached for the sessions feed, then cache the whole thing.
        shed.cache(SESSIONS_ENDPOINT);
      }
    });
  }).catch(function(error) {
    console.error('Could not update the cached sessions feed:', error);
  });

  return Object.keys(updatedSessions).map(function(sessionId) {
    var session = updatedSessions[sessionId];
    return {
      title: 'I/O session "' + session.title + '" was updated.',
      body: 'You previously starred this session.',
      icon: session.photoUrl || DEFAULT_ICON,
      tag: SESSION_DETAILS_URL_PREFIX + session.day + '/' + sessionId
    };
  });
}

function generateVideoNotifications(videos) {
  // TODO: Implement.
  return [];
}

function generateIOExtNotifications(videos) {
  // TODO: Implement.
  return [];
}

/**
 * Writes a SW token value to IndexedDB.
 * @param {string} token A SW token, returned from
 *                 https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves if the token is written successfully, and rejects otherwise.
 */
function saveToken(token) {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.set(DB_KEY, token);
  });
}

self.addEventListener('push', function(event) {
  event.waitUntil(
    loadToken()
      .then(fetchUpdates)
      .then(parseResponseJSON)
      .then(processResponse)
      .then(saveToken)
      .catch(function(error) {
        console.error('Unable to handle event', event, 'due to error', error);
      })
  );
});

self.addEventListener('notificationclick', function(event) {
  // Chrome displays a "default" notification if there isn't a notification shown in response to a
  // push event. We should ideally never get those, but just do a check first to make sure.
  if (event.notification.tag !== 'user_visible_auto_notification') {
    var url = new URL(event.notification.tag, location.href);
    clients.openWindow(url.toString());
  }
});
<commit_msg>Bug fixes.<commit_after>/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DB_KEY = 'token';
var DB_NAME = 'push-notification-updates';
var DEFAULT_ICON = 'images/touch/homescreen192.png';
var SESSIONS_ENDPOINT = 'api/v1/schedule';
var UPDATES_ENDPOINT = 'api/v1/user/updates';
var SESSION_DETAILS_URL_PREFIX = 'schedule?filters=#day';

/**
 * Loads a SW token value from IndexedDB.
 * @return {Promise} Resolves with the token, or null if there isn't one.
 */
function loadToken() {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.get(DB_KEY).then(function(token) {
      return token;
    });
  });
}

/**
 * Makes a request against the updates endpoint to determine which new notifications to display.
 * See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @param {string} token A SW token associated with the current user.
 * @return {Promise} Resolves with a Response, or rejects on a network error.
 */
function fetchUpdates(token) {
  return fetch(new Request(UPDATES_ENDPOINT, {
    headers: {
      Authorization: token
    }
  }));
}

/**
 * Deserializes a JSON body from a Response object.
 * @param {Response} response An HTTP response object with a JSON body.
 * @return {Promise} Resolves with an object representing the JSON in the response body, or rejects
 *                   if the Response was an HTTP error or didn't contain JSON.
 */
function parseResponseJSON(response) {
  if (response.status >= 400) {
    throw Error('The request to ' + response.url + ' failed: ' +
                response.statusText + ' (' + response.status + ')');
  }
  return response.json();
}

/**
 * Processes the updates response to show notifications for each update.
 * @param {object} body See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves with a SW token that should be used the next time updates are fetched.
 *                   Rejects if there are any errors displaying notifications.
 */
function processResponse(body) {
  var notifications = generateSessionNotifications(body.sessions)
    .concat(generateVideoNotifications(body.videos))
    .concat(generateIOExtNotifications(body.ioext));

  return Promise.all(notifications.map(function(notification) {
    return self.registration.showNotification(notification.title, notification);
  })).then(function() {
    return body.token;
  });
}

/**
 * For each session that's passed in as a parameter, constructs the data needed to display a
 * notification.
 * Additionally, updates the cached session feed with the latest data from the update, to ensure
 * that if the session detail page is opened while offline, the details are up to date.
 * @param {array} updatedSessions One or more sessions.
 * @return {array} An array of objects with data that can be passed directly to
 *                   registration.showNotification()
 */
function generateSessionNotifications(updatedSessions) {
  if (updatedSessions && updatedSessions.length) {
    // Ensure that we have an up-to-date sessions feed cached.
    // This will happen aysnchronously, independent from the notification creation, so it shouldn't be
    // necessary to wait on the promise resolutions.
    caches.open(shed.options.cacheName).then(function(cache) {
      cache.match(SESSIONS_ENDPOINT).then(function(response) {
        if (response) {
          // If there's a cached sessions feed, then update the changed fields and replace the cached
          // version with the updated version.
          parseResponseJSON(response).then(function(allSessions) {
            Object.keys(updatedSessions).forEach(function(sessionId) {
              allSessions[sessionId] = updatedSessions[sessionId];
            });

            cache.put(SESSIONS_ENDPOINT, new Response(JSON.stringify(allSessions)));
          });
        } else {
          // If there isn't anything already cached for the sessions feed, then cache the whole thing.
          shed.cache(SESSIONS_ENDPOINT);
        }
      });
    }).catch(function(error) {
      console.error('Could not update the cached sessions feed:', error);
    });

    return Object.keys(updatedSessions).map(function(sessionId) {
      var session = updatedSessions[sessionId];
      return {
        title: 'I/O session "' + session.title + '" was updated.',
        body: 'You previously starred this session.',
        icon: session.photoUrl || DEFAULT_ICON,
        tag: SESSION_DETAILS_URL_PREFIX + session.day + '/' + sessionId
      };
    });
  } else {
    return [];
  }
}

function generateVideoNotifications(videos) {
  // TODO: Implement.
  return [];
}

function generateIOExtNotifications(videos) {
  // TODO: Implement.
  return [];
}

/**
 * Writes a SW token value to IndexedDB.
 * @param {string} token A SW token, returned from
 *                 https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves if the token is written successfully, and rejects otherwise.
 */
function saveToken(token) {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.set(DB_KEY, token);
  });
}

self.addEventListener('push', function(event) {
  event.waitUntil(
    loadToken()
      .then(fetchUpdates)
      .then(parseResponseJSON)
      .then(processResponse)
      .then(saveToken)
      .catch(function(error) {
        console.error('Unable to handle event', event, 'due to error', error);
      })
  );
});

self.addEventListener('notificationclick', function(event) {
  // Chrome displays a "default" notification if there isn't a notification shown in response to a
  // push event. We should ideally never get those, but just do a check first to make sure.
  if (event.notification.tag !== 'user_visible_auto_notification') {
    var url = new URL(event.notification.tag, location.href);
    clients.openWindow(url.toString());
  }
});
<|endoftext|>


<commit_before>/**
 * Created by javaBoxer on 3/23/2017.
 */

// Assuming backend is writing the sale price strike through, we can toggle sale sticker in upper left of product.
// iterate over each product-wrapper and search for value of retail-price.
// if not empty, then it's on sale. Toggle Less class to display sale sticker in upper left of parent wrapper

$(document).ready(function(){

    // affixes top nav to be sticky
    $('#topnavbar').affix({
        offset: {
            top: $('#banner').height()
        }
    });

    $('.product-wrapper').each(function () {
        var retailValue = $(this).find('.retail-price');
        var retailValueText = $(retailValue).text();

        if(retailValueText) {
            $(this).find('.product-sale').addClass("sale-on");
        }

        else {
            $(this).find('.product-sale').addClass("sale-off");
        }
  })

    // Checkout Page - move the shopping cart section from the bottom of the page to the top
    // above "Account"

    var cartDetails = $(".checkout-details .cart-details");         // Remove Shopping Cart Div (cart-details)
    var checkoutAccount = $(".checkout-details .checkout-account"); // Insert Shopping Cart Div before Account Div (checkout-account)
    $(cartDetails).remove();
    $(cartDetails).insertBefore(checkoutAccount);

});

/**
 * Created by javaBoxer on 5/5/2017.
 */
// *************** BILL TO/SHIP TO ADDRESS FORM (from checkout page) ***************

// NEED TO MAKE THE ADD NEW SHIPPING, ADD NEW BILLING ADDRESS .container CLASS 50% WIDTH TO REDUCE THE SIZE AND CENTER IT ON SCREEN
// FOR DESKTOP > 800PX.
// IT DOES NOT HAVE A UNIQUE CLASS AND SETTING WIDTH ON .container AFFECTS EVERY PAGE ON THE SITE.
// READ IN THE URL AND APPLY STYLING ONLY TO THE ADDRESS PAGE for SHIPPING/BILLING(address/detail)
// APPLIES TO:
// http://a2zhardware.com/address/detail?makePrimary=True&addressType=Shipping&returnurl=%2Fcheckout
// http://a2zhardware.com/address/detail?makePrimary=True&addressType=Billing&returnurl=%2Fcheckout

$(window).load(function() {
    var loc = window.location.href.toLowerCase();
    var container = $(".container");
    var viewportWidth = $(window).width();

    if (loc.indexOf('/address/detail') > -1) {

        if (viewportWidth > 800) {
            $(container).removeClass("addressMobileWidth").addClass("addressDesktopWidth");
        }

        $(window).resize(function () {
            var viewportWidth = $(window).width();
            if (viewportWidth < 800) {
                $(container).removeClass("addressDesktopWidth").addClass("addressMobileWidth");
            }
            if (viewportWidth > 800) {
                $(container).removeClass("addressMobileWidth").addClass("addressDesktopWidth");
            }
        });
    }

    // *************** CONTACT US PAGE ***************
    // Center the page with a left margin class when in Desktop view > 800px

    if (loc.indexOf('/contactus') > -1) {

        if (viewportWidth > 800) {
            $(container).removeClass("contactMobileWidth").addClass("contactDesktopWidth");
        }

        $(window).resize(function () {
            var viewportWidth = $(window).width();
            if (viewportWidth < 800) {
                $(container).removeClass("contactDesktopWidth").addClass("contactMobileWidth");
            }
            if (viewportWidth > 800) {
                $(container).removeClass("contactMobileWidth").addClass("contactDesktopWidth");
            }
        });
    }

});

<commit_msg>Address for Billing and Shipping - Added mobile class by default in page.js to ensure bottom margin on page seperation at footer<commit_after>/**
 * Created by javaBoxer on 3/23/2017.
 */

// Assuming backend is writing the sale price strike through, we can toggle sale sticker in upper left of product.
// iterate over each product-wrapper and search for value of retail-price.
// if not empty, then it's on sale. Toggle Less class to display sale sticker in upper left of parent wrapper

$(document).ready(function(){

    // affixes top nav to be sticky
    $('#topnavbar').affix({
        offset: {
            top: $('#banner').height()
        }
    });

    $('.product-wrapper').each(function () {
        var retailValue = $(this).find('.retail-price');
        var retailValueText = $(retailValue).text();

        if(retailValueText) {
            $(this).find('.product-sale').addClass("sale-on");
        }

        else {
            $(this).find('.product-sale').addClass("sale-off");
        }
  })

    // Checkout Page - move the shopping cart section from the bottom of the page to the top
    // above "Account"

    var cartDetails = $(".checkout-details .cart-details");         // Remove Shopping Cart Div (cart-details)
    var checkoutAccount = $(".checkout-details .checkout-account"); // Insert Shopping Cart Div before Account Div (checkout-account)
    $(cartDetails).remove();
    $(cartDetails).insertBefore(checkoutAccount);

});

/**
 * Created by javaBoxer on 5/5/2017.
 */
// *************** BILL TO/SHIP TO ADDRESS FORM (from checkout page) ***************

// NEED TO MAKE THE ADD NEW SHIPPING, ADD NEW BILLING ADDRESS .container CLASS 50% WIDTH TO REDUCE THE SIZE AND CENTER IT ON SCREEN
// FOR DESKTOP > 800PX.
// IT DOES NOT HAVE A UNIQUE CLASS AND SETTING WIDTH ON .container AFFECTS EVERY PAGE ON THE SITE.
// READ IN THE URL AND APPLY STYLING ONLY TO THE ADDRESS PAGE for SHIPPING/BILLING(address/detail)
// APPLIES TO:
// http://a2zhardware.com/address/detail?makePrimary=True&addressType=Shipping&returnurl=%2Fcheckout
// http://a2zhardware.com/address/detail?makePrimary=True&addressType=Billing&returnurl=%2Fcheckout

$(window).load(function() {
    var loc = window.location.href.toLowerCase();
    var container = $(".container");
    var viewportWidth = $(window).width();

    if (loc.indexOf('/address/detail') > -1) {
        $(container).addClass("addressMobileWidth");
        if (viewportWidth > 800) {
            $(container).removeClass("addressMobileWidth").addClass("addressDesktopWidth");
        }

        $(window).resize(function () {
            var viewportWidth = $(window).width();
            if (viewportWidth < 800) {
                $(container).removeClass("addressDesktopWidth").addClass("addressMobileWidth");
            }
            if (viewportWidth > 800) {
                $(container).removeClass("addressMobileWidth").addClass("addressDesktopWidth");
            }
        });
    }

    // *************** CONTACT US PAGE ***************
    // Center the page with a left margin class when in Desktop view > 800px

    if (loc.indexOf('/contactus') > -1) {

        if (viewportWidth > 800) {
            $(container).removeClass("contactMobileWidth").addClass("contactDesktopWidth");
        }

        $(window).resize(function () {
            var viewportWidth = $(window).width();
            if (viewportWidth < 800) {
                $(container).removeClass("contactDesktopWidth").addClass("contactMobileWidth");
            }
            if (viewportWidth > 800) {
                $(container).removeClass("contactMobileWidth").addClass("contactDesktopWidth");
            }
        });
    }

});

<|endoftext|>