<commit_before>/* exported anim */

var anim =
// @EXPORT@
(function() {
  'use strict';

  var
    FUNC_KEYS = {
      'ease': [0.25, 0.1, 0.25, 1],
      'linear': [0, 0, 1, 1],
      'ease-in': [0.42, 0, 1, 1],
      'ease-out': [0, 0, 0.58, 1],
      'ease-in-out': [0.42, 0, 0.58, 1]
    },
    MSPF = 1000 / 60 / 2, // precision ms/frame (FPS: 60)

    requestAnim = window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function(callback) { setTimeout(callback, MSPF); },

    cancelAnim = window.cancelAnimationFrame ||
      window.mozCancelAnimationFrame ||
      window.webkitCancelAnimationFrame ||
      window.msCancelAnimationFrame ||
      function(requestID) { clearTimeout(requestID); },

    /**
     * @callback frameCallback
     * @param {} value - A value that was made by `valueCallback`.
     * @param {boolean} finish
     * @param {number} timeRatio - Progress [0, 1].
     * @param {number} outputRatio - Progress [0, 1].
     */

    /**
     * @typedef {Object} task
     * @property {number} animId
     * @property {frameCallback} frameCallback - Callback that is called each frame.
     * @property {number} duration
     * @property {number} count - `0` as infinite.
     * @property {{value, timeRatio: number, outputRatio: number}[]} frames
     * @property {(number|null)} framesStart - The time when first frame ran, or `null` if it is not running.
     * @property {number} loopsLeft - A counter for loop.
     */

    /** @type {task[]} */
    tasks = [],
    newAnimId = -1,
    requestID;

  var running; // [DEBUG/]

  window.addReqFrameAnim2 = function(cb) { requestAnim = cb; }; // [DEBUG/]
  window.delReqFrameAnim2 = function(cb) { cancelAnim = cb; }; // [DEBUG/]

  function step() {
    running = true; // [DEBUG/]
    var now = Date.now(), next = false;
    if (requestID) {
      cancelAnim.call(window, requestID);
      requestID = null;
    }

    tasks.forEach(function(task) {
      var timeLen, loops, frame;

      if (!task.framesStart) { return; }
      timeLen = now - task.framesStart;

      if (timeLen >= task.duration && task.count && task.loopsLeft <= 1) {
        frame = task.frames[task.frames.length - 1];
        task.frameCallback(frame.value, true, 1, 1);
        task.framesStart = null;
        return;
      }
      if (timeLen > task.duration) {
        loops = Math.floor(timeLen / task.duration);
        if (task.count && loops >= task.loopsLeft) { // Here `task.loopsLeft > 1`
          task.frameCallback(frame.value, true, 1, 1);
          task.framesStart = null;
          return;
        }
        task.loopsLeft -= loops;
        task.framesStart += task.duration * loops;
        timeLen = now - task.framesStart;
      }

      frame = task.frames[Math.round(timeLen / MSPF)];
      if (task.frameCallback(frame.value, false, frame.timeRatio, frame.outputRatio
          // [DEBUG]
          , timeLen
          // [/DEBUG]
          ) !== false) {
        next = true;
      } else {
        task.framesStart = null;
      }
    });

    if (next) { requestID = requestAnim.call(window, step); }
  }

  // [DEBUG]
  window.anim_lastRunning = false;
  window.anim_watchStart = function() {
    window.anim_watchTimer = setInterval(function() {
      if (running !== window.anim_lastRunning) {
        document.body.style.backgroundColor = running ? '#f7f6cb' : '';
        window.anim_lastRunning = running;
      }
      running = false;
    }, 200);
  };
  window.anim_watchStop = function() { clearInterval(window.anim_watchTimer); };
  // [/DEBUG]

  function startTask(task) {
    task.framesStart = Date.now();
    task.loopsLeft = task.count;
    step();
  }

  window.tasks = tasks; // [DEBUG/]

  return {
    /**
     * Callback that makes value that is required by each frame.
     * @callback valueCallback
     * @param {number} outputRatio - Progress [0, 1].
     * @returns {}
     */

    /**
     * @param {valueCallback} valueCallback - valueCallback
     * @param {frameCallback} frameCallback - task property
     * @param {number} duration - task property
     * @param {number} count - task property
     * @param {(string|number[])} timing - FUNC_KEYS or [x1, y1, x2, y2]
     * @returns {number} - animID to remove.
     */
    add: function(valueCallback, frameCallback, duration, count, timing) {
      var animId = ++newAnimId, task, frames,
        stepX, stepT, nextX, t, point;

      function getPoint(t) {
        var t2 = t * t, t3 = t2 * t, t1 = 1 - t, t12 = t1 * t1,
          p1f = 3 * t12 * t, p2f = 3 * t1 * t2;
        return {
          x: p1f * timing[0] + p2f * timing[2] + t3,
          y: p1f * timing[1] + p2f * timing[3] + t3
        };
      }

      function newFrame(timeRatio, outputRatio) {
        return {value: valueCallback(outputRatio),
          timeRatio: timeRatio, outputRatio: outputRatio};
      }

      if (typeof timing === 'string') { timing = FUNC_KEYS[timing]; }

      // Generate `frames` list
      if (duration < MSPF) {
        frames = [newFrame(0, 0), newFrame(1, 1)];
      } else {
        stepX = MSPF / duration;
        frames = [newFrame(0, 0)];

        if (timing[0] === 0 && timing[1] === 0 && timing[2] === 1 && timing[3] === 1) { // linear
          for (nextX = stepX; nextX <= 1; nextX += stepX) {
            frames.push(newFrame(nextX, nextX)); // x === y
          }

        } else {
          stepT = stepX / 10; // precision for `t`
          nextX = stepX;
          for (t = stepT; t <= 1; t += stepT) {
            point = getPoint(t);
            if (point.x >= nextX) {
              frames.push(newFrame(point.x, point.y));
              nextX += stepX;
            }
          }
        }

        frames.push(newFrame(1, 1)); // for tolerance
      }

      task = {
        animId: animId,
        frameCallback: frameCallback, duration: duration, count: count, // task properties
        frames: frames
      };
      tasks.push(task);
      startTask(task);

      return animId;
    },

    remove: function(animId) {
      var iRemove;
      if (tasks.some(function(task, i) {
        if (task.animId === animId) {
          iRemove = i;
          task.framesStart = null; // for `tasks.forEach` that is running now.
          return true;
        }
        return false;
      })) {
        tasks.splice(iRemove, 1);
      }
    },

    start: function(animId) {
      tasks.some(function(task) {
        if (task.animId === animId) {
          startTask(task);
          return true;
        }
        return false;
      });
    },

    stop: function(animId) {
      tasks.some(function(task) {
        if (task.animId === animId) {
          task.framesStart = null;
          return true;
        }
        return false;
      });
    },

    validTiming: function(timing) {
      return typeof timing === 'string' ? FUNC_KEYS[timing] :
        Array.isArray(timing) && [0, 1, 2, 3].every(function(i) {
          return typeof timing[i] === 'number' && timing[i] >= 0 && timing[i] <= 1;
        }) ? [timing[0], timing[1], timing[2], timing[3]] :
        null;
    }
  };
})()
// @/EXPORT@
;
<commit_msg>Add `reverse` anim.js<commit_after>/* exported anim */

var anim =
// @EXPORT@
(function() {
  'use strict';

  var
    FUNC_KEYS = {
      'ease': [0.25, 0.1, 0.25, 1],
      'linear': [0, 0, 1, 1],
      'ease-in': [0.42, 0, 1, 1],
      'ease-out': [0, 0, 0.58, 1],
      'ease-in-out': [0.42, 0, 0.58, 1]
    },
    MSPF = 1000 / 60 / 2, // precision ms/frame (FPS: 60)

    requestAnim = window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function(callback) { setTimeout(callback, MSPF); },

    cancelAnim = window.cancelAnimationFrame ||
      window.mozCancelAnimationFrame ||
      window.webkitCancelAnimationFrame ||
      window.msCancelAnimationFrame ||
      function(requestID) { clearTimeout(requestID); },

    /**
     * @callback frameCallback
     * @param {} value - A value that was made by `valueCallback`.
     * @param {boolean} finish
     * @param {number} timeRatio - Progress [0, 1].
     * @param {number} outputRatio - Progress [0, 1].
     */

    /**
     * @typedef {Object} task
     * @property {number} animId
     * @property {frameCallback} frameCallback - Callback that is called each frame.
     * @property {number} duration
     * @property {number} count - `0` as infinite.
     * @property {{value, timeRatio: number, outputRatio: number}[]} frames
     * @property {(number|null)} framesStart - The time when first frame ran, or `null` if it is not running.
     * @property {number} loopsLeft - A counter for loop.
     * @property {boolean} reverse
     */

    /** @type {task[]} */
    tasks = [],
    newAnimId = -1,
    requestID;

  window.addReqFrameAnim2 = function(cb) { requestAnim = cb; }; // [DEBUG/]
  window.delReqFrameAnim2 = function(cb) { cancelAnim = cb; }; // [DEBUG/]
  window.tasks = tasks; // [DEBUG/]

  var running; // [DEBUG/]

  function step() {
    running = true; // [DEBUG/]
    var now = Date.now(), next = false;
    if (requestID) {
      cancelAnim.call(window, requestID);
      requestID = null;
    }

    tasks.forEach(function(task) {
      var timeLen, loops, frame;

      if (!task.framesStart) { return; }
      timeLen = now - task.framesStart;

      if (timeLen >= task.duration && task.count && task.loopsLeft <= 1) {
        frame = task.frames[task.reverse ? 0 : task.frames.length - 1];
        task.frameCallback(frame.value, true, frame.timeRatio, frame.outputRatio);
        task.framesStart = null;
        return;
      }
      if (timeLen > task.duration) {
        loops = Math.floor(timeLen / task.duration);
        if (task.count) {
          if (loops >= task.loopsLeft) { // Here `task.loopsLeft > 1`
            frame = task.frames[task.reverse ? 0 : task.frames.length - 1];
            task.frameCallback(frame.value, true, frame.timeRatio, frame.outputRatio);
            task.framesStart = null;
            return;
          }
          task.loopsLeft -= loops;
        }
        task.framesStart += task.duration * loops;
        timeLen = now - task.framesStart;
      }

      if (task.reverse) { timeLen = task.duration - timeLen; }
      frame = task.frames[Math.round(timeLen / MSPF)];
      if (task.frameCallback(frame.value, false, frame.timeRatio, frame.outputRatio
          // [DEBUG]
          , timeLen
          // [/DEBUG]
          ) !== false) {
        next = true;
      } else {
        task.framesStart = null;
      }
    });

    if (next) { requestID = requestAnim.call(window, step); }
  }

  // [DEBUG]
  window.anim_lastRunning = false;
  window.anim_watchStart = function() {
    window.anim_watchTimer = setInterval(function() {
      if (running !== window.anim_lastRunning) {
        document.body.style.backgroundColor = running ? '#f7f6cb' : '#fff'; // not `''` for IE bug
        window.anim_lastRunning = running;
      }
      running = false;
    }, 200);
  };
  window.anim_watchStop = function() { clearInterval(window.anim_watchTimer); };
  // [/DEBUG]

  function startTask(task) {
    task.framesStart = Date.now();
    task.loopsLeft = task.count;
    step();
  }

  return {
    /**
     * Callback that makes value that is required by each frame.
     * @callback valueCallback
     * @param {number} outputRatio - Progress [0, 1].
     * @returns {}
     */

    /**
     * @param {(valueCallback|null)} valueCallback - valueCallback
     * @param {frameCallback} frameCallback - task property
     * @param {number} duration - task property
     * @param {number} count - task property
     * @param {(string|number[])} timing - FUNC_KEYS or [x1, y1, x2, y2]
     * @param {boolean} [reverse] - running property
     * @returns {number} - animID to remove.
     */
    add: function(valueCallback, frameCallback, duration, count, timing, reverse) {
      var animId = ++newAnimId, task, frames,
        stepX, stepT, nextX, t, point;

      function getPoint(t) {
        var t2 = t * t, t3 = t2 * t, t1 = 1 - t, t12 = t1 * t1,
          p1f = 3 * t12 * t, p2f = 3 * t1 * t2;
        return {
          x: p1f * timing[0] + p2f * timing[2] + t3,
          y: p1f * timing[1] + p2f * timing[3] + t3
        };
      }

      function newFrame(timeRatio, outputRatio) {
        return {value: valueCallback(outputRatio),
          timeRatio: timeRatio, outputRatio: outputRatio};
      }

      if (typeof timing === 'string') { timing = FUNC_KEYS[timing]; }
      valueCallback = valueCallback || function() {};

      // Generate `frames` list
      if (duration < MSPF) {
        frames = [newFrame(0, 0), newFrame(1, 1)];
      } else {
        stepX = MSPF / duration;
        frames = [newFrame(0, 0)];

        if (timing[0] === 0 && timing[1] === 0 && timing[2] === 1 && timing[3] === 1) { // linear
          for (nextX = stepX; nextX <= 1; nextX += stepX) {
            frames.push(newFrame(nextX, nextX)); // x === y
          }

        } else {
          stepT = stepX / 10; // precision for `t`
          nextX = stepX;
          for (t = stepT; t <= 1; t += stepT) {
            point = getPoint(t);
            if (point.x >= nextX) {
              frames.push(newFrame(point.x, point.y));
              nextX += stepX;
            }
          }
        }

        frames.push(newFrame(1, 1)); // for tolerance
      }

      task = {
        animId: animId,
        frameCallback: frameCallback, duration: duration, count: count, // task properties
        frames: frames,
        reverse: !!reverse
      };
      tasks.push(task);
      startTask(task);

      return animId;
    },

    remove: function(animId) {
      var iRemove;
      if (tasks.some(function(task, i) {
        if (task.animId === animId) {
          iRemove = i;
          task.framesStart = null; // for `tasks.forEach` that is running now.
          return true;
        }
        return false;
      })) {
        tasks.splice(iRemove, 1);
      }
    },

    start: function(animId, reverse) {
      tasks.some(function(task) {
        if (task.animId === animId) {
          task.reverse = !!reverse;
          startTask(task);
          return true;
        }
        return false;
      });
    },

    stop: function(animId) {
      tasks.some(function(task) {
        if (task.animId === animId) {
          task.framesStart = null;
          return true;
        }
        return false;
      });
    },

    validTiming: function(timing) {
      return typeof timing === 'string' ? FUNC_KEYS[timing] :
        Array.isArray(timing) && [0, 1, 2, 3].every(function(i) {
          return typeof timing[i] === 'number' && timing[i] >= 0 && timing[i] <= 1;
        }) ? [timing[0], timing[1], timing[2], timing[3]] :
        null;
    }
  };
})()
// @/EXPORT@
;
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc module
 * @name vxWamp
 * @description
 *
 * # vxWamp
 *
 * The `vxWamp` module provides seamless WAMPv2 integration, by wrapping AutobahnJS, for angular apps.
 *
 * ## Example
 * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
 *
 *
 *

 */
/* global -vxWampModule */
var vxWampModule = angular.module('vxWamp', []).provider('$wamp', $WampProvider);

/**
 * @ngdoc provider
 * @name $wampProvider
 *
 * @description
 *
 * Used for accessing a WAMP resource.
 *
 * ## Example
 * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
 *
 * ## Dependencies
 * Requires the {@link vxWamp `vxWamp`} module to be installed.
 */
function $WampProvider() {
    var options;

    /**
     * @ngdoc method
     * @name $wampProvider#init
     *
     * @param {Object} initOptions autobahnJS connection options
     *
     *    Object properties:
     *
     *      Required options:
     *
     *    - `url`: `{string=}` - the WebSocket URL of the WAMP router to connect to, e.g. ws://myserver.com:8080/ws
     *    - `realm`: `{string=}` - The WAMP realm to join, e.g. realm1
     *
     *    Optional options:
     *
     *      Options that control what kind of Deferreds to use:
     *
     *      - `use_es6_promises`: `{boolean=}` - use deferreds based on ES6 promises
     *      - `use_deferred`: `{callable=}` - if provided, use this deferred constructor, e.g. jQuery.Deferred or Q.defer
     *
     *      Options that control automatic reconnection:
     *
     *      - `max_retries`: `{integer=}` - Maximum number of reconnection attempts (default: 15)
     *      - `initial_retry_delay`: `{float=}` - Initial delay for reconnection attempt in seconds (default: 1.5).
     *      - `max_retry_delay`: `{float=}` - Maximum delay for reconnection attempts in seconds (default: 300).
     *      - `retry_delay_growth`: `{float=}` - The growth factor applied to the retry delay between reconnection attempts (default: 1.5).
     *      - `retry_delay_jitter`: `{float=}` - The standard deviation of a Gaussian to jitter the delay on each retry cycle as a fraction of the mean (default: 0.1).
     *
     * @description
     * Configures the AutobhanJS Service
     */
    this.init = function (initOptions) {
        options = initOptions || {};
    };

    this.$get = function ($rootScope, $q, $log) {

        /**
         * @ngdoc service
         * @name $wamp
         *
         * @description
         * `$wamp` give you access to the autobahnJS methods, call, register, subscribe, unsubscribe and publish.
         *
         * Requires the {@link vxWamp `vxWamp`} module to be installed.
         *
         * You can configure the WAMP connection through {@link vxWamp.$wampProvider $wampProvider}'s API.
         *
         * @example
         *
         * app.config(function ($wampProvider) {
         *      $wampProvider.init({
         *          url: 'ws://127.0.0.1:9000/',
         *          realm: 'realm1'
         *      });
         * })
         *
         *  app.controller("MyCtrl", function($scope, $wamp) {
         *
         *      // 1) subscribe to a topic
         *      function onevent(args) {
         *          $scope.hello = args[0];
         *      }
         *      $wamp.subscribe('com.myapp.hello', onevent);
         *
         *      // 2) publish an event
         *      $wamp.publish('com.myapp.hello', ['Hello, world!']);
         *
         *      // 3) register a procedure for remoting
         *      function add2(args) {
         *          return args[0] + args[1];
         *      }
         *      $wamp.register('com.myapp.add2', add2);
         *
         *      // 4) call a remote procedure
         *      $wamp.call('com.myapp.add2', [2, 3]).then(
         *          function (res) {
         *          $scope.add2 = res;
         *      });
         * });
         *
         *
         * @todo write more docs
         */

        var callbackQueue = [], connection;
        var onChallengeDeferred = $q.defer();

        /**
         * @param session
         * @param method
         * @param extra
         * @returns {*}
         *
         * @description
         * Gets called when a Challenge Message is sent by the router
         */
        var onchallenge = function (session, method, extra) {

            $rootScope.$broadcast("$wamp.onchallenge", {
                promise: onChallengeDeferred,
                session: session,
                method: method,
                extra: extra
            });

            return onChallengeDeferred.promise;
        };

        /**
         * @param func
         * @returns {Function}
         *
         * @description
         * Wraps a callback with a function that calls scope.$apply(), so that the callback is added to the digest
         */
        function digestWrapper(func) {
            return function () {
                var cb = func.apply(this, arguments);
                $rootScope.$apply();
                return cb;
            };
        }

        options = angular.extend({onchallenge: digestWrapper(onchallenge), use_deferred: $q.defer}, options);

        connection = new autobahn.Connection(options);
        connection.onopen = digestWrapper(function (session) {
            $log.debug("Congrats!  You're connected to the WAMP server!");
            $rootScope.$broadcast("$wamp.open", session);

            //Call any callbacks that were queued up before the connection was established
            var call, resultPromise;

            while (callbackQueue.length > 0) {
                call = callbackQueue.shift();
                resultPromise = session[call.method].apply(session, call.args);
                call.promise.resolve(resultPromise);
                $log.debug("processed queued " + call.method);
            }
        });

        connection.onclose = digestWrapper(function (reason, details) {
            $log.debug("Connection Closed: ", reason);
            $rootScope.$broadcast("$wamp.close", {reason: reason, details: details});

        });

        /**
         * Subscription object which self manages reconnections
         * @param topic
         * @param handler
         * @param options
         * @param subscribedCallback
         * @returns {{}}
         * @constructor
         */
        var Subscription = function (topic, handler, options, subscribedCallback) {

            var subscription = {}, unregister, onOpen, deferred = $q.defer();

            handler = digestWrapper(handler);

            onOpen = function () {
                var p = connection.session.subscribe(topic, handler, options).then(
                    function (s) {
                        subscription = angular.extend(s, subscription);
                        deferred.resolve(subscription);
                        return s;
                    }
                );
                if (subscribedCallback) {
                    subscribedCallback(p);
                }

            };

            if (connection.isOpen) {
                onOpen();
            }

            unregister = $rootScope.$on("$wamp.open", onOpen);

            subscription.promise = deferred.promise;
            subscription.unsubscribe = function () {
                unregister(); //Remove the event listener, so this object can get cleaned up by gc
                return connection.session.unsubscribe(subscription);
            };

            return subscription;
        };

        return {
            connection: connection,
            session: connection.session,
            open: function () {
                connection.open();
            },
            close: function () {
                connection.close();
            },
            subscribe: function (topic, handler, options, subscribedCallback) {
                return Subscription(topic, handler, options, subscribedCallback).promise;
            },
            unsubscribe: function (subscription) {
                return subscription.unsubscribe();
            },
            publish: function (topic, args, kwargs, options) {

                if (!connection.isOpen) {
                    var deferred = $q.defer();
                    callbackQueue.push({method: 'publish', args: arguments, promise: deferred});
                    $log.debug("connection not open, queuing publish");
                    return deferred.promise;
                }

                return connection.session.publish(topic, args, kwargs, options);
            },
            register: function (procedure, endpoint, options) {

                endpoint = digestWrapper(endpoint);
                if (!connection.isOpen) {
                    var deferred = $q.defer();
                    callbackQueue.push({
                        method: 'register',
                        args: [procedure, endpoint, options],
                        promise: deferred
                    });
                    $log.debug("connection not open, queuing register");
                    return deferred.promise;
                }

                return connection.session.register(procedure, endpoint, options);
            },
            call: function (procedure, args, kwargs, options) {

                if (!connection.isOpen) {
                    var deferred = $q.defer();
                    callbackQueue.push({method: 'call', args: arguments, promise: deferred});
                    $log.debug("connection not open, queuing call");
                    return deferred.promise;
                }

                return connection.session.call(procedure, args, kwargs, options);
            }
        };
    };

    return this;

}
<commit_msg>eliminate need for callback queue<commit_after>'use strict';

/**
 * @ngdoc module
 * @name vxWamp
 * @description
 *
 * # vxWamp
 *
 * The `vxWamp` module provides seamless WAMPv2 integration, by wrapping AutobahnJS, for angular apps.
 *
 * ## Example
 * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
 *
 *
 *

 */
/* global -vxWampModule */
var vxWampModule = angular.module('vxWamp', []).provider('$wamp', $WampProvider);

/**
 * @ngdoc provider
 * @name $wampProvider
 *
 * @description
 *
 * Used for accessing a WAMP resource.
 *
 * ## Example
 * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
 *
 * ## Dependencies
 * Requires the {@link vxWamp `vxWamp`} module to be installed.
 */
function $WampProvider() {
    var options;

    /**
     * @ngdoc method
     * @name $wampProvider#init
     *
     * @param {Object} initOptions autobahnJS connection options
     *
     *    Object properties:
     *
     *      Required options:
     *
     *    - `url`: `{string=}` - the WebSocket URL of the WAMP router to connect to, e.g. ws://myserver.com:8080/ws
     *    - `realm`: `{string=}` - The WAMP realm to join, e.g. realm1
     *
     *    Optional options:
     *
     *      Options that control what kind of Deferreds to use:
     *
     *      - `use_es6_promises`: `{boolean=}` - use deferreds based on ES6 promises
     *      - `use_deferred`: `{callable=}` - if provided, use this deferred constructor, e.g. jQuery.Deferred or Q.defer
     *
     *      Options that control automatic reconnection:
     *
     *      - `max_retries`: `{integer=}` - Maximum number of reconnection attempts (default: 15)
     *      - `initial_retry_delay`: `{float=}` - Initial delay for reconnection attempt in seconds (default: 1.5).
     *      - `max_retry_delay`: `{float=}` - Maximum delay for reconnection attempts in seconds (default: 300).
     *      - `retry_delay_growth`: `{float=}` - The growth factor applied to the retry delay between reconnection attempts (default: 1.5).
     *      - `retry_delay_jitter`: `{float=}` - The standard deviation of a Gaussian to jitter the delay on each retry cycle as a fraction of the mean (default: 0.1).
     *
     * @description
     * Configures the AutobhanJS Service
     */
    this.init = function (initOptions) {
        options = initOptions || {};
    };

    this.$get = function ($rootScope, $q, $log) {

        /**
         * @ngdoc service
         * @name $wamp
         *
         * @description
         * `$wamp` give you access to the autobahnJS methods, call, register, subscribe, unsubscribe and publish.
         *
         * Requires the {@link vxWamp `vxWamp`} module to be installed.
         *
         * You can configure the WAMP connection through {@link vxWamp.$wampProvider $wampProvider}'s API.
         *
         * @example
         *
         * app.config(function ($wampProvider) {
         *      $wampProvider.init({
         *          url: 'ws://127.0.0.1:9000/',
         *          realm: 'realm1'
         *      });
         * })
         *
         *  app.controller("MyCtrl", function($scope, $wamp) {
         *
         *      // 1) subscribe to a topic
         *      function onevent(args) {
         *          $scope.hello = args[0];
         *      }
         *      $wamp.subscribe('com.myapp.hello', onevent);
         *
         *      // 2) publish an event
         *      $wamp.publish('com.myapp.hello', ['Hello, world!']);
         *
         *      // 3) register a procedure for remoting
         *      function add2(args) {
         *          return args[0] + args[1];
         *      }
         *      $wamp.register('com.myapp.add2', add2);
         *
         *      // 4) call a remote procedure
         *      $wamp.call('com.myapp.add2', [2, 3]).then(
         *          function (res) {
         *          $scope.add2 = res;
         *      });
         * });
         *
         *
         * @todo write more docs
         */

        var connection;

        var sessionDeferred = $q.defer();
        var sessionPromise = sessionDeferred.promise;

        var onChallengeDeferred = $q.defer();

        /**
         * @param session
         * @param method
         * @param extra
         * @returns {*}
         *
         * @description
         * Gets called when a Challenge Message is sent by the router
         */
        var onchallenge = function (session, method, extra) {

            $rootScope.$broadcast("$wamp.onchallenge", {
                promise: onChallengeDeferred,
                session: session,
                method: method,
                extra: extra
            });

            return onChallengeDeferred.promise;
        };

        /**
         * @param func
         * @returns {Function}
         *
         * @description
         * Wraps a callback with a function that calls scope.$apply(), so that the callback is added to the digest
         */
        function digestWrapper(func) {
            return function () {
                var cb = func.apply(this, arguments);
                $rootScope.$apply();
                return cb;
            };
        }

        options = angular.extend({onchallenge: digestWrapper(onchallenge), use_deferred: $q.defer}, options);

        connection = new autobahn.Connection(options);
        connection.onopen = digestWrapper(function (session) {
            $log.debug("Congrats!  You're connected to the WAMP server!");
            $rootScope.$broadcast("$wamp.open", session);

            sessionDeferred.resolve(session);
        });

        connection.onclose = digestWrapper(function (reason, details) {
            $log.debug("Connection Closed: ", reason);
            $rootScope.$broadcast("$wamp.close", {reason: reason, details: details});

        });

        /**
         * Subscription object which self manages reconnections
         * @param topic
         * @param handler
         * @param options
         * @param subscribedCallback
         * @returns {{}}
         * @constructor
         */
        var Subscription = function (topic, handler, options, subscribedCallback) {

            var subscription = {}, unregister, onOpen, deferred = $q.defer();

            handler = digestWrapper(handler);

            onOpen = function () {
                var p = connection.session.subscribe(topic, handler, options).then(
                    function (s) {
                        subscription = angular.extend(s, subscription);
                        deferred.resolve(subscription);
                        return s;
                    }
                );
                if (subscribedCallback) {
                    subscribedCallback(p);
                }

            };

            if (connection.isOpen) {
                onOpen();
            }

            unregister = $rootScope.$on("$wamp.open", onOpen);

            subscription.promise = deferred.promise;
            subscription.unsubscribe = function () {
                unregister(); //Remove the event listener, so this object can get cleaned up by gc
                return connection.session.unsubscribe(subscription);
            };

            return subscription;
        };

        return {
            connection: connection,
            open: function () {
                connection.open();
            },
            close: function () {
                connection.close();
            },
            subscribe: function (topic, handler, options, subscribedCallback) {
                return Subscription(topic, handler, options, subscribedCallback).promise;
            },
            unsubscribe: function (subscription) {
                return subscription.unsubscribe();
            },
            publish: function (topic, args, kwargs, options) {

                var deferred = $q.defer();

                sessionPromise.then(
                    function(session) {
                        $q.when(session.publish(topic, args, kwargs, options)).then(
                            function(publication) {
                                deferred.resolve(publication);
                            }
                        );
                     }
                );

                return deferred.promise;
            },
            register: function (procedure, endpoint, options) {

                endpoint = digestWrapper(endpoint);

                var deferred = $q.defer();

                sessionPromise.then(
                    function(session) {
                        $q.when(session.register(procedure, endpoint, option)).then(
                            function(registration) {
                                deferred.resolve(registration);
                            }
                        );
                    }
                );

                return deferred.promise;
            },
            call: function (procedure, args, kwargs, options) {

                var deferred = $q.defer();

                sessionPromise.then(
                    function(session) {
                        $q.when(session.call(procedure, args, kwargs, options)).then(
                            function(result) {
                                deferred.resolve(result);
                            }
                        );
                    }
                );

                return deferred.promise;
            }
        };
    };

    return this;

}
<|endoftext|>


<commit_before>requirejs.config({
    waitSeconds: 60
});

require([
  "libs/text", // part of require js
  "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js",
  "libs/ejs_production"
  ],
    function(until)
    {
      require([
      "//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js",
      "libs/jquery.form"]);

      $.getJSON("api/phoxy", function(data)
      {
        phoxy.config = data;
        requirejs.config({baseUrl: phoxy.Config()['js_dir']});

        $('script[phoxy]').each(function()
        {
          phoxy.ApiRequest($(this).attr("phoxy"));
        });
      });
    }
  );

function PhoxyHashChangeCallback()
{
  if (phoxy.ChangeHash(location.hash))
    phoxy.ApiRequest(phoxy.hash);
}

var phoxy =
{
  loaded : false,
  hash : false,
  Load : function( )
    {
      this.loaded = true;
      var hash = location.hash.substring(1);
      phoxy.ApiRequest(hash);
      this.hash = hash;
      $(window).bind('hashchange', PhoxyHashChangeCallback);
    }
  ,
  Defer : function(callback, time)
  {
    if (time == undefined)
      time = 0;
    var th = this;
    setTimeout(function()
    {
      callback.call(th);
    }, time);
  }
  ,
  WaitFor : function(callback_condition, callback, timeout, check_every)
    {
      var
        check_timeout = 60, // 1 minute for render to complete
        check_delay = 500; // check every 500ms

      if (timeout != undefined)
        check_timeout = timeout;
      if (check_every != undefined)
        check_delay = check_every;

      var func = function()
      {
        if (!callback_condition())
          return;
        callback();
      }
      if (callback_condition())
        return func();

      function WaitAndCallCountDown( i )
      {
        if (i <= 0)
          return func();

        phoxy.Defer(function()
        {
          if (callback_condition())
            i = 0;
          WaitAndCallCountDown(i - 1);
        }, check_delay);
      }

      WaitAndCallCountDown(check_timeout * 1000 / check_delay);
    }
  ,
  Appeared : function(jquery_selector, callback, timeout, call_delay)
    {
      function Div()
      {
        return $(phoxy.OptimiseSelector(jquery_selector));
      }
      function IsDivAppeared()
      {
        return Div()[0] != undefined;
      }

      phoxy.Defer(function()
      {
        phoxy.WaitFor(IsDivAppeared, function()
        {
          phoxy.Defer.call(Div(), callback, call_delay);
        }, timeout)
      });
    }
  ,
  Disappeared : function(jquery_selector, callback, timeout, call_delay)
    {
      function IsDivDisappeared()
      {
        return $(phoxy.OptimiseSelector(jquery_selector))[0] == undefined;
      }

      phoxy.Defer(function()
      {
        phoxy.WaitFor(IsDivDisappeared, function()
        {
          phoxy.Defer(callback, call_delay);
        }, timeout);
      });
    }
  ,
  GenerateUniqueID : function()
    {
      var ret = "";
      var dictonary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

      for (var i = 0; i < 10; i++)
        ret += dictonary.charAt(Math.floor(Math.random() * dictonary.length));

      return ret;
    }
  ,
  PrepareCanvas : function(tag)
    {
      if (tag == undefined)
        tag = '
';
      function GetElementCode( el )
      {
        return $(el).wrapAll('

').parent().html();
      }

      var id =  phoxy.GenerateUniqueID();
      var obj = $(tag).attr('id', id);
      var div = GetElementCode(obj);

      return { id: id, obj: obj, html: div };
    }
  ,
  DeferRender : function (ejs, data, rendered_callback, tag)
    {
      console.log("phoxy.DeferRender", arguments);
      if (tag == undefined)
        tag = '<defer_render>';
      var canvas = phoxy.PrepareCanvas(tag);
      var id = canvas.id;

      phoxy.Appeared('#' + id, function()
      {
        phoxy.Fancy(ejs, data, function(html)
        {
          $(this).replaceWith(html);
          rendered_callback(ejs, data);
        });
      });

      return canvas.html;
    }
  ,
  Fancy : function(design, data, callback)
    {
      console.log("phoxy.Fancy", arguments);

      var func;
      var callback = arguments[2];
      if (typeof(callback) == 'undefined')
        callback = function (){};
      var design = arguments[0];

      if (typeof(data) == 'undefined')
      { // single param call
        if (typeof(design) == 'undefined')
          return callback(undefined, undefined);
        if (typeof(design) == 'object')
        {
          var obj = arguments[0];
          // Maybe its wrong. Maybe i should ignore other params
          var design = obj.design;
          var data = obj.data;
          if (typeof(data) == 'undefined')
            data = {};
          delete obj.design;
          delete obj.data;
          delete obj.result;
          delete obj.replace;

          phoxy.ApiAnswer(obj, function()
          {
            phoxy.Fancy(design, data, callback);
          });
        }
        else
        { /* Called as RPC
            PARAMS:
              [0] - string with RPC command
            NOTE:
              target commands ignored.
              result will be rendered in this div.
           */
          var rpc = arguments[0];
          phoxy.AJAX(rpc, function(data, callback)
          {
            phoxy.Fancy(data, undefined, callback);
          }, [callback]);
        }
        return;
      }

      var data = arguments[1];
      function DataLoadedCallback(data)
      {
        if (typeof(data) == 'undefined')
          data = {};
        phoxy.Fancy(design, data, callback);
      }

      if (typeof(data) == 'function')
      { /* Get data as function callback */
        data(DataLoadedCallback);
      }
      else if (typeof(data) == 'string')
      { /* Download data with RPC */
        phoxy.AJAX(data, function(json)
        {
          DataLoadedCallback(json.data);
        });
      }
      else
      { /* Data loaded */
        var html;
        if (design != undefined)
          html = phoxy.Render(phoxy.Config()['ejs_dir'] + "/" + design, undefined, data);
        callback(html, data);
      }
    }
  ,
  ChangeHash : function (hash)
    {
      var t;
      t = hash.split(location.origin)[1];
      if (t !== undefined)
        hash = t;
      var t = hash.split('#')[1];
      if (t !== undefined)
        hash = t;
      var ret = phoxy.hash != hash;
      phoxy.hash = hash;
      location.hash = hash;
      return ret;
    }
  ,
  Reset : function (url)
    {
      if (url == true)
        location.reload();
      var parts = url.split('#');
      if (parts[1] == undefined)
        phoxy.ChangeHash('');
      else
        phoxy.ChangeHash("#" + parts[1]);
      var host = parts[0];
      if (host.length)
        location = host;
      else
        location.reload(parts[0]);
    }
  ,
  Render : function (design, result, data)
    {
      if (data === undefined)
        data = {};

      console.log("phoxy.Render", arguments);
      var html;
      if (design.search(".ejs") == -1)
        design += ".ejs";
      if (!phoxy.ForwardDownload(design))
        html = new EJS({'url' : design}).render(data);
      else
        html = new EJS({'text' : phoxy.ForwardDownload(design, true)}).render(data);
      if (result != undefined && result != '')
        $("#" + result).replaceWith(html);
      return html;
    }
  ,
  ApiAnswer : function( answer, callback )
    {
      if (answer.hash !== undefined)
      {
        if (answer.hash === null)
          answer.hash = "";
        this.ChangeHash(answer.hash);
      }
      if (answer.error)
      {
        alert(answer.error);
        if (answer.reset !== undefined)
          phoxy.Reset(answer.reset);
        return;
      }
      if (answer.reset !== undefined)
        phoxy.Reset(answer.reset);

      function Before()
      {
        function AfterBefore(_answer)
        {
          if (_answer !== undefined)
            answer = _answer;
          phoxy.ScriptsLoaded(answer, callback);
        }
        if (answer.before !== undefined)
          window[answer.before](answer, AfterBefore);
        else
          AfterBefore();
      }

      if (answer.script)
        require(answer.script, Before);
      else
        Before();
    }
  ,
  ScriptsLoaded : function( answer, callback )
    {
      function ScriptsFiresUp()
      {
        if (answer.routeline !== undefined)
          window[answer.routeline](answer);
        if (callback)
          callback(answer.data);
        if (!phoxy.loaded)
          phoxy.Load();
      }
      if (answer.design === undefined)
        return ScriptsFiresUp();

      var canvas = phoxy.PrepareCanvas('<render>');
      var id = canvas.id;
      var render_id = id;

      var element = canvas.obj;

      var url = phoxy.Config()['ejs_dir'] + "/" + answer.design;
      phoxy.ForwardDownload(url + ".ejs", function()
      {
        if (answer.replace === undefined)
          if (answer.result === undefined)
            $('body').append(element);
          else
            $('#' + answer.result).html(element);
        else
          render_id = answer.replace;

        phoxy.Render(
          url,
          render_id,
          answer.data);

        phoxy.Disappeared('#' + id, ScriptsFiresUp);
      });
    }
  ,
  ForwardDownload : function( url, callback_or_true_for_return )
  {
    if (typeof(storage) === "undefined")
      storage = {};

    if (callback_or_true_for_return === true)
      return storage[url];

    function AddToLocalStorage(data)
    {
      storage[url] = data;
      if (typeof(callback_or_true_for_return) == 'function')
        callback_or_true_for_return(data);
    }

    if (storage[url] != undefined)
    {
      if (typeof(callback_or_true_for_return) == 'function')
        callback_or_true_for_return(storage[url]);
      return true;
    }

    $.get(url, AddToLocalStorage);
    return false;
  }
  ,
  AJAX : function( url, callback, params )
    {
      console.log("phoxy.AJAX", arguments);
      $(function()
      {
        $.getJSON(phoxy.Config()['api_dir'] + "/" + url, function(data)
          {
            if (params == undefined)
              params = [];
            params.unshift(data);
            callback.apply(this, params);
          });
      });
    }
  ,
  ApiRequest : function( url, callback )
    {
      if (callback == undefined)
        phoxy.AJAX(url, phoxy.ApiAnswer);
      else
        phoxy.AJAX(url, phoxy.ApiAnswer, [callback]);
    }
  ,
  MenuCall : function( url, callback )
    {
        $(function()
        {
          $.getJSON(phoxy.Config()['api_dir'] + "/" + url, function(data)
          {
            phoxy.ChangeHash(url);
            phoxy.ApiAnswer(data, callback);
          });
        });
    }
  ,
  Config : function()
    {
      return this.config;
    }
  ,
  OptimiseSelector : function( str )
    { // http://learn.jquery.com/performance/optimize-selectors/
      if (typeof(str) != 'string')
        return str;

      var last_id_tag = str.lastIndexOf('#');
      if (last_id_tag > 0)
        str = str.substr(last_id_tag);
      var elements = str.split(" ");
      var ret = $(elements[0]);
      var i = 0;
      while (++i < elements.length)
        if (elements[i].length != 0)
          ret = ret.find(elements[i]);
      return ret;
    }
}
<commit_msg>Fix mistyping issues<commit_after>requirejs.config({
    waitSeconds: 60
});

require([
  "libs/text", // part of require js
  "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js",
  "libs/ejs_production"
  ],
    function(until)
    {
      require([
      "//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js",
      "libs/jquery.form"]);

      $.getJSON("api/phoxy", function(data)
      {
        phoxy.config = data;
        requirejs.config({baseUrl: phoxy.Config()['js_dir']});

        $('script[phoxy]').each(function()
        {
          phoxy.ApiRequest($(this).attr("phoxy"));
        });
      });
    }
  );

function PhoxyHashChangeCallback()
{
  if (phoxy.ChangeHash(location.hash))
    phoxy.ApiRequest(phoxy.hash);
}

var phoxy =
{
  loaded : false,
  hash : false,
  Load : function( )
    {
      this.loaded = true;
      var hash = location.hash.substring(1);
      phoxy.ApiRequest(hash);
      this.hash = hash;
      $(window).bind('hashchange', PhoxyHashChangeCallback);
    }
  ,
  Defer : function(callback, time)
  {
    if (time == undefined)
      time = 0;
    var th = this;
    setTimeout(function()
    {
      callback.call(th);
    }, time);
  }
  ,
  WaitFor : function(callback_condition, callback, timeout, check_every)
    {
      var
        check_timeout = 60, // 1 minute for render to complete
        check_delay = 500; // check every 500ms

      if (timeout != undefined)
        check_timeout = timeout;
      if (check_every != undefined)
        check_delay = check_every;

      var func = function()
      {
        if (!callback_condition())
          return;
        callback();
      }
      if (callback_condition())
        return func();

      function WaitAndCallCountDown( i )
      {
        if (i <= 0)
          return func();

        phoxy.Defer(function()
        {
          if (callback_condition())
            i = 0;
          WaitAndCallCountDown(i - 1);
        }, check_delay);
      }

      WaitAndCallCountDown(check_timeout * 1000 / check_delay);
    }
  ,
  Appeared : function(jquery_selector, callback, timeout, call_delay)
    {
      function Div()
      {
        return $(phoxy.OptimiseSelector(jquery_selector));
      }
      function IsDivAppeared()
      {
        return Div()[0] != undefined;
      }

      phoxy.Defer(function()
      {
        phoxy.WaitFor(IsDivAppeared, function()
        {
          phoxy.Defer.call(Div(), callback, call_delay);
        }, timeout)
      });
    }
  ,
  Disappeared : function(jquery_selector, callback, timeout, call_delay)
    {
      function IsDivDisappeared()
      {
        return $(phoxy.OptimiseSelector(jquery_selector))[0] == undefined;
      }

      phoxy.Defer(function()
      {
        phoxy.WaitFor(IsDivDisappeared, function()
        {
          phoxy.Defer(callback, call_delay);
        }, timeout);
      });
    }
  ,
  GenerateUniqueID : function()
    {
      var ret = "";
      var dictonary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

      for (var i = 0; i < 10; i++)
        ret += dictonary.charAt(Math.floor(Math.random() * dictonary.length));

      return ret;
    }
  ,
  PrepareCanvas : function(tag)
    {
      if (tag == undefined)
        tag = '
';
      function GetElementCode( el )
      {
        return $(el).wrapAll('

').parent().html();
      }

      var id =  phoxy.GenerateUniqueID();
      var obj = $(tag).attr('id', id);
      var div = GetElementCode(obj);

      return { id: id, obj: obj, html: div };
    }
  ,
  DeferRender : function (ejs, data, rendered_callback, tag)
    {
      console.log("phoxy.DeferRender", arguments);
      if (tag == undefined)
        tag = '<defer_render>';
      var canvas = phoxy.PrepareCanvas(tag);
      var id = canvas.id;

      phoxy.Appeared('#' + id, function()
      {
        phoxy.Fancy(ejs, data, function(html)
        {
          $('#' + id).replaceWith(html);
          if (typeof(rendered_callback) != 'undefined')
            rendered_callback(ejs, data);
        });
      });

      return canvas.html;
    }
  ,
  Fancy : function(design, data, callback)
    {
      console.log("phoxy.Fancy", arguments);

      var func;
      var callback = arguments[2];
      if (typeof(callback) == 'undefined')
        callback = function (){};
      var design = arguments[0];

      if (typeof(data) == 'undefined')
      { // single param call
        if (typeof(design) == 'undefined')
          return callback(undefined, undefined, undefined);
        if (typeof(design) == 'object')
        {
          var obj = arguments[0];
          // Maybe its wrong. Maybe i should ignore other params
          var design = obj.design;
          var data = obj.data;
          if (typeof(data) == 'undefined')
            data = {};
          delete obj.design;
          delete obj.data;
          delete obj.result;
          delete obj.replace;

          phoxy.ApiAnswer(obj, function()
          {
            phoxy.Fancy(design, data, callback);
          });
        }
        else
        { /* Called as RPC
            PARAMS:
              [0] - string with RPC command
            NOTE:
              target commands ignored.
              result will be rendered in this div.
           */
          var rpc = arguments[0];
          phoxy.AJAX(rpc, function(data, callback)
          {
            phoxy.Fancy(data, undefined, callback);
          }, [callback]);
        }
        return;
      }

      var data = arguments[1];
      function DataLoadedCallback(data)
      {
        if (typeof(data) == 'undefined')
          data = {};
        phoxy.Fancy(design, data, callback);
      }

      if (typeof(data) == 'function')
      { /* Get data as function callback */
        data(DataLoadedCallback);
      }
      else if (typeof(data) == 'string')
      { /* Download data with RPC */
        phoxy.AJAX(data, function(json)
        {
          DataLoadedCallback(json.data);
        });
      }
      else
      { /* Data loaded */
        var html;
        if (design != undefined)
          html = phoxy.Render(phoxy.Config()['ejs_dir'] + "/" + design, undefined, data);
        callback(html, design, data);
      }
    }
  ,
  ChangeHash : function (hash)
    {
      var t;
      t = hash.split(location.origin)[1];
      if (t !== undefined)
        hash = t;
      var t = hash.split('#')[1];
      if (t !== undefined)
        hash = t;
      var ret = phoxy.hash != hash;
      phoxy.hash = hash;
      location.hash = hash;
      return ret;
    }
  ,
  Reset : function (url)
    {
      if (url == true)
        location.reload();
      var parts = url.split('#');
      if (parts[1] == undefined)
        phoxy.ChangeHash('');
      else
        phoxy.ChangeHash("#" + parts[1]);
      var host = parts[0];
      if (host.length)
        location = host;
      else
        location.reload(parts[0]);
    }
  ,
  Render : function (design, result, data)
    {
      if (data === undefined)
        data = {};

      console.log("phoxy.Render", arguments);
      var html;
      if (design.search(".ejs") == -1)
        design += ".ejs";
      if (!phoxy.ForwardDownload(design))
        html = new EJS({'url' : design}).render(data);
      else
        html = new EJS({'text' : phoxy.ForwardDownload(design, true)}).render(data);
      if (result != undefined && result != '')
        $("#" + result).replaceWith(html);
      return html;
    }
  ,
  ApiAnswer : function( answer, callback )
    {
      if (answer.hash !== undefined)
      {
        if (answer.hash === null)
          answer.hash = "";
        this.ChangeHash(answer.hash);
      }
      if (answer.error)
      {
        alert(answer.error);
        if (answer.reset !== undefined)
          phoxy.Reset(answer.reset);
        return;
      }
      if (answer.reset !== undefined)
        phoxy.Reset(answer.reset);

      function Before()
      {
        function AfterBefore(_answer)
        {
          if (_answer !== undefined)
            answer = _answer;
          phoxy.ScriptsLoaded(answer, callback);
        }
        if (answer.before !== undefined)
          window[answer.before](answer, AfterBefore);
        else
          AfterBefore();
      }

      if (answer.script)
        require(answer.script, Before);
      else
        Before();
    }
  ,
  ScriptsLoaded : function( answer, callback )
    {
      function ScriptsFiresUp()
      {
        if (answer.routeline !== undefined)
          window[answer.routeline](answer);
        if (callback)
          callback(answer.data);
        if (!phoxy.loaded)
          phoxy.Load();
      }
      if (answer.design === undefined)
        return ScriptsFiresUp();

      var canvas = phoxy.PrepareCanvas('<render>');
      var id = canvas.id;
      var render_id = id;

      var element = canvas.obj;

      var url = phoxy.Config()['ejs_dir'] + "/" + answer.design;
      phoxy.ForwardDownload(url + ".ejs", function()
      {
        if (answer.replace === undefined)
          if (answer.result === undefined)
            $('body').append(element);
          else
            $('#' + answer.result).html(element);
        else
          render_id = answer.replace;

        phoxy.Render(
          url,
          render_id,
          answer.data);

        phoxy.Disappeared('#' + id, ScriptsFiresUp);
      });
    }
  ,
  ForwardDownload : function( url, callback_or_true_for_return )
  {
    if (typeof(storage) === "undefined")
      storage = {};

    if (callback_or_true_for_return === true)
      return storage[url];

    function AddToLocalStorage(data)
    {
      storage[url] = data;
      if (typeof(callback_or_true_for_return) == 'function')
        callback_or_true_for_return(data);
    }

    if (storage[url] != undefined)
    {
      if (typeof(callback_or_true_for_return) == 'function')
        callback_or_true_for_return(storage[url]);
      return true;
    }

    $.get(url, AddToLocalStorage);
    return false;
  }
  ,
  AJAX : function( url, callback, params )
    {
      console.log("phoxy.AJAX", arguments);
      $(function()
      {
        $.getJSON(phoxy.Config()['api_dir'] + "/" + url, function(data)
          {
            if (params == undefined)
              params = [];
            params.unshift(data);
            callback.apply(this, params);
          });
      });
    }
  ,
  ApiRequest : function( url, callback )
    {
      if (callback == undefined)
        phoxy.AJAX(url, phoxy.ApiAnswer);
      else
        phoxy.AJAX(url, phoxy.ApiAnswer, [callback]);
    }
  ,
  MenuCall : function( url, callback )
    {
        $(function()
        {
          $.getJSON(phoxy.Config()['api_dir'] + "/" + url, function(data)
          {
            phoxy.ChangeHash(url);
            phoxy.ApiAnswer(data, callback);
          });
        });
    }
  ,
  Config : function()
    {
      return this.config;
    }
  ,
  OptimiseSelector : function( str )
    { // http://learn.jquery.com/performance/optimize-selectors/
      if (typeof(str) != 'string')
        return str;

      var last_id_tag = str.lastIndexOf('#');
      if (last_id_tag > 0)
        str = str.substr(last_id_tag);
      var elements = str.split(" ");
      var ret = $(elements[0]);
      var i = 0;
      while (++i < elements.length)
        if (elements[i].length != 0)
          ret = ret.find(elements[i]);
      return ret;
    }
}
<|endoftext|>


<commit_before>const entities = require('entities'),
  STYLES = {
    'ef0': 'color:#000',
    'ef1': 'color:#A00',
    'ef2': 'color:#0A0',
    'ef3': 'color:#A50',
    'ef4': 'color:#00A',
    'ef5': 'color:#A0A',
    'ef6': 'color:#0AA',
    'ef7': 'color:#AAA',
    'ef8': 'color:#555',
    'ef9': 'color:#F55',
    'ef10': 'color:#5F5',
    'ef11': 'color:#FF5',
    'ef12': 'color:#55F',
    'ef13': 'color:#F5F',
    'ef14': 'color:#5FF',
    'ef15': 'color:#FFF',
    'eb0': 'background-color:#000',
    'eb1': 'background-color:#A00',
    'eb2': 'background-color:#0A0',
    'eb3': 'background-color:#A50',
    'eb4': 'background-color:#00A',
    'eb5': 'background-color:#A0A',
    'eb6': 'background-color:#0AA',
    'eb7': 'background-color:#AAA',
    'eb8': 'background-color:#555',
    'eb9': 'background-color:#F55',
    'eb10': 'background-color:#5F5',
    'eb11': 'background-color:#FF5',
    'eb12': 'background-color:#55F',
    'eb13': 'background-color:#F5F',
    'eb14': 'background-color:#5FF',
    'eb15': 'background-color:#FFF'
  },
  defaults = {
    fg: '#FFF',
    bg: '#000',
    newline: false,
    escapeXML: false,
    stream: false
  };

/**
 * Converts from a number like 15 to a hex string like 'F'
 * @param {number} num
 * @returns {string}
 */
function toHexString(num) {
  num = num.toString(16);
  while (num.length < 2) {
    num = '0' + num;
  }
  return num;
}

/**
 * Converts from an array of numbers like [15, 15, 15] to a hex string like 'FFF'
 * @param {[red, green, blue]} ref
 * @returns {string}
 */
function toColorHexString(ref) {
  let results = [];

  for (let j = 0, len = ref.length; j < len; j++) {
    results.push(toHexString(ref[j]));
  }

  return results.join('');
}

/**
 * Creates an array of numbers ranging from low to high
 * @param {number} low
 * @param {number} high
 * @returns {Array}
 * @example range(3, 7); // creates [3, 4, 5, 6, 7]
 */
function range(low, high) {
  const results = [];

  for (let j = low; j <= high; j++) {
    results.push(j);
  }

  return results;
}

/**
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 */
function setStyleColor(red, green, blue) {
  const c = 16 + (red * 36) + (green * 6) + blue,
    r = red > 0 ? red * 40 + 55 : 0,
    g = green > 0 ? green * 40 + 55 : 0,
    b = blue > 0 ? blue * 40 + 55 : 0,
    rgb = toColorHexString([r, g, b]);

  STYLES['ef' + c] = 'color:#' + rgb;
  STYLES['eb' + c] = 'background-color:#' + rgb;
}

// fill in standard colors
range(0, 5).forEach(red => {
  range(0, 5).forEach(green => {
    range(0, 5).forEach(blue => setStyleColor(red, green, blue));
  });
});

range(0, 23).forEach(function (gray) {
  const c = gray + 232,
    l = toHexString(gray * 10 + 8);

  STYLES['ef' + c] = 'color:#' + l + l + l;
  STYLES['eb' + c] = 'background-color:#' + l + l + l;
});

/**
 * Returns a new function that is true if value is NOT the same category
 * @param {string} category
 * @returns {function}
 */
function notCategory(category) {
  return function (e) {
    return (category === null || e.category !== category) && category !== 'all';
  };
}

/**
 * Converts a code into an ansi token type
 * @param {number} code
 * @returns {string}
 */
function categoryForCode(code) {
  code = parseInt(code, 10);
  if (code === 0) {
    return 'all';
  } else if (code === 1) {
    return 'bold';
  } else if ((2 < code && code < 5)) {
    return 'underline';
  } else if ((4 < code && code < 7)) {
    return 'blink';
  } else if (code === 8) {
    return 'hide';
  } else if (code === 9) {
    return 'strike';
  } else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) {
    return 'foreground-color';
  } else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) {
    return 'background-color';
  } else {
    return null;
  }
}

/**
 * @param {string} text
 * @param {object} options
 * @returns {string}
 */
function pushText(text, options) {
  if (options.escapeXML) {
    return entities.encodeXML(text);
  } else {
    return text;
  }
}

/**
 * @param {Array} stack
 * @param {string} tag
 * @param {string} [style='']
 * @returns {string}
 */
function pushTag(stack, tag, style) {
  if (!style) {
    style = '';
  }

  if (style.length && style.indexOf(':') === -1) {
    style = STYLES[style];
  }

  stack.push(tag);

  return ['<' + tag, (style ? ' style="' + style + '"' : void 0), '>'].join('');
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function pushStyle (stack, style) {
  return pushTag(stack, 'span', style);
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function closeTag(stack, style) {
  let last;

  if (stack.slice(-1)[0] === style) {
    last = stack.pop();
  }

  if (last) {
    return '</' + style + '>';
  }
}

/**
 * @param {string} text
 * @param {object} options
 * @param {function} callback
 * @returns {Array}
 */
function tokenize(text, options, callback) {
  let ansiMatch = false,
    ansiHandler = 3;

  function remove() {
    return '';
  }

  function removeXterm256(m, g1) {
    callback('xterm256', g1);
    return '';
  }

  function newline(m) {
    if (options.newline) {
      callback('display', -1);
    } else {
      callback('text', m);
    }

    return '';
  }

  function ansiMess(m, g1) {
    ansiMatch = true;
    if (g1.trim().length === 0) {
      g1 = '0';
    }

    g1 = g1.trimRight(';').split(';');

    for (let o = 0, len = g1.length; o < len; o++) {
      callback('display', g1[o]);
    }

    return '';
  }

  function realText(m) {
    callback('text', m);

    return '';
  }

  /* eslint no-control-regex:0 */
  const tokens = [
    {
      pattern: /^\x08+/,
      sub: remove
    }, {
      pattern: /^\x1b\[[012]?K/,
      sub: remove
    }, {
      pattern: /^\x1b\[38;5;(\d+)m/,
      sub: removeXterm256
    }, {
      pattern: /^\n/,
      sub: newline
    }, {
      pattern: /^\x1b\[((?:\d{1,3};?)+|)m/,
      sub: ansiMess
    }, {
      pattern: /^\x1b\[?[\d;]{0,3}/,
      sub: remove
    }, {
      pattern: /^([^\x1b\x08\n]+)/,
      sub: realText
    }
  ];

  function process(handler, i) {
    if (i > ansiHandler && ansiMatch) {
      return;
    }

    ansiMatch = false;

    text = text.replace(handler.pattern, handler.sub);
  }

  let handler,
    results1 = [],
    length = text.length;
  while (length > 0) {
    for (let i = 0, o = 0, len = tokens.length; o < len; i = ++o) {
      handler = tokens[i];
      process(handler, i);
    }

    if (text.length === length) {
      break;
    } else {
      results1.push(0);
    }

    length = text.length;
  }

  return results1;
}

/**
 * If streaming, then the stack is "sticky"
 *
 * @param {Array} stickyStack
 * @param {string} token
 * @param {*} data
 * @returns {Array}
 */
function updateStickyStack(stickyStack, token, data) {
  if (token !== 'text') {
    stickyStack = stickyStack.filter(notCategory(categoryForCode(data)));
    stickyStack.push({token: token, data: data, category: categoryForCode(data)});
  }

  return stickyStack;
}

const Filter = (function () {
  function Filter(options) {
    options = options || {};

    this.opts = Object.assign({}, defaults, options);
    this.input = [];
    this.stack = [];
    this.stickyStack = [];
  }

  Filter.prototype = {
    toHtml (input) {
      this.input = typeof input === 'string' ? [input] : input;
      let buf = [];
      this.stickyStack.forEach((element) => {
        let output = this.generateOutput(element.token, element.data);

        if (output) {
          buf.push(output);
        }
      });
      this.forEach(function (chunk) {
        return buf.push(chunk);
      });

      this.input = [];
      return buf.join('');
    },

    forEach (callback) {
      const options = this.opts,
        shouldStream = this.opts.stream,
        input = this.input;
      let buf = '';

      input.forEach(chunk => {
        buf += chunk;

        return tokenize(buf, options, (token, data) => {
          let output = this.generateOutput(token, data);

          if (output) {
            callback(output);
          }

          if (shouldStream) {
            this.stickyStack = updateStickyStack(this.stickyStack, token, data);
          }
        });
      });

      if (this.stack.length) {
        return callback(this.resetStyles());
      }
    },

    /**
     * @param token
     * @param data
     */
    generateOutput (token, data) {
      const options = this.opts,
        stack = this.stack;
      let result;

      if (token === 'text') {
        result = pushText(data, options);
      } else if (token === 'display') {
        result = this.handleDisplay(data);
      } else if (token === 'xterm256') {
        result = pushStyle(stack, 'ef' + data);
      }

      return result;
    },

    /**
     *
     * @param {number} code
     * @returns {*}
     */
    handleDisplay (code) {
      code = parseInt(code, 10);
      let result;
      const stack = this.stack,
        codeMap = {
          '-1': () => '
',
          0: () => this.stack.length && this.resetStyles(),
          1: () => pushTag(stack, 'b'),
          3: () => pushTag(stack, 'i'),
          4: () => pushTag(stack, 'u'),
          8: () => pushStyle(stack, 'display:none'),
          9: () => pushTag(stack, 'strike'),
          22: () => closeTag(stack, 'b'),
          23: () => closeTag(stack, 'i'),
          24: () => closeTag(stack, 'u'),
          39: () =>  pushStyle(stack, 'color:' + this.opts.fg),
          49: () => pushStyle(stack, 'background-color:' + this.opts.bg)
        };

      if (codeMap[code]) {
        result = codeMap[code]();
      } else if (4 < code && code < 7) {
        result = pushTag(stack, 'blink');
      } else if (29 < code && code < 38) {
        result = pushStyle(stack, 'ef' + (code - 30));
      } else if ((39 < code && code < 48)) {
        result = pushStyle(stack, 'eb' + (code - 40));
      } else if ((89 < code && code < 98)) {
        result = pushStyle(stack, 'ef' + (8 + (code - 90)));
      } else if ((99 < code && code < 108)) {
        result = pushStyle(stack, 'eb' + (8 + (code - 100)));
      }

      return result;
    },

    /**
     * Clear all the styles
     * @returns {string}
     */
    resetStyles () {
      let ref = [this.stack, []],
        stack = ref[0];

      this.stack = ref[1];

      return stack.reverse().map(function (tag) {
        return '</' + tag + '>';
      }).join('');
    }
  };

  return Filter;
})();

module.exports = Filter;
<commit_msg>remove this.input<commit_after>const entities = require('entities'),
  STYLES = {
    'ef0': 'color:#000',
    'ef1': 'color:#A00',
    'ef2': 'color:#0A0',
    'ef3': 'color:#A50',
    'ef4': 'color:#00A',
    'ef5': 'color:#A0A',
    'ef6': 'color:#0AA',
    'ef7': 'color:#AAA',
    'ef8': 'color:#555',
    'ef9': 'color:#F55',
    'ef10': 'color:#5F5',
    'ef11': 'color:#FF5',
    'ef12': 'color:#55F',
    'ef13': 'color:#F5F',
    'ef14': 'color:#5FF',
    'ef15': 'color:#FFF',
    'eb0': 'background-color:#000',
    'eb1': 'background-color:#A00',
    'eb2': 'background-color:#0A0',
    'eb3': 'background-color:#A50',
    'eb4': 'background-color:#00A',
    'eb5': 'background-color:#A0A',
    'eb6': 'background-color:#0AA',
    'eb7': 'background-color:#AAA',
    'eb8': 'background-color:#555',
    'eb9': 'background-color:#F55',
    'eb10': 'background-color:#5F5',
    'eb11': 'background-color:#FF5',
    'eb12': 'background-color:#55F',
    'eb13': 'background-color:#F5F',
    'eb14': 'background-color:#5FF',
    'eb15': 'background-color:#FFF'
  },
  defaults = {
    fg: '#FFF',
    bg: '#000',
    newline: false,
    escapeXML: false,
    stream: false
  };

/**
 * Converts from a number like 15 to a hex string like 'F'
 * @param {number} num
 * @returns {string}
 */
function toHexString(num) {
  num = num.toString(16);
  while (num.length < 2) {
    num = '0' + num;
  }
  return num;
}

/**
 * Converts from an array of numbers like [15, 15, 15] to a hex string like 'FFF'
 * @param {[red, green, blue]} ref
 * @returns {string}
 */
function toColorHexString(ref) {
  let results = [];

  for (let j = 0, len = ref.length; j < len; j++) {
    results.push(toHexString(ref[j]));
  }

  return results.join('');
}

/**
 * Creates an array of numbers ranging from low to high
 * @param {number} low
 * @param {number} high
 * @returns {Array}
 * @example range(3, 7); // creates [3, 4, 5, 6, 7]
 */
function range(low, high) {
  const results = [];

  for (let j = low; j <= high; j++) {
    results.push(j);
  }

  return results;
}

/**
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 */
function setStyleColor(red, green, blue) {
  const c = 16 + (red * 36) + (green * 6) + blue,
    r = red > 0 ? red * 40 + 55 : 0,
    g = green > 0 ? green * 40 + 55 : 0,
    b = blue > 0 ? blue * 40 + 55 : 0,
    rgb = toColorHexString([r, g, b]);

  STYLES['ef' + c] = 'color:#' + rgb;
  STYLES['eb' + c] = 'background-color:#' + rgb;
}

// fill in standard colors
range(0, 5).forEach(red => {
  range(0, 5).forEach(green => {
    range(0, 5).forEach(blue => setStyleColor(red, green, blue));
  });
});

range(0, 23).forEach(function (gray) {
  const c = gray + 232,
    l = toHexString(gray * 10 + 8);

  STYLES['ef' + c] = 'color:#' + l + l + l;
  STYLES['eb' + c] = 'background-color:#' + l + l + l;
});

/**
 * Returns a new function that is true if value is NOT the same category
 * @param {string} category
 * @returns {function}
 */
function notCategory(category) {
  return function (e) {
    return (category === null || e.category !== category) && category !== 'all';
  };
}

/**
 * Converts a code into an ansi token type
 * @param {number} code
 * @returns {string}
 */
function categoryForCode(code) {
  code = parseInt(code, 10);
  if (code === 0) {
    return 'all';
  } else if (code === 1) {
    return 'bold';
  } else if ((2 < code && code < 5)) {
    return 'underline';
  } else if ((4 < code && code < 7)) {
    return 'blink';
  } else if (code === 8) {
    return 'hide';
  } else if (code === 9) {
    return 'strike';
  } else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) {
    return 'foreground-color';
  } else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) {
    return 'background-color';
  } else {
    return null;
  }
}

/**
 * @param {string} text
 * @param {object} options
 * @returns {string}
 */
function pushText(text, options) {
  if (options.escapeXML) {
    return entities.encodeXML(text);
  } else {
    return text;
  }
}

/**
 * @param {Array} stack
 * @param {string} tag
 * @param {string} [style='']
 * @returns {string}
 */
function pushTag(stack, tag, style) {
  if (!style) {
    style = '';
  }

  if (style.length && style.indexOf(':') === -1) {
    style = STYLES[style];
  }

  stack.push(tag);

  return ['<' + tag, (style ? ' style="' + style + '"' : void 0), '>'].join('');
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function pushStyle (stack, style) {
  return pushTag(stack, 'span', style);
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function closeTag(stack, style) {
  let last;

  if (stack.slice(-1)[0] === style) {
    last = stack.pop();
  }

  if (last) {
    return '</' + style + '>';
  }
}

/**
 * @param {string} text
 * @param {object} options
 * @param {function} callback
 * @returns {Array}
 */
function tokenize(text, options, callback) {
  let ansiMatch = false,
    ansiHandler = 3;

  function remove() {
    return '';
  }

  function removeXterm256(m, g1) {
    callback('xterm256', g1);
    return '';
  }

  function newline(m) {
    if (options.newline) {
      callback('display', -1);
    } else {
      callback('text', m);
    }

    return '';
  }

  function ansiMess(m, g1) {
    ansiMatch = true;
    if (g1.trim().length === 0) {
      g1 = '0';
    }

    g1 = g1.trimRight(';').split(';');

    for (let o = 0, len = g1.length; o < len; o++) {
      callback('display', g1[o]);
    }

    return '';
  }

  function realText(m) {
    callback('text', m);

    return '';
  }

  /* eslint no-control-regex:0 */
  const tokens = [
    {
      pattern: /^\x08+/,
      sub: remove
    }, {
      pattern: /^\x1b\[[012]?K/,
      sub: remove
    }, {
      pattern: /^\x1b\[38;5;(\d+)m/,
      sub: removeXterm256
    }, {
      pattern: /^\n/,
      sub: newline
    }, {
      pattern: /^\x1b\[((?:\d{1,3};?)+|)m/,
      sub: ansiMess
    }, {
      pattern: /^\x1b\[?[\d;]{0,3}/,
      sub: remove
    }, {
      pattern: /^([^\x1b\x08\n]+)/,
      sub: realText
    }
  ];

  function process(handler, i) {
    if (i > ansiHandler && ansiMatch) {
      return;
    }

    ansiMatch = false;

    text = text.replace(handler.pattern, handler.sub);
  }

  let handler,
    results1 = [],
    length = text.length;
  while (length > 0) {
    for (let i = 0, o = 0, len = tokens.length; o < len; i = ++o) {
      handler = tokens[i];
      process(handler, i);
    }

    if (text.length === length) {
      break;
    } else {
      results1.push(0);
    }

    length = text.length;
  }

  return results1;
}

/**
 * If streaming, then the stack is "sticky"
 *
 * @param {Array} stickyStack
 * @param {string} token
 * @param {*} data
 * @returns {Array}
 */
function updateStickyStack(stickyStack, token, data) {
  if (token !== 'text') {
    stickyStack = stickyStack.filter(notCategory(categoryForCode(data)));
    stickyStack.push({token: token, data: data, category: categoryForCode(data)});
  }

  return stickyStack;
}

const Filter = (function () {
  function Filter(options) {
    options = options || {};

    this.opts = Object.assign({}, defaults, options);
    this.stack = [];
    this.stickyStack = [];
  }

  Filter.prototype = {
    toHtml (input) {
      input = typeof input === 'string' ? [input] : input;
      const options = this.opts,
        buf = [];

      this.stickyStack.forEach(element => {
        let output = this.generateOutput(element.token, element.data);

        if (output) {
          buf.push(output);
        }
      });

      this.forEach(input, options, function (chunk) {
        return buf.push(chunk);
      });

      return buf.join('');
    },

    forEach (input, options, callback) {
      let buf = '';

      input.forEach(chunk => {
        buf += chunk;

        return tokenize(buf, options, (token, data) => {
          let output = this.generateOutput(token, data);

          if (output) {
            callback(output);
          }

          if (options.stream) {
            this.stickyStack = updateStickyStack(this.stickyStack, token, data);
          }
        });
      });

      if (this.stack.length) {
        return callback(this.resetStyles());
      }
    },

    /**
     * @param token
     * @param data
     */
    generateOutput (token, data) {
      const options = this.opts,
        stack = this.stack;
      let result;

      if (token === 'text') {
        result = pushText(data, options);
      } else if (token === 'display') {
        result = this.handleDisplay(data);
      } else if (token === 'xterm256') {
        result = pushStyle(stack, 'ef' + data);
      }

      return result;
    },

    /**
     *
     * @param {number} code
     * @returns {*}
     */
    handleDisplay (code) {
      code = parseInt(code, 10);
      let result;
      const stack = this.stack,
        codeMap = {
          '-1': () => '
',
          0: () => this.stack.length && this.resetStyles(),
          1: () => pushTag(stack, 'b'),
          3: () => pushTag(stack, 'i'),
          4: () => pushTag(stack, 'u'),
          8: () => pushStyle(stack, 'display:none'),
          9: () => pushTag(stack, 'strike'),
          22: () => closeTag(stack, 'b'),
          23: () => closeTag(stack, 'i'),
          24: () => closeTag(stack, 'u'),
          39: () =>  pushStyle(stack, 'color:' + this.opts.fg),
          49: () => pushStyle(stack, 'background-color:' + this.opts.bg)
        };

      if (codeMap[code]) {
        result = codeMap[code]();
      } else if (4 < code && code < 7) {
        result = pushTag(stack, 'blink');
      } else if (29 < code && code < 38) {
        result = pushStyle(stack, 'ef' + (code - 30));
      } else if ((39 < code && code < 48)) {
        result = pushStyle(stack, 'eb' + (code - 40));
      } else if ((89 < code && code < 98)) {
        result = pushStyle(stack, 'ef' + (8 + (code - 90)));
      } else if ((99 < code && code < 108)) {
        result = pushStyle(stack, 'eb' + (8 + (code - 100)));
      }

      return result;
    },

    /**
     * Clear all the styles
     * @returns {string}
     */
    resetStyles () {
      let ref = [this.stack, []],
        stack = ref[0];

      this.stack = ref[1];

      return stack.reverse().map(function (tag) {
        return '</' + tag + '>';
      }).join('');
    }
  };

  return Filter;
})();

module.exports = Filter;
<|endoftext|>


<commit_before>var querystring = require('querystring');
var http = require('http');
var fs = require('fs');
var elasticsearch = require('elasticsearch');
var logger = require('../utils/logger');
var request = require('request');
var stompit = require('stompit');
var nodemailer = require('nodemailer');

var m = {}

var smtp_host = process.env.FG_SMTP_HOST;
var smtp_port = process.env.FG_SMTP_PORT;
var to = process.env.FG_MAILING_LIST;
var from = process.env.FG_MAILING_FROM;

var esClient = new elasticsearch.Client({
    host: (process.env.FK_ES_HOST || 'localhost') + ":" + (process.env.FK_ES_PORT || 9200)
});

var smtp_config = {
    host: smtp_host || 'localhost',
    port: smtp_port || 465,
    auth: {
        user: null,
        pass: null
    }
}

var transporter = nodemailer.createTransport(smtp_config);

m.processReplayStatusEmail = function () {
    if (process.env.FG_MAILING_LIST && process.env.FG_MAILING_FROM) {
        var index = 'forklift-replay*';
        esClient.search({
            index: index,
            size: 5000,
            body: {
                query: {
                    query_string: {
                        query: "Error",
                        fields: ["step"]
                    }
                },
                "sort": [{
                    "time": {"order": "desc"}
                }]
            }
        }).then(function (resp) {
            var message = "
+-- Todays Summary --+
" +
                "
" +
                "There are currently " + resp.hits.hits.length + " items in the forklift replay queue that need attention!" +
                "
";
            var queueMap = {};

            resp.hits.hits.forEach(function (hit) {
                var source = hit._source;
                if (queueMap[source.queue]) {
                    // increase a currently added queue items size
                    var size = parseInt(queueMap[source.queue]);
                    size++;
                    queueMap[source.queue] = size;
                } else {
                    // add the queue item to the map
                    queueMap[source.queue] = 1;
                }
            });
            for (var q in queueMap) {
                message += "" + q + ": " + queueMap[q] + "
";
            }
            logger.info("sending forklift replay status email now");
            sendReplayStatusEmail(message);
        }, function (err) {
            logger.error(err.message);
        });
    } else {
        logger.error("Unable to send out replay status emails due to undefined mailing parameters");
    }
}

var sendReplayStatusEmail = function (message) {
    var mailOptions = {
        to: to,
        from: from,
        subject: "Forklift Replay - Daily Summary",
        html: message,
    }
    transporter.sendMail(mailOptions, function(error, info){
       if (error) {
           return logger.error(error);
       }
       logger.info("Daily Summary Sent! " + info.response);
    });
}

module.exports = m;
<commit_msg>added smtp user and pass vars<commit_after>var querystring = require('querystring');
var http = require('http');
var fs = require('fs');
var elasticsearch = require('elasticsearch');
var logger = require('../utils/logger');
var request = require('request');
var stompit = require('stompit');
var nodemailer = require('nodemailer');

var m = {}

var smtp_host = process.env.FG_SMTP_HOST;
var smtp_port = process.env.FG_SMTP_PORT;
var smtp_user = process.env.FG_SMTP_USER;
var smtp_pass = process.env.FG_SMTP_PASS;

var to = process.env.FG_MAILING_LIST;
var from = process.env.FG_MAILING_FROM;

var esClient = new elasticsearch.Client({
    host: (process.env.FK_ES_HOST || 'localhost') + ":" + (process.env.FK_ES_PORT || 9200)
});

var smtp_config = {
    host: smtp_host || 'localhost',
    port: smtp_port || 465,
    auth: {
        user: smtp_user,
        pass: smtp_pass
    }
}

var transporter = nodemailer.createTransport(smtp_config);

m.processReplayStatusEmail = function () {
    if (process.env.FG_MAILING_LIST && process.env.FG_MAILING_FROM) {
        var index = 'forklift-replay*';
        esClient.search({
            index: index,
            size: 5000,
            body: {
                query: {
                    query_string: {
                        query: "Error",
                        fields: ["step"]
                    }
                },
                "sort": [{
                    "time": {"order": "desc"}
                }]
            }
        }).then(function (resp) {
            var message = "
+-- Todays Summary --+
" +
                "
" +
                "There are currently " + resp.hits.hits.length + " items in the forklift replay queue that need attention!" +
                "
";
            var queueMap = {};

            resp.hits.hits.forEach(function (hit) {
                var source = hit._source;
                if (queueMap[source.queue]) {
                    // increase a currently added queue items size
                    var size = parseInt(queueMap[source.queue]);
                    size++;
                    queueMap[source.queue] = size;
                } else {
                    // add the queue item to the map
                    queueMap[source.queue] = 1;
                }
            });
            for (var q in queueMap) {
                message += "" + q + ": " + queueMap[q] + "
";
            }
            logger.info("sending forklift replay status email now");
            sendReplayStatusEmail(message);
        }, function (err) {
            logger.error(err.message);
        });
    } else {
        logger.error("Unable to send out replay status emails due to undefined mailing parameters");
    }
}

var sendReplayStatusEmail = function (message) {
    var mailOptions = {
        to: to,
        from: from,
        subject: "Forklift Replay - Daily Summary",
        html: message,
    }
    transporter.sendMail(mailOptions, function(error, info){
       if (error) {
           return logger.error(error);
       }
       logger.info("Daily Summary Sent! " + info.response);
    });
}

module.exports = m;
<|endoftext|>


<commit_before>/* eslint-env jest */

beforeEach(() => {
  delete window.googletag
  delete window.tabforacause

  // Mock googletag
  const mockAddEventListener = jest.fn()
  window.googletag = {
    cmd: [],
    pubads: () => ({
      addEventListener: mockAddEventListener
    })
  }

  // Mock tabforacause global
  window.tabforacause = {
    ads: {
      slotsLoaded: {},
      slotsAlreadyLoggedRevenue: {}
    }
  }

  jest.clearAllMocks()
  jest.resetModules()
})

afterAll(() => {
  delete window.googletag
  delete window.tabforacause
})

describe('handleAdsLoaded', function () {
  it('adds a slot ID to window.tabforacause\'s "loaded slots" object when GPT\'s "slotOnload" event is fired', () => {
    // Mock GPT's pubads addEventListener so we can fake an event
    var passedEventName
    var passedEventCallback
    window.googletag.pubads().addEventListener.mockImplementation((eventName, callback) => {
      passedEventName = eventName
      passedEventCallback = callback
    })

    const handleAdsLoaded = require('../handleAdsLoaded').default
    handleAdsLoaded()

    // Run the queued googletag commands
    window.googletag.cmd.forEach((cmd) => cmd())

    // Fake the event callback
    // https://developers.google.com/doubleclick-gpt/reference#googletageventsslotonloadevent
    const slotId = 'abc-123'
    passedEventCallback({
      slot: {
        getSlotElementId: () => slotId
      }
    })

    // Check that we're using the expected GPT event
    expect(passedEventName).toEqual('slotOnload')

    // Make sure we've marked the slot as loaded
    expect(window.tabforacause.ads.slotsLoaded[slotId]).toBe(true)

    // Make sure it works multiple times
    const otherSlotId = 'xyz-987'
    expect(window.tabforacause.ads.slotsLoaded[otherSlotId]).toBeUndefined()
    passedEventCallback({
      slot: {
        getSlotElementId: () => otherSlotId
      }
    })
    expect(window.tabforacause.ads.slotsLoaded[otherSlotId]).toBe(true)
  })
})
<commit_msg>Augment mock SlotOnload event<commit_after>/* eslint-env jest */

beforeEach(() => {
  delete window.googletag
  delete window.tabforacause

  // Mock googletag
  const mockAddEventListener = jest.fn()
  window.googletag = {
    cmd: [],
    pubads: () => ({
      addEventListener: mockAddEventListener
    })
  }

  // Mock tabforacause global
  window.tabforacause = {
    ads: {
      slotsLoaded: {},
      slotsAlreadyLoggedRevenue: {}
    }
  }

  jest.clearAllMocks()
  jest.resetModules()
})

afterAll(() => {
  delete window.googletag
  delete window.tabforacause
})

describe('handleAdsLoaded', function () {
  it('adds a slot ID to window.tabforacause\'s "loaded slots" object when GPT\'s "slotOnload" event is fired', () => {
    // Mock GPT's pubads addEventListener so we can fake an event
    var passedEventName
    var passedEventCallback
    window.googletag.pubads().addEventListener.mockImplementation((eventName, callback) => {
      passedEventName = eventName
      passedEventCallback = callback
    })

    const handleAdsLoaded = require('../handleAdsLoaded').default
    handleAdsLoaded()

    // Run the queued googletag commands
    window.googletag.cmd.forEach((cmd) => cmd())

    // Fake the event callback
    // https://developers.google.com/doubleclick-gpt/reference#googletageventsslotonloadevent
    const slotId = 'abc-123'
    const mockSlotLoadEventData = {
      // https://developers.google.com/doubleclick-gpt/reference#googletagslot
      slot: {
        getSlotElementId: () => slotId
        // ... other methods here
      },
      advertiserId: 1234,
      campaignId: 99887766,
      creativeId: 111222333444555,
      isEmpty: false,
      lineItemId: 123456,
      serviceName: 'something',
      size: '728x90',
      sourceAgnosticCreativeId: null,
      sourceAgnosticLineItemId: null
    }
    passedEventCallback(mockSlotLoadEventData)

    // Check that we're using the expected GPT event
    expect(passedEventName).toEqual('slotOnload')

    // Make sure we've marked the slot as loaded
    expect(window.tabforacause.ads.slotsLoaded[slotId]).toBe(true)

    // Make sure it works multiple times
    const otherSlotId = 'xyz-987'
    expect(window.tabforacause.ads.slotsLoaded[otherSlotId]).toBeUndefined()
    passedEventCallback({
      slot: {
        getSlotElementId: () => otherSlotId
      }
    })
    expect(window.tabforacause.ads.slotsLoaded[otherSlotId]).toBe(true)
  })
})
<|endoftext|>


<commit_before>import React from 'react';
const domainProps = {
    params: {
        domainTrackHeight  : React.PropTypes.number,
        proteinTrackHeight : React.PropTypes.number,
        leftSideGutter     : React.PropTypes.number,
        rightSideGutter    : React.PropTypes.number,
        topGutter          : React.PropTypes.number,
        trackGap           : React.PropTypes.number,
        svgWidth           : React.PropTypes.number
    },
    colors: {
        domains: React.PropTypes.shape({
            PROSITE     : React.PropTypes.string,
            HAMAP       : React.PropTypes.string,
            PFAM        : React.PropTypes.string,
            PRINTS      : React.PropTypes.string,
            PRODOM      : React.PropTypes.string,
            SMART       : React.PropTypes.string,
            TIGRFAMS    : React.PropTypes.string,
            SUPERFAMILY : React.PropTypes.string,
            GENE3D      : React.PropTypes.string,
            PANTHER     : React.PropTypes.string,
            TMHMM       : React.PropTypes.string,
            PHOBIUS     : React.PropTypes.string,
            SIGNALP     : React.PropTypes.string,
            PIRSF       : React.PropTypes.string
        }),
        signature: React.PropTypes.shape({
            accession   : React.PropTypes.string,
            description : React.PropTypes.string
        }),
        protein: React.PropTypes.string
    },
    numofBins: 4
};

const domainDefaults = {
    params: {
        domainTrackHeight: 10,
        proteinTrackHeight: 18,
        topGutter: 10,
        leftSideGutter: 150,
        rightSideGutter: 50,
        trackGap: 40,
        svgWidth: 950
    },
    colors: {
        domains: {
            PROFILE: "#87D37C",
            HAMAP: "#D64541",
            PFAM: "#D2527F",
            PRINTS: "#BF55EC",
            PRODOM: "3A539B",
            SMART: "#F4D03F",
            TIGRFAMS: "#F2784B",
            PIRSF: "#6C7A89",
            SSF: "#96281B",
            GENE3D: "#E26A6A",
            PANTHER: "#9A12B3",
            TMHMM: "#2574A9",
            PHOBIUS: "#03A678",
            SIGNALP: "#F5D76E",
            unavailable: "#BDC3C7"
        },
        signature: {
            accession: "#6C7A89",
            description: "#95A5A6"
        },
        polypeptide: {
            track: "#446CB3",
            label: "#FFFFCC"
        }
    }
};

export { domainProps, domainDefaults };
<commit_msg>added numOfBins to default props<commit_after>import React from 'react';
const domainProps = {
    params: {
        domainTrackHeight  : React.PropTypes.number,
        proteinTrackHeight : React.PropTypes.number,
        leftSideGutter     : React.PropTypes.number,
        rightSideGutter    : React.PropTypes.number,
        topGutter          : React.PropTypes.number,
        trackGap           : React.PropTypes.number,
        svgWidth           : React.PropTypes.number
    },
    colors: {
        domains: React.PropTypes.shape({
            PROSITE     : React.PropTypes.string,
            HAMAP       : React.PropTypes.string,
            PFAM        : React.PropTypes.string,
            PRINTS      : React.PropTypes.string,
            PRODOM      : React.PropTypes.string,
            SMART       : React.PropTypes.string,
            TIGRFAMS    : React.PropTypes.string,
            SUPERFAMILY : React.PropTypes.string,
            GENE3D      : React.PropTypes.string,
            PANTHER     : React.PropTypes.string,
            TMHMM       : React.PropTypes.string,
            PHOBIUS     : React.PropTypes.string,
            SIGNALP     : React.PropTypes.string,
            PIRSF       : React.PropTypes.string
        }),
        signature: React.PropTypes.shape({
            accession   : React.PropTypes.string,
            description : React.PropTypes.string
        }),
        protein: React.PropTypes.string
    }
};

const domainDefaults = {
    params: {
        domainTrackHeight: 10,
        proteinTrackHeight: 18,
        topGutter: 10,
        leftSideGutter: 150,
        rightSideGutter: 50,
        trackGap: 40,
        svgWidth: 950
    },
    colors: {
        domains: {
            PROFILE: "#87D37C",
            HAMAP: "#D64541",
            PFAM: "#D2527F",
            PRINTS: "#BF55EC",
            PRODOM: "3A539B",
            SMART: "#F4D03F",
            TIGRFAMS: "#F2784B",
            PIRSF: "#6C7A89",
            SSF: "#96281B",
            GENE3D: "#E26A6A",
            PANTHER: "#9A12B3",
            TMHMM: "#2574A9",
            PHOBIUS: "#03A678",
            SIGNALP: "#F5D76E",
            unavailable: "#BDC3C7"
        },
        signature: {
            accession: "#6C7A89",
            description: "#95A5A6"
        },
        polypeptide: {
            track: "#446CB3",
            label: "#FFFFCC"
        }
    },
    numOfBins: 4
};

export { domainProps, domainDefaults };
<|endoftext|>