<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2017 Olivier Biot
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * cache value for the offset of the canvas position within the page
     * @ignore
     */
    var viewportOffset = new me.Vector2d();

    /**
     * a pointer object, representing a single finger on a touch enabled device.
     * @class
     * @extends me.Rect
     * @memberOf me
     * @constructor
     */
    me.Pointer = me.Rect.extend(
    /** @scope me.Pointer.prototype */
    {
        /**
         * @ignore
         */
        init : function (x, y, w, h) {

          /**
            * the originating Event Object
            * @public
            * @type {Event}
            * @name event
            * @see https://developer.mozilla.org/en-US/docs/Web/API/Event
            * @memberOf me.Pointer
            */
            this.event = undefined;

           /**
            * a string containing the event's type.
            * @public
            * @type {String}
            * @name type
            * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/type
            * @memberOf me.Pointer
            */
            this.type = undefined;

           /**
            * the button property indicates which button was pressed on the mouse to trigger the event.
            * @public
            * @type {Number}
            * @name width
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
            * @memberOf me.Pointer
            */
            this.button = 0;

           /**
            * indicates whether or not the pointer device that created the event is the primary pointe.
            * @public
            * @type {Boolean}
            * @name width
            * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
            * @memberOf me.Pointer
            */
            this.isPrimary = false;

           /**
            * the horizontal coordinate within the application's client area at which the event occurred
            * @public
            * @type {Number}
            * @name clientX
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX
            * @memberOf me.Pointer
            */
            this.clientX = undefined;

           /**
            * the vertical coordinate within the application's client area at which the event occurred
            * @public
            * @type {Number}
            * @name clientY
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY
            * @memberOf me.Pointer
            */
            this.clientY = undefined;

          /**
            * Event normalized X coordinate within the game canvas itself

            *
            * @public
            * @type {Number}
            * @name gameX
            * @memberOf me.Pointer
            */
            this.gameX = undefined;

           /**
            * Event normalized Y coordinate within the game canvas itself

            *
            * @public
            * @type {Number}
            * @name gameY
            * @memberOf me.Pointer
            */
            this.gameY = undefined;

           /**
            * Event X coordinate relative to the viewport
            * @public
            * @type {Number}
            * @name gameScreenX
            * @memberOf me.Pointe
            */
            this.gameScreenX = undefined;

           /**
            * Event Y coordinate relative to the viewport
            * @public
            * @type {Number}
            * @name gameScreenY
            * @memberOf me.Pointer
            */
            this.gameScreenY = undefined;

           /**
            * Event X coordinate relative to the map
            * @public
            * @type {Number}
            * @name gameWorldX
            * @memberOf me.Pointer
            */
            this.gameWorldX = undefined;

           /**
            * Event Y coordinate relative to the map
            * @public
            * @type {Number}
            * @name gameWorldY
            * @memberOf me.Pointer
            */
            this.gameWorldY = undefined;

           /**
            * Event X coordinate relative to the holding container
            * @public
            * @type {Number}
            * @name gameLocalX
            * @memberOf me.Pointer
            */
            this.gameLocalX = undefined;

           /**
            * Event Y coordinate relative to the holding container
            * @public
            * @type {Number}
            * @name gameLocalY
            * @memberOf me.Pointer
            */
            this.gameLocalY = undefined;

           /**
            * The unique identifier of the contact for a touch, mouse or pen
            * @public
            * @type {Number}
            * @name pointerId
            * @memberOf me.Pointer
            * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh466123.aspx
            */
            this.pointerId = undefined;

            // parent constructor
            this._super(me.Rect, "init", [x || 0, y || 0, w || 1, h || 1]);
        },

        /**
         * initialize the Pointer object using the given Event Object
         * @name me.Pointer#set
         * @private
         * @function
         * @param {Event} event the original Event object
         * @param {Number} clientX the horizontal coordinate within the application's client area at which the event occurred
         * @param {Number} clientX the vertical coordinate within the application's client area at which the event occurred
         * @param {Number} pointedId the Pointer, Touch or Mouse event Id
         */
        setEvent : function (event, clientX, clientY, pointerId) {
            var width = 1;
            var height = 1;

            // the original event object
            this.event = event;

            this.clientX = clientX || 0;
            this.clientY = clientY || 0;

            // translate to local coordinates
            me.input.globalToLocal(clientX, clientY, this.pos);

            if (event.type === "wheel") {
                this.deltaMode = 1;
                this.deltaX = event.deltaX;
                this.deltaY = - 1 / 40 * event.wheelDelta;
                event.wheelDeltaX && (this.deltaX = - 1 / 40 * event.wheelDeltaX);
            }

            // could be 0, so test if defined
            this.pointerId = (typeof pointerId !== "undefined") ? pointerId : 1;

            this.isPrimary = (typeof event.isPrimary !== "undefined") ? event.isPrimary : true;

            // in case of touch events, button is not defined
            this.button = event.button || 0;

            this.type = event.type;

            this.gameScreenX = this.pos.x;
            this.gameScreenY = this.pos.y;

            // get the current screen to world offset
            me.game.viewport.localToWorld(0, 0, viewportOffset);

            /* Initialize the two coordinate space properties. */
            this.gameWorldX = this.gameScreenX + viewportOffset.x;
            this.gameWorldY = this.gameScreenY + viewportOffset.y;

            // get the pointer size
            if (typeof(event.width) === "number") {
                // PointerEvent
                width = event.width;
                height = event.height;
            } else if (typeof(event.radiusX) === "number")  {
                // TouchEvent
                width = event.radiusX * 2;
                height = event.radiusY * 2;
            }
            // resize the pointer object accordingly
            this.resize(width, height);
        }
    });
})();
<commit_msg>isNormalized flag to know is the originating event is a PointerEvent<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2017 Olivier Biot
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * cache value for the offset of the canvas position within the page
     * @ignore
     */
    var viewportOffset = new me.Vector2d();

    /**
     * a pointer object, representing a single finger on a touch enabled device.
     * @class
     * @extends me.Rect
     * @memberOf me
     * @constructor
     */
    me.Pointer = me.Rect.extend(
    /** @scope me.Pointer.prototype */
    {
        /**
         * @ignore
         */
        init : function (x, y, w, h) {

          /**
            * the originating Event Object
            * @public
            * @type {Event}
            * @name event
            * @see https://developer.mozilla.org/en-US/docs/Web/API/Event
            * @memberOf me.Pointer
            */
            this.event = undefined;

           /**
            * a string containing the event's type.
            * @public
            * @type {String}
            * @name type
            * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/type
            * @memberOf me.Pointer
            */
            this.type = undefined;

           /**
            * the button property indicates which button was pressed on the mouse to trigger the event.
            * @public
            * @type {Number}
            * @name width
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
            * @memberOf me.Pointer
            */
            this.button = 0;

           /**
            * indicates whether or not the pointer device that created the event is the primary pointe.
            * @public
            * @type {Boolean}
            * @name width
            * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
            * @memberOf me.Pointer
            */
            this.isPrimary = false;

           /**
            * the horizontal coordinate within the application's client area at which the event occurred
            * @public
            * @type {Number}
            * @name clientX
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX
            * @memberOf me.Pointer
            */
            this.clientX = undefined;

           /**
            * the vertical coordinate within the application's client area at which the event occurred
            * @public
            * @type {Number}
            * @name clientY
            * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY
            * @memberOf me.Pointer
            */
            this.clientY = undefined;

          /**
            * Event normalized X coordinate within the game canvas itself

            *
            * @public
            * @type {Number}
            * @name gameX
            * @memberOf me.Pointer
            */
            this.gameX = undefined;

           /**
            * Event normalized Y coordinate within the game canvas itself

            *
            * @public
            * @type {Number}
            * @name gameY
            * @memberOf me.Pointer
            */
            this.gameY = undefined;

           /**
            * Event X coordinate relative to the viewport
            * @public
            * @type {Number}
            * @name gameScreenX
            * @memberOf me.Pointe
            */
            this.gameScreenX = undefined;

           /**
            * Event Y coordinate relative to the viewport
            * @public
            * @type {Number}
            * @name gameScreenY
            * @memberOf me.Pointer
            */
            this.gameScreenY = undefined;

           /**
            * Event X coordinate relative to the map
            * @public
            * @type {Number}
            * @name gameWorldX
            * @memberOf me.Pointer
            */
            this.gameWorldX = undefined;

           /**
            * Event Y coordinate relative to the map
            * @public
            * @type {Number}
            * @name gameWorldY
            * @memberOf me.Pointer
            */
            this.gameWorldY = undefined;

           /**
            * Event X coordinate relative to the holding container
            * @public
            * @type {Number}
            * @name gameLocalX
            * @memberOf me.Pointer
            */
            this.gameLocalX = undefined;

           /**
            * Event Y coordinate relative to the holding container
            * @public
            * @type {Number}
            * @name gameLocalY
            * @memberOf me.Pointer
            */
            this.gameLocalY = undefined;

           /**
            * The unique identifier of the contact for a touch, mouse or pen
            * @public
            * @type {Number}
            * @name pointerId
            * @memberOf me.Pointer
            * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh466123.aspx
            */
            this.pointerId = undefined;

            // parent constructor
            this._super(me.Rect, "init", [x || 0, y || 0, w || 1, h || 1]);
        },

        /**
         * initialize the Pointer object using the given Event Object
         * @name me.Pointer#set
         * @private
         * @function
         * @param {Event} event the original Event object
         * @param {Number} clientX the horizontal coordinate within the application's client area at which the event occurred
         * @param {Number} clientX the vertical coordinate within the application's client area at which the event occurred
         * @param {Number} pointedId the Pointer, Touch or Mouse event Id
         */
        setEvent : function (event, clientX, clientY, pointerId) {
            var width = 1;
            var height = 1;

            // the original event object
            this.event = event;

            this.clientX = clientX || 0;
            this.clientY = clientY || 0;

            // translate to local coordinates
            me.input.globalToLocal(clientX, clientY, this.pos);

            // true if not originally a pointer event
            this.isNormalized = !me.device.pointerEvent || (me.device.pointerEvent && !(event instanceof window.PointerEvent));

            if (event.type === "wheel") {
                this.deltaMode = 1;
                this.deltaX = event.deltaX;
                this.deltaY = - 1 / 40 * event.wheelDelta;
                event.wheelDeltaX && (this.deltaX = - 1 / 40 * event.wheelDeltaX);
            }

            // could be 0, so test if defined
            this.pointerId = (typeof pointerId !== "undefined") ? pointerId : 1;

            this.isPrimary = (typeof event.isPrimary !== "undefined") ? event.isPrimary : true;

            // in case of touch events, button is not defined
            this.button = event.button || 0;

            this.type = event.type;

            this.gameScreenX = this.pos.x;
            this.gameScreenY = this.pos.y;

            // get the current screen to world offset
            me.game.viewport.localToWorld(0, 0, viewportOffset);

            /* Initialize the two coordinate space properties. */
            this.gameWorldX = this.gameScreenX + viewportOffset.x;
            this.gameWorldY = this.gameScreenY + viewportOffset.y;

            // get the pointer size
            if (typeof(event.width) === "number") {
                // PointerEvent
                width = event.width;
                height = event.height;
            } else if (typeof(event.radiusX) === "number")  {
                // TouchEvent
                width = event.radiusX * 2;
                height = event.radiusY * 2;
            }
            // resize the pointer object accordingly
            this.resize(width, height);
        }
    });
})();
<|endoftext|>


<commit_before>function debounce (func, wait, immediate) {
    var timeout;
    return function () {
        var context = this, args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(function () {
            timeout = null;
            if (!immediate) func.apply(context, args);
        }, wait);
        if (immediate && !timeout) func.apply(context, args);
    };
};

L.MapboxGL = L.Layer.extend({
    options: {
      updateInterval: 50
    },

    initialize: function (options) {
        L.setOptions(this, options);

        if (options.accessToken) {
            mapboxgl.accessToken = options.accessToken;
        } else {
            throw new Error('You should provide a Mapbox GL access token as a token option.');
        }

        // setup throttling the update event when panning
        this._throttledUpdate = debounce.call(this, this._update, this.options.updateInterval);
    },

    onAdd: function (map) {
        this._map = map;

        if (!this._glContainer) {
            this._initContainer();
        }

        map._panes.tilePane.appendChild(this._glContainer);

        this._initGL();
    },

    onRemove: function (map) {
        map.getPanes().tilePane.removeChild(this._glContainer);
        this._glMap.remove();
        this._glMap = null;
    },

    getEvents: function () {
        return {
            move: this._throttledUpdate, // sensibly throttle updating while panning
            zoomanim: this._animateZoom, // ensure animation at the end of a zoom
            zoom: this._animateZoom // animate on the zoom event for smooth pinch-zooming
        }
    },

    addTo: function (map) {
        map.addLayer(this);
        return this;
    },

    _initContainer: function () {
        var container = this._glContainer = L.DomUtil.create('div', 'leaflet-gl-layer');

        var size = this._map.getSize();
        container.style.width  = size.x + 'px';
        container.style.height = size.y + 'px';
    },

    _initGL: function () {
        var center = this._map.getCenter();

        var options = L.extend({}, this.options, {
            container: this._glContainer,
            interactive: false,
            center: [center.lng, center.lat],
            zoom: this._map.getZoom() - 1,
            attributionControl: false
        });

        this._glMap = new mapboxgl.Map(options);
        // allow GL base map to pan beyond min/max latitudes
        this._glMap.transform.latRange = null;
    },

    _update: function () {
        var size = this._map.getSize(),
            container = this._glContainer,
            gl = this._glMap,
            topLeft = this._map.containerPointToLayerPoint([0, 0]);

        L.DomUtil.setPosition(container, topLeft);

        var center = this._map.getCenter();

        // gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
        // calling setView directly causes sync issues because it uses requestAnimFrame

        var tr = gl.transform;
        tr.center = mapboxgl.LngLat.convert([center.lng, center.lat]);
        tr.zoom = this._map.getZoom() - 1;

        if (gl.transform.width !== size.x || gl.transform.height !== size.y) {
            container.style.width  = size.x + 'px';
            container.style.height = size.y + 'px';
            gl.resize();
        } else {
            gl.update();
        }
    },

    _animateZoom: function (e) {
        // borrowed from the Leaflet 0.7.7 origin calcuation
        // https://github.com/Leaflet/Leaflet/blob/v0.7.7/src/map/anim/Map.ZoomAnimation.js#L47-L50
        var scale = this._map.getZoomScale(e.zoom),
            offset = this._map._getCenterOffset(e.center)._divideBy(1 - 1 / scale),
            origin = this._map._getCenterLayerPoint()._add(offset),
            zoomOffset = origin.add(this._map._getMapPanePos()).subtract(this._map.getSize().divideBy(2));

        this._glMap.zoomTo(e.zoom - 1, {
            duration: 250,
            offset: [zoomOffset.x, zoomOffset.y],
            easing: [0, 0, 0.25, 1]
        });
    }
});

L.mapboxGL = function (options) {
    return new L.MapboxGL(options);
};
<commit_msg>better sync during a pinch zoom<commit_after>function debounce (func, wait, immediate) {
    var timeout;
    return function () {
        var context = this, args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(function () {
            timeout = null;
            if (!immediate) func.apply(context, args);
        }, wait);
        if (immediate && !timeout) func.apply(context, args);
    };
};

L.MapboxGL = L.Layer.extend({
    options: {
      updateInterval: 50
    },

    initialize: function (options) {
        L.setOptions(this, options);

        if (options.accessToken) {
            mapboxgl.accessToken = options.accessToken;
        } else {
            throw new Error('You should provide a Mapbox GL access token as a token option.');
        }

        // setup throttling the update event when panning
        this._throttledUpdate = debounce.call(this, this._update, this.options.updateInterval);
    },

    onAdd: function (map) {
        this._map = map;

        if (!this._glContainer) {
            this._initContainer();
        }

        map._panes.tilePane.appendChild(this._glContainer);

        this._initGL();
    },

    onRemove: function (map) {
        map.getPanes().tilePane.removeChild(this._glContainer);
        this._glMap.remove();
        this._glMap = null;
    },

    getEvents: function () {
        return {
            move: this._throttledUpdate, // sensibly throttle updating while panning
            zoomanim: this._animateZoom, // ensure animation at the end of a zoom
            zoom: this._pinch // animate on the zoom event for smooth pinch-zooming
        }
    },

    addTo: function (map) {
        map.addLayer(this);
        return this;
    },

    _initContainer: function () {
        var container = this._glContainer = L.DomUtil.create('div', 'leaflet-gl-layer');

        var size = this._map.getSize();
        container.style.width  = size.x + 'px';
        container.style.height = size.y + 'px';
    },

    _initGL: function () {
        var center = this._map.getCenter();

        var options = L.extend({}, this.options, {
            container: this._glContainer,
            interactive: false,
            center: [center.lng, center.lat],
            zoom: this._map.getZoom() - 1,
            attributionControl: false
        });

        this._glMap = new mapboxgl.Map(options);
        // allow GL base map to pan beyond min/max latitudes
        this._glMap.transform.latRange = null;
    },

    _pinch: function (e) {
      this._glMap.jumpTo({
        zoom: this._map.getZoom() - 1,
        center: this._map.getCenter()
      });
    },

    _update: function (e) {
        var size = this._map.getSize(),
            container = this._glContainer,
            gl = this._glMap,
            topLeft = this._map.containerPointToLayerPoint([0, 0]);

        L.DomUtil.setPosition(container, topLeft);

        var center = this._map.getCenter();

        // gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
        // calling setView directly causes sync issues because it uses requestAnimFrame

        var tr = gl.transform;
        tr.center = mapboxgl.LngLat.convert([center.lng, center.lat]);
        tr.zoom = this._map.getZoom() - 1;

        if (gl.transform.width !== size.x || gl.transform.height !== size.y) {
            container.style.width  = size.x + 'px';
            container.style.height = size.y + 'px';
            gl.resize();
        } else {
            gl.update();
        }
    },
    _animateZoom: function (e) {
        // borrowed from the Leaflet 0.7.7 origin calcuation
        // https://github.com/Leaflet/Leaflet/blob/v0.7.7/src/map/anim/Map.ZoomAnimation.js#L47-L50
        var scale = this._map.getZoomScale(e.zoom),
            offset = this._map._getCenterOffset(e.center)._divideBy(1 - 1 / scale),
            origin = this._map._getCenterLayerPoint()._add(offset),
            zoomOffset = origin.add(this._map._getMapPanePos()).subtract(this._map.getSize().divideBy(2));

        this._glMap.zoomTo(e.zoom - 1, {
            duration: 250,
            offset: [zoomOffset.x, zoomOffset.y],
            easing: [0, 0, 0.25, 1]
        });
    }
});

L.mapboxGL = function (options) {
    return new L.MapboxGL(options);
};
<|endoftext|>


<commit_before>(function () {
  var providers = {};

  L.TileLayer.Provider = L.TileLayer.extend({
    initialize: function (arg, options) {
      var parts = arg.split('.');

      var providerName = parts[0];
      var variantName = parts[1];

      if (!providers[providerName]) {
        throw "No such provider (" + providerName + ")";
      }

      var provider = {
        url: providers[providerName].url,
        options: providers[providerName].options
      };

      // overwrite values in provider from variant.
      if (variantName && 'variants' in providers[providerName]) {
        if (!(variantName in providers[providerName].variants)) {
          throw "No such name in provider (" + variantName + ")";
        }
        var variant = providers[providerName].variants[variantName];
        provider = {
          url: variant.url || provider.url,
          options: L.Util.extend({}, provider.options, variant.options)
        };
      } else if (typeof provider.url === 'function') {
        provider.url = provider.url(parts.splice(1).join('.'));
      }

      // replace attribution placeholders with their values from toplevel provider attribution.
      var attribution = provider.options.attribution;
      if (attribution.indexOf('{attribution.') != -1) {
        provider.options.attribution = attribution.replace(/\{attribution.(\w*)\}/,
          function(match, attributionName){
            return providers[attributionName].options.attribution;
          });
      }
      // Compute final options combining provider options with any user overrides
      var layer_opts = L.Util.extend({}, provider.options, options);
      L.TileLayer.prototype.initialize.call(this, provider.url, layer_opts);
    }
  });

  /**
   * Definition of providers.
   * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
   */
  providers = {
    OpenStreetMap: {
      url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenStreetMap contributors, CC-BY-SA'
      },
      variants: {
        Mapnik: {},
        BlackAndWhite: {
          url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'
        },
        DE: {
          url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'
        }
      }
    },
    OpenCycleMap: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenCycleMap, OpenStreetMap contributors, CC-BY-SA'
      }
    },
    Thunderforest: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '{attribution.OpenCycleMap}'
      },
      variants: {
        OpenCycleMap: {},
        Transport: {
          url: 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png'
        },
        Landscape: {
          url: 'http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png'
        }
      }
    },
    MapQuestOpen: {
      url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
      options: {
        attribution: 'Tiles Courtesy of MapQuest — Map data {attribution.OpenStreetMap}',
        subdomains: '1234'
      },
      variants: {
        OSM: {},
        Aerial:{
          url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',
          options: {
            attribution:
              'Tiles Courtesy of MapQuest — ' +
              'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency'
          }
        }
      }
    },
    MapBox: {
      url: function(id) {
        return 'http://{s}.tiles.mapbox.com/v3/' + id + '/{z}/{x}/{y}.png';
      },
      options: {
        attribution: 'Imagery from MapBox — Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd'
      }
    },
    Stamen: {
      url: 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png',
      options: {
        attribution:
          'Map tiles by Stamen Design, CC BY 3.0 — ' +
          'Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd',
        minZoom: 0,
        maxZoom: 20
      },
      variants: {
        Toner: {},
        TonerBackground: {
          url: 'http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png'
        },
        TonerHybrid: {
          url: 'http://{s}.tile.stamen.com/toner-hybrid/{z}/{x}/{y}.png'
        },
        TonerLines: {
          url: 'http://{s}.tile.stamen.com/toner-lines/{z}/{x}/{y}.png'
        },
        TonerLabels: {
          url: 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
        },
        TonerLite: {
          url: 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
        },
        Terrain: {
          url: 'http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png',
          options: {
            minZoom: 4,
            maxZoom: 18
          }
        },
        Watercolor: {
          url: 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png',
          options: {
            minZoom: 3,
            maxZoom: 16
          }
        }
      }
    },
    Esri: {
      url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
      options: {
        attribution: 'Tiles © Esri'
      },
      variants: {
        WorldStreetMap: {
          options: {
            attribution: '{attribution.Esri} — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
          }
        },
        DeLorme: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme'
          }
        },
        WorldTopoMap: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
          }
        },
        WorldImagery: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
          }
        },
        WorldTerrain: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: USGS, Esri, TANA, DeLorme, and NPS'
          }
        },
        WorldShadedRelief: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri'
          }
        },
        WorldPhysical: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: US National Park Service'
          }
        },
        OceanBasemap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
          }
        },
        NatGeoWorldMap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
          }
        },
        WorldGrayCanvas: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ'
          }
        }
      }
    },
    OpenWeatherMap: {
      options: {
        attribution: 'Map data © OpenWeatherMap'
      },
      variants: {
        Clouds: {
          url: 'http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png'
        },
        CloudsClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/clouds_cls/{z}/{x}/{y}.png'
        },
        Precipitation: {
          url: 'http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png'
        },
        PrecipitationClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/precipitation_cls/{z}/{x}/{y}.png'
        },
        Rain: {
          url: 'http://{s}.tile.openweathermap.org/map/rain/{z}/{x}/{y}.png'
        },
        RainClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/rain_cls/{z}/{x}/{y}.png'
        },
        Pressure: {
          url: 'http://{s}.tile.openweathermap.org/map/pressure/{z}/{x}/{y}.png'
        },
        PressureContour: {
          url: 'http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png'
        },
        Wind: {
          url: 'http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png'
        },
        Temperature: {
          url: 'http://{s}.tile.openweathermap.org/map/temp/{z}/{x}/{y}.png'
        },
        Snow: {
          url: 'http://{s}.tile.openweathermap.org/map/snow/{z}/{x}/{y}.png'
        }
      }
    }

  };
}());

L.TileLayer.provider = function(provider, options){
  return new L.TileLayer.Provider(provider, options);
};
L.TileLayer.provider = function(provider){
  return new L.TileLayer.Provider(provider);
};

L.Control.Layers.Provided = L.Control.Layers.extend({
    initialize: function(base, overlay, options){
        var first;
        if(base.length){
            (function(){
                var out = {},
                len = base.length,
                i=0;
                while(i<len){
                    if (i === 0) {
                        first = L.TileLayer.provider(base[0]);
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = first;
                    } else {
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(base[i]);
                    }
                    i++;
                }
                base = out;
            }());
        this._first = first;
        }
        if(overlay && overlay.length){
            (function(){
                var out = {},
                len = overlay.length,
                i=0;
                while(i<len){
                    out[overlay[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(overlay[i]);
                    i++;
                }
                overlay = out;
            }());
        }
        L.Control.Layers.prototype.initialize.call(this, base, overlay, options);
    },
    onAdd: function(map){
        this._first.addTo(map);
        return L.Control.Layers.prototype.onAdd.call(this, map);
    }
});
L.control.layers.provided = function (baseLayers, overlays, options) {
    return new L.Control.Layers.Provided(baseLayers, overlays, options);
};
<commit_msg>duplicate factory function<commit_after>(function () {
  var providers = {};

  L.TileLayer.Provider = L.TileLayer.extend({
    initialize: function (arg, options) {
      var parts = arg.split('.');

      var providerName = parts[0];
      var variantName = parts[1];

      if (!providers[providerName]) {
        throw "No such provider (" + providerName + ")";
      }

      var provider = {
        url: providers[providerName].url,
        options: providers[providerName].options
      };

      // overwrite values in provider from variant.
      if (variantName && 'variants' in providers[providerName]) {
        if (!(variantName in providers[providerName].variants)) {
          throw "No such name in provider (" + variantName + ")";
        }
        var variant = providers[providerName].variants[variantName];
        provider = {
          url: variant.url || provider.url,
          options: L.Util.extend({}, provider.options, variant.options)
        };
      } else if (typeof provider.url === 'function') {
        provider.url = provider.url(parts.splice(1).join('.'));
      }

      // replace attribution placeholders with their values from toplevel provider attribution.
      var attribution = provider.options.attribution;
      if (attribution.indexOf('{attribution.') != -1) {
        provider.options.attribution = attribution.replace(/\{attribution.(\w*)\}/,
          function(match, attributionName){
            return providers[attributionName].options.attribution;
          });
      }
      // Compute final options combining provider options with any user overrides
      var layer_opts = L.Util.extend({}, provider.options, options);
      L.TileLayer.prototype.initialize.call(this, provider.url, layer_opts);
    }
  });

  /**
   * Definition of providers.
   * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
   */
  providers = {
    OpenStreetMap: {
      url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenStreetMap contributors, CC-BY-SA'
      },
      variants: {
        Mapnik: {},
        BlackAndWhite: {
          url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'
        },
        DE: {
          url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'
        }
      }
    },
    OpenCycleMap: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenCycleMap, OpenStreetMap contributors, CC-BY-SA'
      }
    },
    Thunderforest: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '{attribution.OpenCycleMap}'
      },
      variants: {
        OpenCycleMap: {},
        Transport: {
          url: 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png'
        },
        Landscape: {
          url: 'http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png'
        }
      }
    },
    MapQuestOpen: {
      url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
      options: {
        attribution: 'Tiles Courtesy of MapQuest — Map data {attribution.OpenStreetMap}',
        subdomains: '1234'
      },
      variants: {
        OSM: {},
        Aerial:{
          url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',
          options: {
            attribution:
              'Tiles Courtesy of MapQuest — ' +
              'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency'
          }
        }
      }
    },
    MapBox: {
      url: function(id) {
        return 'http://{s}.tiles.mapbox.com/v3/' + id + '/{z}/{x}/{y}.png';
      },
      options: {
        attribution: 'Imagery from MapBox — Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd'
      }
    },
    Stamen: {
      url: 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png',
      options: {
        attribution:
          'Map tiles by Stamen Design, CC BY 3.0 — ' +
          'Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd',
        minZoom: 0,
        maxZoom: 20
      },
      variants: {
        Toner: {},
        TonerBackground: {
          url: 'http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png'
        },
        TonerHybrid: {
          url: 'http://{s}.tile.stamen.com/toner-hybrid/{z}/{x}/{y}.png'
        },
        TonerLines: {
          url: 'http://{s}.tile.stamen.com/toner-lines/{z}/{x}/{y}.png'
        },
        TonerLabels: {
          url: 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
        },
        TonerLite: {
          url: 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
        },
        Terrain: {
          url: 'http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png',
          options: {
            minZoom: 4,
            maxZoom: 18
          }
        },
        Watercolor: {
          url: 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png',
          options: {
            minZoom: 3,
            maxZoom: 16
          }
        }
      }
    },
    Esri: {
      url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
      options: {
        attribution: 'Tiles © Esri'
      },
      variants: {
        WorldStreetMap: {
          options: {
            attribution: '{attribution.Esri} — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
          }
        },
        DeLorme: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme'
          }
        },
        WorldTopoMap: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
          }
        },
        WorldImagery: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
          }
        },
        WorldTerrain: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: USGS, Esri, TANA, DeLorme, and NPS'
          }
        },
        WorldShadedRelief: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri'
          }
        },
        WorldPhysical: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: US National Park Service'
          }
        },
        OceanBasemap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
          }
        },
        NatGeoWorldMap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
          }
        },
        WorldGrayCanvas: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ'
          }
        }
      }
    },
    OpenWeatherMap: {
      options: {
        attribution: 'Map data © OpenWeatherMap'
      },
      variants: {
        Clouds: {
          url: 'http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png'
        },
        CloudsClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/clouds_cls/{z}/{x}/{y}.png'
        },
        Precipitation: {
          url: 'http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png'
        },
        PrecipitationClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/precipitation_cls/{z}/{x}/{y}.png'
        },
        Rain: {
          url: 'http://{s}.tile.openweathermap.org/map/rain/{z}/{x}/{y}.png'
        },
        RainClassic: {
          url: 'http://{s}.tile.openweathermap.org/map/rain_cls/{z}/{x}/{y}.png'
        },
        Pressure: {
          url: 'http://{s}.tile.openweathermap.org/map/pressure/{z}/{x}/{y}.png'
        },
        PressureContour: {
          url: 'http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png'
        },
        Wind: {
          url: 'http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png'
        },
        Temperature: {
          url: 'http://{s}.tile.openweathermap.org/map/temp/{z}/{x}/{y}.png'
        },
        Snow: {
          url: 'http://{s}.tile.openweathermap.org/map/snow/{z}/{x}/{y}.png'
        }
      }
    }

  };
}());

L.TileLayer.provider = function(provider, options){
  return new L.TileLayer.Provider(provider, options);
};

L.Control.Layers.Provided = L.Control.Layers.extend({
    initialize: function(base, overlay, options){
        var first;
        if(base.length){
            (function(){
                var out = {},
                len = base.length,
                i=0;
                while(i<len){
                    if (i === 0) {
                        first = L.TileLayer.provider(base[0]);
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = first;
                    } else {
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(base[i]);
                    }
                    i++;
                }
                base = out;
            }());
        this._first = first;
        }
        if(overlay && overlay.length){
            (function(){
                var out = {},
                len = overlay.length,
                i=0;
                while(i<len){
                    out[overlay[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(overlay[i]);
                    i++;
                }
                overlay = out;
            }());
        }
        L.Control.Layers.prototype.initialize.call(this, base, overlay, options);
    },
    onAdd: function(map){
        this._first.addTo(map);
        return L.Control.Layers.prototype.onAdd.call(this, map);
    }
});
L.control.layers.provided = function (baseLayers, overlays, options) {
    return new L.Control.Layers.Provided(baseLayers, overlays, options);
};
<|endoftext|>


<commit_before>var AWS      = require('aws-sdk');
var config   = require('./config');
var fs       = require('fs');
var tmp      = require('tmp');
var processx = require('child_process');
var when     = require('when');
var nodefn   = require('when/node/function');
var es       = require('event-stream');
var request  = require('superagent');
var ipp      = require('ipp');
var _        = require('underscore');
var diacritics = require('diacritics');

function AgentClass() {

    var SQS = new AWS.SQS({
        accessKeyId: config.awsAccessKeys.global.accessKeyId,
        secretAccessKey: config.awsAccessKeys.global.secretAccessKey,
        region: 'us-east-1',
        apiVersion: '2012-11-05',
    });

    var nextPrintShopPrinter = 0;

    //============================================================
    //
    //
    //============================================================
    function selectQueue(message)
    {
        var box = normalize(message.box);

        if(message.anonymous) { // For printshop

            nextPrintShopPrinter++;

            if(nextPrintShopPrinter%5===1) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-12'; }
            if(nextPrintShopPrinter%5===3) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-14'; }
            if(nextPrintShopPrinter%5===4) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-15'; }

            return 'ipp://localhost:631/classes/printshop';
        }

        if(box<'0060') return 'ipp://localhost:631/classes/ctr-a';
        if(box<'0130') return 'ipp://localhost:631/classes/ctr-b';
        if(box<'0200') return 'ipp://localhost:631/classes/ctr-c';

        if(box<'2000') return 'ipp://localhost:631/classes/org-a';
        if(box<'3000') return 'ipp://localhost:631/classes/org-b';
        if(box<'5000') return 'ipp://localhost:631/classes/org-c';

        return 'ipp://localhost:631/classes/default';
    }

    //============================================================
    //
    //
    //============================================================
    this.processMessage = function processMessage(message) {

        message.printerUri = selectQueue(message);

        console.log('Processing:', message);

        var filenames = [ nodefn.call(tmp.tmpName, { postfix: '.pdf'       } ),
                          nodefn.call(tmp.tmpName, { postfix: '.ps'        } ),
                          nodefn.call(tmp.tmpName, { postfix: '.custom.ps' } ) ];

        return when.all(filenames).then(function (filenames) {

            return when(download(message.url, filenames[0])).then(function () {

                return convertToPS(filenames[0], filenames[1]);

            }).then(function () {

                return prepare(filenames[1], filenames[2], message);

            }).then(function () {

                return print(filenames[2], message);

            }).then(function (jobUri) {

                return nodefn.call(SQS.sendMessage.bind(SQS), {
                    QueueUrl: config.printsmart.awsQueues.updateJobStatus,
                    MessageBody: JSON.stringify({ 'id': message.id, 'printerUri': message.printerUri, 'jobUri': jobUri })
                });

            }).then(function () {

                return when.map(filenames, function (filepath) { return nodefn.call(fs.unlink, filepath); } );
            });

        }).otherwise(function (err) {

            console.log('ERROR ==>', err);
        });
    };
}

module.exports = exports = new AgentClass();

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

//============================================================
//
//
//============================================================
function download(inputUrl, outputPath) {

    var deferred = when.defer();

    var req = request.get(inputUrl);

    var stream = fs.createWriteStream(outputPath);

    req.pipe(stream);

    stream.on('finish', function() {
        deferred.resolve(outputPath);
    });

    return deferred.promise;
}

//============================================================
//
//
//============================================================
function convertToPS(filePath, outputPath) {

    var deferred = when.defer();

    var ls = processx.spawn('pdftops', [filePath, outputPath]);

    ls.stdout.on('data', function (data) { console.log('stdout: ' + data); });
    ls.stderr.on('data', function (data) { console.log('stderr: ' + data); });

    ls.on('close', function (code) {

        if(code==0) deferred.resolve(outputPath);
        else        deferred.reject(code);
    });

    return deferred.promise;
}

//============================================================
//
//
//============================================================
function prepare(inputPath, outputPath, message) {

    var deferred = when.defer();
    var page = 0;

    var rStream = fs.createReadStream(inputPath, { encoding: 'utf8' });
    var wStream = fs.createWriteStream(outputPath);

    wStream.write('@PJL SET FINISH = LEFT1POINT\n', 'UTF-8');

    var transform = function (line) {

        // if(line=='%%EndSetup') {
        //
        //     line  = '%%BeginFeature: *Duplex NoTumble\n';
        //     line += '(<<) cvx exec /Duplex true /Tumble false (>>) cvx exec setpagedevice\n';
        //     line += '%%EndFeature\n';
        //     line += '%%BeginFeature: *Stapling Single-Portrait\n';
        //     line += '<< /Staple 3 /StapleDetails << /Type 1 /StapleLocation (SinglePortrait) >> >> setpagedevice\n';
        //     line += '%%EndFeature\n';
        //     line += '%%EndSetup';
        // }

        if(line=='%%Page: 1 1') page = 1;
        if(line=='%%Page: 2 2') page = 2;

        if(!message.anonymous && line=='showpage' && page==1) {

            line  = 'newpath\n';

            // TOP OF PAGE

            // NAME
            line += '/Helvetica-Bold findfont 12 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 20 sub 190 exch moveto\n';
            line += '('+escape(diacritics.remove(message.name||"Not Named"))+') show\n';

            // COUNTRY

            if(message.government) {
                line += '/Helvetica-Bold findfont 10 scalefont setfont\n';
                line += 'currentpagedevice /PageSize get aload pop exch pop 36 sub 190 exch moveto\n';
                line += '('+escape(diacritics.remove(message.government))+') show\n';
            }

            line += '/Helvetica findfont 7 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 48 sub 190 exch moveto\n';
            line += '(SCBD PrintSmart - Copy printed ON-DEMAND) show\n';

            // TOP LEFT

            var box = [message.box, initials(message.name||"")].join(' - ');

            line += '/Helvetica-Bold findfont 24 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 30 sub 40 exch moveto\n';
            line += '('+escape(diacritics.remove(box||"NOT-SET"))+') show\n';

            // TOP RIGHT

            var tag = diacritics.remove(message.tag||"");

            if(message.tag) {
                line += '/Helvetica-Bold findfont 24 scalefont setfont\n';
                line += 'currentpagedevice /PageSize get aload pop exch pop 30 sub 472 exch moveto\n';
                line += '('+escape(diacritics.remove(tag))+') true charpath 1 setlinewidth 0.0 setgray stroke\n';
            }

            line += 'showpage';
        }

        return line + '\n';
    };

    rStream.pipe(es.split())
           .pipe(es.mapSync(transform))
           .pipe(es.wait())
           .pipe(wStream);

    wStream.on('finish', function () {
        deferred.resolve(outputPath);
    });

    return deferred.promise;
}

//============================================================
//
//
//
//============================================================
function escape (text) {
    return text.replace(/\\/, '\\\\')
               .replace(/\(/, '\\(')
               .replace(/\)/, '\\)');
}

//============================================================
//
//
//
//============================================================
function initials (name) {

    var names = (name||"").toString().toLocaleUpperCase().split(' ');

    return _.chain(names).map(function(n){
        return (n||"").replace(/[^A-Z\s]+/gi, '').charAt(0);
    }).compact().value().join('');
}

//============================================================
//
//
//
//============================================================
function normalize (text) {

    text = (text||"").toString();

    while(text.length<4)
        text = '0'+text;

    return text;
}

//============================================================
//
//
//============================================================
function print(filename, message) {

    var printer = ipp.Printer(message.printerUri);

    var title  = message.box + ': ' + message.symbol + ' (' + message.language + ') - ' + message.name;
    var copies = Math.round(message.copies || 1);

    var options = {
        'operation-attributes-tag': {
            'job-name': title,
            'requesting-user-name': 'PrintSmart',
            'document-format': 'application/postscript'
        },
        "job-attributes-tag": {
            'sides': 'two-sided-long-edge',
            'finishings': ['staple'],
            'copies' : copies
        },
        data: fs.readFileSync(filename)
    };

    if(message.multijobs && copies>1) { //Send copies-1 copy of document for stupid PCL canon printers

        delete options['job-attributes-tag'].copies;

        for(var i=1;i<copies;++i) {
            nodefn.call(printer.execute.bind(printer), "Print-Job", options);
        }
    }

    return when(nodefn.call(printer.execute.bind(printer), "Print-Job", options), function (res) {
        return res['job-attributes-tag']['job-uri'];
    });
}
<commit_msg>Print copy info<commit_after>var AWS      = require('aws-sdk');
var config   = require('./config');
var fs       = require('fs');
var tmp      = require('tmp');
var processx = require('child_process');
var when     = require('when');
var nodefn   = require('when/node/function');
var es       = require('event-stream');
var request  = require('superagent');
var ipp      = require('ipp');
var _        = require('underscore');
var diacritics = require('diacritics');

function AgentClass() {

    var SQS = new AWS.SQS({
        accessKeyId: config.awsAccessKeys.global.accessKeyId,
        secretAccessKey: config.awsAccessKeys.global.secretAccessKey,
        region: 'us-east-1',
        apiVersion: '2012-11-05',
    });

    var nextPrintShopPrinter = 0;

    //============================================================
    //
    //
    //============================================================
    function selectQueue(message)
    {
        var box = normalize(message.box);

        if(message.anonymous) { // For printshop

            nextPrintShopPrinter++;

            if(nextPrintShopPrinter%5===1) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-12'; }
            if(nextPrintShopPrinter%5===3) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-14'; }
            if(nextPrintShopPrinter%5===4) { message.multijobs=true; return 'ipp://localhost:631/classes/printshop-pcl-15'; }

            return 'ipp://localhost:631/classes/printshop';
        }

        if(box<'0060') return 'ipp://localhost:631/classes/ctr-a';
        if(box<'0130') return 'ipp://localhost:631/classes/ctr-b';
        if(box<'0200') return 'ipp://localhost:631/classes/ctr-c';

        if(box<'2000') return 'ipp://localhost:631/classes/org-a';
        if(box<'3000') return 'ipp://localhost:631/classes/org-b';
        if(box<'5000') return 'ipp://localhost:631/classes/org-c';

        return 'ipp://localhost:631/classes/default';
    }

    //============================================================
    //
    //
    //============================================================
    this.processMessage = function processMessage(message) {

        message.printerUri = selectQueue(message);

        console.log('Processing:', message);

        var filenames = [ nodefn.call(tmp.tmpName, { postfix: '.pdf'       } ),
                          nodefn.call(tmp.tmpName, { postfix: '.ps'        } ),
                          nodefn.call(tmp.tmpName, { postfix: '.custom.ps' } ) ];

        return when.all(filenames).then(function (filenames) {

            return when(download(message.url, filenames[0])).then(function () {

                return convertToPS(filenames[0], filenames[1]);

            }).then(function () {

                return prepare(filenames[1], filenames[2], message);

            }).then(function () {

                return print(filenames[2], message);

            }).then(function (jobUri) {

                return nodefn.call(SQS.sendMessage.bind(SQS), {
                    QueueUrl: config.printsmart.awsQueues.updateJobStatus,
                    MessageBody: JSON.stringify({ 'id': message.id, 'printerUri': message.printerUri, 'jobUri': jobUri })
                });

            }).then(function () {

                return when.map(filenames, function (filepath) { return nodefn.call(fs.unlink, filepath); } );
            });

        }).otherwise(function (err) {

            console.log('ERROR ==>', err);
        });
    };
}

module.exports = exports = new AgentClass();

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

//============================================================
//
//
//============================================================
function download(inputUrl, outputPath) {

    var deferred = when.defer();

    var req = request.get(inputUrl);

    var stream = fs.createWriteStream(outputPath);

    req.pipe(stream);

    stream.on('finish', function() {
        deferred.resolve(outputPath);
    });

    return deferred.promise;
}

//============================================================
//
//
//============================================================
function convertToPS(filePath, outputPath) {

    var deferred = when.defer();

    var ls = processx.spawn('pdftops', [filePath, outputPath]);

    ls.stdout.on('data', function (data) { console.log('stdout: ' + data); });
    ls.stderr.on('data', function (data) { console.log('stderr: ' + data); });

    ls.on('close', function (code) {

        if(code==0) deferred.resolve(outputPath);
        else        deferred.reject(code);
    });

    return deferred.promise;
}

//============================================================
//
//
//============================================================
function prepare(inputPath, outputPath, message) {

    var deferred = when.defer();
    var page = 0;

    var rStream = fs.createReadStream(inputPath, { encoding: 'utf8' });
    var wStream = fs.createWriteStream(outputPath);

    wStream.write('@PJL SET FINISH = LEFT1POINT\n', 'UTF-8');

    var transform = function (line) {

        // if(line=='%%EndSetup') {
        //
        //     line  = '%%BeginFeature: *Duplex NoTumble\n';
        //     line += '(<<) cvx exec /Duplex true /Tumble false (>>) cvx exec setpagedevice\n';
        //     line += '%%EndFeature\n';
        //     line += '%%BeginFeature: *Stapling Single-Portrait\n';
        //     line += '<< /Staple 3 /StapleDetails << /Type 1 /StapleLocation (SinglePortrait) >> >> setpagedevice\n';
        //     line += '%%EndFeature\n';
        //     line += '%%EndSetup';
        // }

        if(line=='%%Page: 1 1') page = 1;
        if(line=='%%Page: 2 2') page = 2;

        if(!message.anonymous && line=='showpage' && page==1) {

            line  = 'newpath\n';

            // TOP OF PAGE

            // NAME
            line += '/Helvetica-Bold findfont 12 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 20 sub 190 exch moveto\n';
            line += '('+escape(diacritics.remove(message.name||"Not Named"))+') show\n';

            // COUNTRY

            if(message.government) {
                line += '/Helvetica-Bold findfont 10 scalefont setfont\n';
                line += 'currentpagedevice /PageSize get aload pop exch pop 36 sub 190 exch moveto\n';
                line += '('+escape(diacritics.remove(message.government))+') show\n';
            }

            line += '/Helvetica findfont 7 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 48 sub 190 exch moveto\n';
            line += '(SCBD PrintSmart - Copy printed ON-DEMAND) show\n';

            // TOP LEFT

            var box = [message.box, initials(message.name||"")].join(' - ');

            line += '/Helvetica-Bold findfont 24 scalefont setfont\n';
            line += 'currentpagedevice /PageSize get aload pop exch pop 30 sub 40 exch moveto\n';
            line += '('+escape(diacritics.remove(box||"NOT-SET"))+') show\n';

            // TOP RIGHT

            var tag = diacritics.remove(message.tag||"");

            if(message.tag) {
                line += '/Helvetica-Bold findfont 24 scalefont setfont\n';
                line += 'currentpagedevice /PageSize get aload pop exch pop 30 sub 472 exch moveto\n';
                line += '('+escape(diacritics.remove(tag))+') true charpath 1 setlinewidth 0.0 setgray stroke\n';
            }

            line += 'showpage';
        }

        return line + '\n';
    };

    rStream.pipe(es.split())
           .pipe(es.mapSync(transform))
           .pipe(es.wait())
           .pipe(wStream);

    wStream.on('finish', function () {
        deferred.resolve(outputPath);
    });

    return deferred.promise;
}

//============================================================
//
//
//
//============================================================
function escape (text) {
    return text.replace(/\\/, '\\\\')
               .replace(/\(/, '\\(')
               .replace(/\)/, '\\)');
}

//============================================================
//
//
//
//============================================================
function initials (name) {

    var names = (name||"").toString().toLocaleUpperCase().split(' ');

    return _.chain(names).map(function(n){
        return (n||"").replace(/[^A-Z\s]+/gi, '').charAt(0);
    }).compact().value().join('');
}

//============================================================
//
//
//
//============================================================
function normalize (text) {

    text = (text||"").toString();

    while(text.length<4)
        text = '0'+text;

    return text;
}

//============================================================
//
//
//============================================================
function print(filename, message) {

    var printer = ipp.Printer(message.printerUri);

    var title  = message.box + ': ' + message.symbol + ' (' + message.language + ') - ' + message.name;
    var copies = Math.round(message.copies || 1);

    var options = {
        'operation-attributes-tag': {
            'job-name': title,
            'requesting-user-name': 'PrintSmart',
            'document-format': 'application/postscript'
        },
        "job-attributes-tag": {
            'sides': 'two-sided-long-edge',
            'finishings': ['staple'],
            'copies' : copies
        },
        data: fs.readFileSync(filename)
    };

    if(message.multijobs && copies>1) { //Send copies-1 copy of document for stupid PCL canon printers

        delete options['job-attributes-tag'].copies;

        for(var i=1;i<copies;++i) {

            Console.log("Copy: ", i, '/', copies);

            nodefn.call(printer.execute.bind(printer), "Print-Job", options);
        }

        Console.log("Copy: ", copies, '/', copies);
    }

    if(options['job-attributes-tag'].copies)
        Console.log("Copies: ", copies);

    return when(nodefn.call(printer.execute.bind(printer), "Print-Job", options), function (res) {
        return res['job-attributes-tag']['job-uri'];
    });
}
<|endoftext|>


<commit_before>import React, { Component, propTypes } from 'react';
import Touchable from 'rc-touchable';

class InputHandler extends Component {
  render() {
    const { prefixCls, disabled, ...otherProps } = this.props;
    return (
      <Touchable disabled={disabled} activeClassName={`${prefixCls}-handler-active`}>
        <span {...otherProps} />
      </Touchable>
    );
  }
}

InputHandler.propTypes = {
  prefixCls: propTypes.string,
  disabled: propTypes.bool,
};

export default InputHandler;
<commit_msg>fix: typo error, propTypes => PropTypes<commit_after>import React, { Component, PropTypes } from 'react';
import Touchable from 'rc-touchable';

class InputHandler extends Component {
  render() {
    const { prefixCls, disabled, ...otherProps } = this.props;
    return (
      <Touchable disabled={disabled} activeClassName={`${prefixCls}-handler-active`}>
        <span {...otherProps} />
      </Touchable>
    );
  }
}

InputHandler.propTypes = {
  prefixCls: PropTypes.string,
  disabled: PropTypes.bool,
};

export default InputHandler;
<|endoftext|>


<commit_before>import clone     from './utils/clone';
import * as is   from './utils/is';
import events    from './utils/events';
import extend    from './utils/extend';
import * as arr  from './utils/arr';
import Eventable from './Eventable';
import defaults  from './defaultOptions';
import { scope } from './scope';

import {
  getElementRect,
  nodeContains,
  trySelector,
}                     from './utils/domUtils';
import { getWindow }  from './utils/window';
import { wheelEvent } from './utils/browser';

class Interactable {
  /** */
  constructor (target, options, defaultContext) {
    this._signals = options.signals;
    this._actions = options.actions;
    this.target   = target;
    this.events   = new Eventable();
    this._context = options.context || defaultContext;
    this._win     = getWindow(trySelector(target)? this._context : target);
    this._doc     = this._win.document;

    this._signals.fire('new', {
      target,
      options,
      interactable: this,
      win: this._win,
    });

    this.set(options);
  }

  setOnEvents (action, phases) {
    const onAction = 'on' + action;

    if (is.func(phases.onstart)       ) { this.events[onAction + 'start'        ] = phases.onstart         ; }
    if (is.func(phases.onmove)        ) { this.events[onAction + 'move'         ] = phases.onmove          ; }
    if (is.func(phases.onend)         ) { this.events[onAction + 'end'          ] = phases.onend           ; }
    if (is.func(phases.oninertiastart)) { this.events[onAction + 'inertiastart' ] = phases.oninertiastart  ; }

    return this;
  }

  setPerAction (actionName, options) {
    // for all the default per-action options
    for (const optionName in options) {
      const actionOptions = this.options[actionName];
      const optionValue = options[optionName];
      const isArray = is.array(optionValue);

      // if the option value is an array
      if (isArray) {
        actionOptions[optionName] = arr.from(optionValue);
      }
      // if the option value is an object
      else if (!isArray && is.plainObject(optionValue)) {
        // copy the object
        actionOptions[optionName] = extend(
          actionOptions[optionName] || {},
          clone(optionValue));

        // set anabled field to true if it exists in the defaults
        if (is.object(defaults.perAction[optionName]) && 'enabled' in defaults.perAction[optionName]) {
          actionOptions[optionName].enabled = optionValue.enabled === false? false : true;
        }
      }
      // if the option value is a boolean and the default is an object
      else if (is.bool(optionValue) && is.object(defaults.perAction[optionName])) {
        actionOptions[optionName].enabled = optionValue;
      }
      // if it's anything else, do a plain assignment
      else {
        actionOptions[optionName] = optionValue;
      }
    }
  }

  /**
   * The default function to get an Interactables bounding rect. Can be
   * overridden using {@link Interactable.rectChecker}.
   *
   * @param {Element} [element] The element to measure.
   * @return {object} The object's bounding rectangle.
   */
  getRect (element) {
    element = element || this.target;

    if (is.string(this.target) && !(is.element(element))) {
      element = this._context.querySelector(this.target);
    }

    return getElementRect(element);
  }

  /**
   * Returns or sets the function used to calculate the interactable's
   * element's rectangle
   *
   * @param {function} [checker] A function which returns this Interactable's
   * bounding rectangle. See {@link Interactable.getRect}
   * @return {function | object} The checker function or this Interactable
   */
  rectChecker (checker) {
    if (is.func(checker)) {
      this.getRect = checker;

      return this;
    }

    if (checker === null) {
      delete this.options.getRect;

      return this;
    }

    return this.getRect;
  }

  _backCompatOption (optionName, newValue) {
    if (trySelector(newValue) || is.object(newValue)) {
      this.options[optionName] = newValue;

      for (const action of this._actions.names) {
        this.options[action][optionName] = newValue;
      }

      return this;
    }

    return this.options[optionName];
  }

  /**
   * Gets or sets the origin of the Interactable's element.  The x and y
   * of the origin will be subtracted from action event coordinates.
   *
   * @param {Element | object | string} [origin] An HTML or SVG Element whose
   * rect will be used, an object eg. { x: 0, y: 0 } or string 'parent', 'self'
   * or any CSS selector
   *
   * @return {object} The current origin or this Interactable
   */
  origin (newValue) {
    return this._backCompatOption('origin', newValue);
  }

  /**
   * Returns or sets the mouse coordinate types used to calculate the
   * movement of the pointer.
   *
   * @param {string} [newValue] Use 'client' if you will be scrolling while
   * interacting; Use 'page' if you want autoScroll to work
   * @return {string | object} The current deltaSource or this Interactable
   */
  deltaSource (newValue) {
    if (newValue === 'page' || newValue === 'client') {
      this.options.deltaSource = newValue;

      return this;
    }

    return this.options.deltaSource;
  }

  /**
   * Gets the selector context Node of the Interactable. The default is
   * `window.document`.
   *
   * @return {Node} The context Node of this Interactable
   */
  context () {
    return this._context;
  }

  inContext (element) {
    return (this._context === element.ownerDocument
            || nodeContains(this._context, element));
  }

  /**
   * Calls listeners for the given InteractEvent type bound globally
   * and directly to this Interactable
   *
   * @param {InteractEvent} iEvent The InteractEvent object to be fired on this
   * Interactable
   * @return {Interactable} this Interactable
   */
  fire (iEvent) {
    this.events.fire(iEvent);

    return this;
  }

  _onOffMultiple (method, eventType, listener, options) {
    if (is.string(eventType) && eventType.search(' ') !== -1) {
      eventType = eventType.trim().split(/ +/);
    }

    if (is.array(eventType)) {
      for (const type of eventType) {
        this[method](type, listener, options);
      }

      return true;
    }

    if (is.object(eventType)) {
      for (const prop in eventType) {
        this[method](prop, eventType[prop], listener);
      }

      return true;
    }
  }

  /**
   * Binds a listener for an InteractEvent, pointerEvent or DOM event.
   *
   * @param {string | array | object} eventType  The types of events to listen
   * for
   * @param {function} listener   The function event (s)
   * @param {object | boolean} [options]    options object or useCapture flag
   * for addEventListener
   * @return {object} This Interactable
   */
  on (eventType, listener, options) {
    if (this._onOffMultiple('on', eventType, listener, options)) {
      return this;
    }

    if (eventType === 'wheel') { eventType = wheelEvent; }

    if (arr.contains(this._actions.eventTypes, eventType)) {
      this.events.on(eventType, listener);
    }
    // delegated event for selector
    else if (is.string(this.target)) {
      events.addDelegate(this.target, this._context, eventType, listener, options);
    }
    else {
      events.add(this.target, eventType, listener, options);
    }

    return this;
  }

  /**
   * Removes an InteractEvent, pointerEvent or DOM event listener
   *
   * @param {string | array | object} eventType The types of events that were
   * listened for
   * @param {function} listener The listener function to be removed
   * @param {object | boolean} [options] options object or useCapture flag for
   * removeEventListener
   * @return {object} This Interactable
   */
  off (eventType, listener, options) {
    if (this._onOffMultiple('off', eventType, listener, options)) {
      return this;
    }

    if (eventType === 'wheel') { eventType = wheelEvent; }

    // if it is an action event type
    if (arr.contains(this._actions.eventTypes, eventType)) {
      this.events.off(eventType, listener);
    }
    // delegated event
    else if (is.string(this.target)) {
      events.removeDelegate(this.target, this._context, eventType, listener, options);
    }
    // remove listener from this Interatable's element
    else {
      events.remove(this.target, eventType, listener, options);
    }

    return this;
  }

  /**
   * Reset the options of this Interactable
   *
   * @param {object} options The new settings to apply
   * @return {object} This Interactable
   */
  set (options) {
    if (!is.object(options)) {
      options = {};
    }

    this.options = extend({}, defaults.base);

    for (const actionName in this._actions.methodDict) {
      const methodName = this._actions.methodDict[actionName];

      this.options[actionName] = {};
      this.setPerAction(actionName, extend(extend({}, defaults.perAction), defaults[actionName]));

      this[methodName](options[actionName]);
    }

    for (const setting in options) {
      if (is.func(this[setting])) {
        this[setting](options[setting]);
      }
    }

    this._signals.fire('set', {
      options,
      interactable: this,
    });

    return this;
  }

  /**
   * Remove this interactable from the list of interactables and remove it's
   * action capabilities and event listeners
   *
   * @return {interact}
   */
  unset () {
    events.remove(this.target, 'all');

    if (is.string(this.target)) {
      // remove delegated events
      for (const type in events.delegatedEvents) {
        const delegated = events.delegatedEvents[type];

        if (delegated.selectors[0] === this.target
            && delegated.contexts[0] === this._context) {

          delegated.selectors.splice(0, 1);
          delegated.contexts .splice(0, 1);
          delegated.listeners.splice(0, 1);

          // remove the arrays if they are empty
          if (!delegated.selectors.length) {
            delegated[type] = null;
          }
        }

        events.remove(this._context, type, events.delegateListener);
        events.remove(this._context, type, events.delegateUseCapture, true);
      }
    }
    else {
      events.remove(this, 'all');
    }

    this._signals.fire('unset', { interactable: this });

    return scope.interact;
  }
}

export default Interactable;
<commit_msg>Interactable: don't return interact from unset()<commit_after>import clone     from './utils/clone';
import * as is   from './utils/is';
import events    from './utils/events';
import extend    from './utils/extend';
import * as arr  from './utils/arr';
import Eventable from './Eventable';
import defaults  from './defaultOptions';

import {
  getElementRect,
  nodeContains,
  trySelector,
}                     from './utils/domUtils';
import { getWindow }  from './utils/window';
import { wheelEvent } from './utils/browser';

class Interactable {
  /** */
  constructor (target, options, defaultContext) {
    this._signals = options.signals;
    this._actions = options.actions;
    this.target   = target;
    this.events   = new Eventable();
    this._context = options.context || defaultContext;
    this._win     = getWindow(trySelector(target)? this._context : target);
    this._doc     = this._win.document;

    this._signals.fire('new', {
      target,
      options,
      interactable: this,
      win: this._win,
    });

    this.set(options);
  }

  setOnEvents (action, phases) {
    const onAction = 'on' + action;

    if (is.func(phases.onstart)       ) { this.events[onAction + 'start'        ] = phases.onstart         ; }
    if (is.func(phases.onmove)        ) { this.events[onAction + 'move'         ] = phases.onmove          ; }
    if (is.func(phases.onend)         ) { this.events[onAction + 'end'          ] = phases.onend           ; }
    if (is.func(phases.oninertiastart)) { this.events[onAction + 'inertiastart' ] = phases.oninertiastart  ; }

    return this;
  }

  setPerAction (actionName, options) {
    // for all the default per-action options
    for (const optionName in options) {
      const actionOptions = this.options[actionName];
      const optionValue = options[optionName];
      const isArray = is.array(optionValue);

      // if the option value is an array
      if (isArray) {
        actionOptions[optionName] = arr.from(optionValue);
      }
      // if the option value is an object
      else if (!isArray && is.plainObject(optionValue)) {
        // copy the object
        actionOptions[optionName] = extend(
          actionOptions[optionName] || {},
          clone(optionValue));

        // set anabled field to true if it exists in the defaults
        if (is.object(defaults.perAction[optionName]) && 'enabled' in defaults.perAction[optionName]) {
          actionOptions[optionName].enabled = optionValue.enabled === false? false : true;
        }
      }
      // if the option value is a boolean and the default is an object
      else if (is.bool(optionValue) && is.object(defaults.perAction[optionName])) {
        actionOptions[optionName].enabled = optionValue;
      }
      // if it's anything else, do a plain assignment
      else {
        actionOptions[optionName] = optionValue;
      }
    }
  }

  /**
   * The default function to get an Interactables bounding rect. Can be
   * overridden using {@link Interactable.rectChecker}.
   *
   * @param {Element} [element] The element to measure.
   * @return {object} The object's bounding rectangle.
   */
  getRect (element) {
    element = element || this.target;

    if (is.string(this.target) && !(is.element(element))) {
      element = this._context.querySelector(this.target);
    }

    return getElementRect(element);
  }

  /**
   * Returns or sets the function used to calculate the interactable's
   * element's rectangle
   *
   * @param {function} [checker] A function which returns this Interactable's
   * bounding rectangle. See {@link Interactable.getRect}
   * @return {function | object} The checker function or this Interactable
   */
  rectChecker (checker) {
    if (is.func(checker)) {
      this.getRect = checker;

      return this;
    }

    if (checker === null) {
      delete this.options.getRect;

      return this;
    }

    return this.getRect;
  }

  _backCompatOption (optionName, newValue) {
    if (trySelector(newValue) || is.object(newValue)) {
      this.options[optionName] = newValue;

      for (const action of this._actions.names) {
        this.options[action][optionName] = newValue;
      }

      return this;
    }

    return this.options[optionName];
  }

  /**
   * Gets or sets the origin of the Interactable's element.  The x and y
   * of the origin will be subtracted from action event coordinates.
   *
   * @param {Element | object | string} [origin] An HTML or SVG Element whose
   * rect will be used, an object eg. { x: 0, y: 0 } or string 'parent', 'self'
   * or any CSS selector
   *
   * @return {object} The current origin or this Interactable
   */
  origin (newValue) {
    return this._backCompatOption('origin', newValue);
  }

  /**
   * Returns or sets the mouse coordinate types used to calculate the
   * movement of the pointer.
   *
   * @param {string} [newValue] Use 'client' if you will be scrolling while
   * interacting; Use 'page' if you want autoScroll to work
   * @return {string | object} The current deltaSource or this Interactable
   */
  deltaSource (newValue) {
    if (newValue === 'page' || newValue === 'client') {
      this.options.deltaSource = newValue;

      return this;
    }

    return this.options.deltaSource;
  }

  /**
   * Gets the selector context Node of the Interactable. The default is
   * `window.document`.
   *
   * @return {Node} The context Node of this Interactable
   */
  context () {
    return this._context;
  }

  inContext (element) {
    return (this._context === element.ownerDocument
            || nodeContains(this._context, element));
  }

  /**
   * Calls listeners for the given InteractEvent type bound globally
   * and directly to this Interactable
   *
   * @param {InteractEvent} iEvent The InteractEvent object to be fired on this
   * Interactable
   * @return {Interactable} this Interactable
   */
  fire (iEvent) {
    this.events.fire(iEvent);

    return this;
  }

  _onOffMultiple (method, eventType, listener, options) {
    if (is.string(eventType) && eventType.search(' ') !== -1) {
      eventType = eventType.trim().split(/ +/);
    }

    if (is.array(eventType)) {
      for (const type of eventType) {
        this[method](type, listener, options);
      }

      return true;
    }

    if (is.object(eventType)) {
      for (const prop in eventType) {
        this[method](prop, eventType[prop], listener);
      }

      return true;
    }
  }

  /**
   * Binds a listener for an InteractEvent, pointerEvent or DOM event.
   *
   * @param {string | array | object} eventType  The types of events to listen
   * for
   * @param {function} listener   The function event (s)
   * @param {object | boolean} [options]    options object or useCapture flag
   * for addEventListener
   * @return {object} This Interactable
   */
  on (eventType, listener, options) {
    if (this._onOffMultiple('on', eventType, listener, options)) {
      return this;
    }

    if (eventType === 'wheel') { eventType = wheelEvent; }

    if (arr.contains(this._actions.eventTypes, eventType)) {
      this.events.on(eventType, listener);
    }
    // delegated event for selector
    else if (is.string(this.target)) {
      events.addDelegate(this.target, this._context, eventType, listener, options);
    }
    else {
      events.add(this.target, eventType, listener, options);
    }

    return this;
  }

  /**
   * Removes an InteractEvent, pointerEvent or DOM event listener
   *
   * @param {string | array | object} eventType The types of events that were
   * listened for
   * @param {function} listener The listener function to be removed
   * @param {object | boolean} [options] options object or useCapture flag for
   * removeEventListener
   * @return {object} This Interactable
   */
  off (eventType, listener, options) {
    if (this._onOffMultiple('off', eventType, listener, options)) {
      return this;
    }

    if (eventType === 'wheel') { eventType = wheelEvent; }

    // if it is an action event type
    if (arr.contains(this._actions.eventTypes, eventType)) {
      this.events.off(eventType, listener);
    }
    // delegated event
    else if (is.string(this.target)) {
      events.removeDelegate(this.target, this._context, eventType, listener, options);
    }
    // remove listener from this Interatable's element
    else {
      events.remove(this.target, eventType, listener, options);
    }

    return this;
  }

  /**
   * Reset the options of this Interactable
   *
   * @param {object} options The new settings to apply
   * @return {object} This Interactable
   */
  set (options) {
    if (!is.object(options)) {
      options = {};
    }

    this.options = extend({}, defaults.base);

    for (const actionName in this._actions.methodDict) {
      const methodName = this._actions.methodDict[actionName];

      this.options[actionName] = {};
      this.setPerAction(actionName, extend(extend({}, defaults.perAction), defaults[actionName]));

      this[methodName](options[actionName]);
    }

    for (const setting in options) {
      if (is.func(this[setting])) {
        this[setting](options[setting]);
      }
    }

    this._signals.fire('set', {
      options,
      interactable: this,
    });

    return this;
  }

  /**
   * Remove this interactable from the list of interactables and remove it's
   * action capabilities and event listeners
   *
   * @return {interact}
   */
  unset () {
    events.remove(this.target, 'all');

    if (is.string(this.target)) {
      // remove delegated events
      for (const type in events.delegatedEvents) {
        const delegated = events.delegatedEvents[type];

        if (delegated.selectors[0] === this.target
            && delegated.contexts[0] === this._context) {

          delegated.selectors.splice(0, 1);
          delegated.contexts .splice(0, 1);
          delegated.listeners.splice(0, 1);

          // remove the arrays if they are empty
          if (!delegated.selectors.length) {
            delegated[type] = null;
          }
        }

        events.remove(this._context, type, events.delegateListener);
        events.remove(this._context, type, events.delegateUseCapture, true);
      }
    }
    else {
      events.remove(this, 'all');
    }

    this._signals.fire('unset', { interactable: this });
  }
}

export default Interactable;
<|endoftext|>


<commit_before>
var defaultOps = {
  url: '/log',
  contentType: 'application/json; charset=utf-8',
  stringify: JSON.stringify,
  jQuery: (typeof window !== 'undefined') ? window.jQuery : null,
  method: 'POST',
  interval: 10 * 1000,
  enabled: true
};

function AjaxJQueryOutput(opts) {
  this.url = opts.url || defaultOps.url;
  this.contentType = opts.contentType || defaultOps.contentType;
  this.stringify = opts.stringify || defaultOps.stringify;
  this.jQuery = opts.jQuery || defaultOps.jQuery;
  this.method = opts.method || defaultOps.method;
  this.interval = opts.interval || defaultOps.interval;

  this.cache = [];
  this.currentPayload = [];
  this.timer = null;
  this.timerMultiplier = 1;

  this.enabled = (typeof opts.enabled !== 'undefined') ? opts.enabled : defaultOps.enabled;
}

AjaxJQueryOutput.prototype.enable = function enable() {
  this.enabled = true;
  this._startTimer();
};

AjaxJQueryOutput.prototype.disable = function enable() {
  this.enabled = false;
  this._stopTimer();
};

AjaxJQueryOutput.prototype._startTimer = function _startTimer() {
  if (this.enabled && !this.timer && this.cache.length > 0) {
    this.timer = setTimeout(this._onTimer.bind(this), this.interval * this.timerMultiplier);
  }
};

AjaxJQueryOutput.prototype._stopTimer = function _startTimer() {
  if (this.timer) {
    clearTimeout(this.timer);
    this.timer = null;
  }
};

AjaxJQueryOutput.prototype.receive = function receive(logEvent) {
  this.cache.push(logEvent);

  if (this.enabled) {
    this._startTimer();
  }
};

AjaxJQueryOutput.prototype._onTimer = function _onTimer() {
  this.timer = null;

  if (this.enabled && this.cache.length > 0 && this.jQuery) {
    this.currentPayload = this.currentPayload.concat(this.cache);
    this.cache = [];

    var data = this.stringify({
      logs: this.currentPayload
    });

    var self = this;

    this.jQuery.ajax({
      type: this.method,
      data: data,
      url: this.url,
      contentType: this.contentType,
      timeout: 10 * 1000,
      cache: false,
      processData: false
    }).success(function() {
      self.currentPayload = [];
      self.timerMultiplier = 1;
    }).error(function () {
      // back off exponentially
      self.timerMultiplier *= 2;
    }).always(function() {
      self._startTimer();
    });
  }
};

module.exports = {
  AjaxJQueryOutput: AjaxJQueryOutput
};
<commit_msg>Consistent formatting<commit_after>var defaultOps = {
  url: '/log',
  contentType: 'application/json; charset=utf-8',
  stringify: JSON.stringify,
  jQuery: (typeof window !== 'undefined') ? window.jQuery : null,
  method: 'POST',
  interval: 10 * 1000,
  enabled: true
};

function AjaxJQueryOutput(opts) {
  this.url = opts.url || defaultOps.url;
  this.contentType = opts.contentType || defaultOps.contentType;
  this.stringify = opts.stringify || defaultOps.stringify;
  this.jQuery = opts.jQuery || defaultOps.jQuery;
  this.method = opts.method || defaultOps.method;
  this.interval = opts.interval || defaultOps.interval;

  this.cache = [];
  this.currentPayload = [];
  this.timer = null;
  this.timerMultiplier = 1;

  this.enabled = (typeof opts.enabled !== 'undefined') ? opts.enabled : defaultOps.enabled;
}

AjaxJQueryOutput.prototype.enable = function enable() {
  this.enabled = true;
  this._startTimer();
};

AjaxJQueryOutput.prototype.disable = function enable() {
  this.enabled = false;
  this._stopTimer();
};

AjaxJQueryOutput.prototype._startTimer = function _startTimer() {
  if (this.enabled && !this.timer && this.cache.length > 0) {
    this.timer = setTimeout(this._onTimer.bind(this), this.interval * this.timerMultiplier);
  }
};

AjaxJQueryOutput.prototype._stopTimer = function _startTimer() {
  if (this.timer) {
    clearTimeout(this.timer);
    this.timer = null;
  }
};

AjaxJQueryOutput.prototype.receive = function receive(logEvent) {
  this.cache.push(logEvent);

  if (this.enabled) {
    this._startTimer();
  }
};

AjaxJQueryOutput.prototype._onTimer = function _onTimer() {
  this.timer = null;

  if (this.enabled && this.cache.length > 0 && this.jQuery) {
    this.currentPayload = this.currentPayload.concat(this.cache);
    this.cache = [];

    var data = this.stringify({
      logs: this.currentPayload
    });

    var self = this;

    this.jQuery.ajax({
      type: this.method,
      data: data,
      url: this.url,
      contentType: this.contentType,
      timeout: 10 * 1000,
      cache: false,
      processData: false
    }).success(function () {
      self.currentPayload = [];
      self.timerMultiplier = 1;
    }).error(function () {
      // back off exponentially
      self.timerMultiplier *= 2;
    }).always(function () {
      self._startTimer();
    });
  }
};

module.exports = {
  AjaxJQueryOutput: AjaxJQueryOutput
};
<|endoftext|>