<commit_before>/*
 * monitor.js: Core functionality for the Monitor object.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var events = require('events'),
    fs = require('fs'),
    path = require('path'),
    nodeFork = require('node-fork'),
    fork = nodeFork.fork,
    spawn = require('child_process').spawn,
    broadway = require('broadway'),
    psTree = require('ps-tree'),
    winston = require('winston'),
    utile = require('utile'),
    forever = require('../forever');

//
// ### function Monitor (script, options)
// #### @script {string} Location of the target script to run.
// #### @options {Object} Configuration for this instance.
// Creates a new instance of forever with specified `options`.
//
var Monitor = exports.Monitor = function (script, options) {
  //
  // Simple bootstrapper for attaching logger
  // and watch plugins by default. Other plugins
  // can be attached through `monitor.use(plugin, options)`.
  //
  function bootstrap(monitor) {
    forever.plugins.logger.attach.call(monitor, options);

    if (options.watch) {
      forever.plugins.watch.attach.call(monitor, options);
    }
  }

  var self = this;

  //
  // Setup basic configuration options
  //
  options          = options || {};
  this.silent      = options.silent || false;
  this.killTree    = options.killTree !== false;
  this.uid         = options.uid || utile.randomString(4);
  this.pidFile     = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
  this.max         = options.max;
  this.killTTL     = options.killTTL;
  this.childExists = false;
  this.times       = 0;

  //
  // Setup log files and logger for this instance.
  //
  this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
  this.outFile = options.outFile;
  this.errFile = options.errFile;
  this.logger  = options.logger || new (winston.Logger)({
    transports: [new winston.transports.Console({ silent: this.silent })]
  });

  //
  // Extend from the winston logger.
  //
  this.logger.extend(this);
  this.log = fs.createWriteStream(this.logFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });

  //
  // Setup restart timing. These options control how quickly forever restarts
  // a child process as well as when to kill a "spinning" process
  //
  this.minUptime     = typeof options.minUptime !== 'number' ? 0 : options.minUptime;
  this.spinSleepTime = options.spinSleepTime || null;

  //
  // Setup the command to spawn and the options to pass
  // to that command.
  //
  this.command   = options.command || 'node';
  this.args      = options.options || [];
  this.spawnWith = options.spawnWith || {};
  this.sourceDir = options.sourceDir;
  this.fork      = options.fork || false;
  this.forkShim  = options.forkShim || false;
  this.cwd       = options.cwd || null;
  this.hideEnv   = options.hideEnv || [];
  this._env      = options.env || {};
  this._hideEnv  = {};

  //
  // Setup watch configuration options
  //
  this.watchIgnoreDotFiles = options.watchIgnoreDotFiles || true;
  this.watchIgnorePatterns = options.watchIgnorePatterns || [];
  this.watchDirectory      = options.watchDirectory || this.sourceDir;

  //
  // Create a simple mapping of `this.hideEnv` to an easily indexable
  // object
  //
  this.hideEnv.forEach(function (key) {
    self._hideEnv[key] = true;
  });

  if (Array.isArray(script)) {
    this.command = script[0];
    this.args = script.slice(1);
  }
  else {
    this.args.unshift(script);
  }

  if (this.sourceDir) {
    this.args[0] = path.join(this.sourceDir, this.args[0]);
  }

  //
  // Bootstrap this instance now that options
  // have been set
  //
  broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } });
};

// Inherit from events.EventEmitter
utile.inherits(Monitor, broadway.App);

//
// ### function start ([restart])
// #### @restart {boolean} Value indicating whether this is a restart.
// Start the process that this instance is configured for
//
Monitor.prototype.start = function (restart) {
  var self = this,
      child;

  if (this.running && !restart) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot start process that is already running.'));
    });
    return this;
  }

  child = this.trySpawn();
  if (!child) {
    process.nextTick(function () {
      self.emit('error', new Error('Target script does not exist: ' + self.args[0]));
    });
    return this;
  }

  this.ctime = Date.now();
  this.child = child;
  this.running = true;

  process.nextTick(function () {
    self.emit(restart ? 'restart' : 'start', self, self.data);
  });

  function onMessage(msg) {
    self.emit('message', msg);
  }

  // Re-emit messages from the child process
  this.child.on('message', onMessage);

  child.on('exit', function (code) {
    var spinning = Date.now() - self.ctime < self.minUptime;
    self.warn('Forever detected script exited with code: ' + code);
    child.removeListener('message', onMessage);

    function letChildDie() {
      self.running = false;
      self.forceStop = false;

      self.emit('exit', self, spinning);
    }

    function restartChild() {
      self.forceRestart = false;
      process.nextTick(function () {
        self.warn('Forever restarting script for ' + self.times + ' time');
        self.start(true);
      });
    }

    self.times++;

    if (self.forceStop || (self.times >= self.max)
      || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
      letChildDie();
    }
    else if (spinning) {
      setTimeout(restartChild, self.spinSleepTime);
    }
    else {
      restartChild();
    }
  });

  return this;
};

//
// ### function trySpawn()
// Tries to spawn the target Forever child process. Depending on
// configuration, it checks the first argument of the options
// to see if the file exists. This is useful is you are
// trying to execute a script with an env: e.g. node myfile.js
//
Monitor.prototype.trySpawn = function () {
  if (this.command === 'node' || (this.checkFile && !this.childExists)) {
    try {
      var stats = fs.statSync(this.args[0]);
      this.childExists = true;
    }
    catch (ex) {
      return false;
    }
  }

  this.spawnWith.cwd = this.cwd || this.spawnWith.cwd;
  this.spawnWith.env = this._getEnv();

  if (this.fork) {
    this.spawnWith.silent = true;
    this.spawnWith.command = this.command;

    if (this.forkShim) {
      this.spawnWith.env['FORK_SHIM'] = true;
      return nodeFork.shim.fork(this.args[0], this.args.slice(1), this.spawnWith);
    }

    return fork(this.args[0], this.args.slice(1), this.spawnWith);
  }

  return spawn(this.command, this.args, this.spawnWith);
};

//
// ### @data {Object}
// Responds with the appropriate information about
// this `Monitor` instance and it's associated child process.
//
Monitor.prototype.__defineGetter__('data', function () {
  var self = this,
      childData;

  if (!this.running) {
    //
    // TODO: Return settings from this forever instance
    // with a state indicator that it is currently stopped.
    //
    return {};
  }

  childData = {
    ctime: this.ctime,
    command: this.command,
    file: this.args[0],
    foreverPid: process.pid,
    logFile: this.logFile,
    options: this.args.slice(1),
    pid: this.child.pid,
    silent: this.silent,
    uid: this.uid,
    spawnWith: this.spawnWith
  };

  ['pidFile', 'outFile', 'errFile', 'env', 'cwd'].forEach(function (key) {
    if (self[key]) {
      childData[key] = self[key];
    }
  });

  if (this.sourceDir) {
    childData.sourceDir = this.sourceDir;
    childData.file = childData.file.replace(this.sourceDir + '/', '');
  }

  this.childData = childData;
  return this.childData;

  //
  // Setup the forever process to listen to
  // SIGINT and SIGTERM events so that we can
  // clean up the *.pid file
  //
  // Remark: This should work, but the fd gets screwed up
  //         with the daemon process.
  //
  // process.on('SIGINT', function () {
  //   process.exit(0);
  // });
  //
  // process.on('SIGTERM', function () {
  //   process.exit(0);
  // });
  // process.on('exit', function () {
  //   fs.unlinkSync(childPath);
  // });
});

//
// ### function restart ()
// Restarts the target script associated with this instance.
//
Monitor.prototype.restart = function () {
  this.forceRestart = true;
  return this.kill(false);
};

//
// ### function stop ()
// Stops the target script associated with this instance. Prevents it from auto-respawning
//
Monitor.prototype.stop = function () {
  return this.kill(true);
};

//
// ### function kill (forceStop)
// #### @forceStop {boolean} Value indicating whether short circuit forever auto-restart.
// Kills the ChildProcess object associated with this instance.
//
Monitor.prototype.kill = function (forceStop) {
  var self = this,
      child = this.child;

  if (!child || !this.running) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot stop process that is not running.'));
    });
  }
  else {
    //
    // Set an instance variable here to indicate this
    // stoppage is forced so that when `child.on('exit', ..)`
    // fires in `Monitor.prototype.start` we can short circuit
    // and prevent auto-restart
    //
    var toKill = [this.child.pid];
    if (forceStop) {
      this.forceStop = true;
      //
      // If we have a time before we truly kill forcefully, set up a timer
      //
      if (this.killTTL) {
        var timer = setTimeout(function () {
          toKill.forEach(function(pid) {
            try {
              process.kill(pid, 'SIGKILL');
            }
            catch (e) {
              //conditions for races may exist, this is most likely an ESRCH
              //these should be ignored, and then we should emit that it is dead
            }
          });

          self.emit('stop', this.childData);
        }, this.killTTL);

        child.on('exit', function () {
          clearTimeout(timer);
        });
      }
    }

    forever.kill(this.child.pid, this.killTree, function() {
      self.emit('stop', self.childData);
    });
  }

  return this;
};

//
// ### @private function _getEnv ()
// Returns the environment variables that should be passed along
// to the target process spawned by this instance.
//
Monitor.prototype._getEnv = function () {
  var self = this,
      merged = {};

  function addKey(key, source) {
    merged[key] = source[key];
  }

  //
  // Mixin the key:value pairs from `process.env` and the custom
  // environment variables in `this._env`.
  //
  Object.keys(process.env).forEach(function (key) {
    if (!self._hideEnv[key]) {
      addKey(key, process.env);
    }
  });

  Object.keys(this._env).forEach(function (key) {
    addKey(key, self._env);
  });

  return merged;
};<commit_msg>[fix api] Expose `checkFile` and fix logical condition<commit_after>/*
 * monitor.js: Core functionality for the Monitor object.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var events = require('events'),
    fs = require('fs'),
    path = require('path'),
    nodeFork = require('node-fork'),
    fork = nodeFork.fork,
    spawn = require('child_process').spawn,
    broadway = require('broadway'),
    psTree = require('ps-tree'),
    winston = require('winston'),
    utile = require('utile'),
    forever = require('../forever');

//
// ### function Monitor (script, options)
// #### @script {string} Location of the target script to run.
// #### @options {Object} Configuration for this instance.
// Creates a new instance of forever with specified `options`.
//
var Monitor = exports.Monitor = function (script, options) {
  //
  // Simple bootstrapper for attaching logger
  // and watch plugins by default. Other plugins
  // can be attached through `monitor.use(plugin, options)`.
  //
  function bootstrap(monitor) {
    forever.plugins.logger.attach.call(monitor, options);

    if (options.watch) {
      forever.plugins.watch.attach.call(monitor, options);
    }
  }

  var self = this;

  //
  // Setup basic configuration options
  //
  options          = options || {};
  this.silent      = options.silent || false;
  this.killTree    = options.killTree !== false;
  this.uid         = options.uid || utile.randomString(4);
  this.pidFile     = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
  this.max         = options.max;
  this.killTTL     = options.killTTL;
  this.childExists = false;
  this.checkFile   = options.checkFile !== false;
  this.times       = 0;

  //
  // Setup log files and logger for this instance.
  //
  this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
  this.outFile = options.outFile;
  this.errFile = options.errFile;
  this.logger  = options.logger || new (winston.Logger)({
    transports: [new winston.transports.Console({ silent: this.silent })]
  });

  //
  // Extend from the winston logger.
  //
  this.logger.extend(this);
  this.log = fs.createWriteStream(this.logFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });

  //
  // Setup restart timing. These options control how quickly forever restarts
  // a child process as well as when to kill a "spinning" process
  //
  this.minUptime     = typeof options.minUptime !== 'number' ? 0 : options.minUptime;
  this.spinSleepTime = options.spinSleepTime || null;

  //
  // Setup the command to spawn and the options to pass
  // to that command.
  //
  this.command   = options.command || 'node';
  this.args      = options.options || [];
  this.spawnWith = options.spawnWith || {};
  this.sourceDir = options.sourceDir;
  this.fork      = options.fork || false;
  this.forkShim  = options.forkShim || false;
  this.cwd       = options.cwd || null;
  this.hideEnv   = options.hideEnv || [];
  this._env      = options.env || {};
  this._hideEnv  = {};

  //
  // Setup watch configuration options
  //
  this.watchIgnoreDotFiles = options.watchIgnoreDotFiles || true;
  this.watchIgnorePatterns = options.watchIgnorePatterns || [];
  this.watchDirectory      = options.watchDirectory || this.sourceDir;

  //
  // Create a simple mapping of `this.hideEnv` to an easily indexable
  // object
  //
  this.hideEnv.forEach(function (key) {
    self._hideEnv[key] = true;
  });

  if (Array.isArray(script)) {
    this.command = script[0];
    this.args = script.slice(1);
  }
  else {
    this.args.unshift(script);
  }

  if (this.sourceDir) {
    this.args[0] = path.join(this.sourceDir, this.args[0]);
  }

  //
  // Bootstrap this instance now that options
  // have been set
  //
  broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } });
};

// Inherit from events.EventEmitter
utile.inherits(Monitor, broadway.App);

//
// ### function start ([restart])
// #### @restart {boolean} Value indicating whether this is a restart.
// Start the process that this instance is configured for
//
Monitor.prototype.start = function (restart) {
  var self = this,
      child;

  if (this.running && !restart) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot start process that is already running.'));
    });
    return this;
  }

  child = this.trySpawn();
  if (!child) {
    process.nextTick(function () {
      self.emit('error', new Error('Target script does not exist: ' + self.args[0]));
    });
    return this;
  }

  this.ctime = Date.now();
  this.child = child;
  this.running = true;

  process.nextTick(function () {
    self.emit(restart ? 'restart' : 'start', self, self.data);
  });

  function onMessage(msg) {
    self.emit('message', msg);
  }

  // Re-emit messages from the child process
  this.child.on('message', onMessage);

  child.on('exit', function (code) {
    var spinning = Date.now() - self.ctime < self.minUptime;
    self.warn('Forever detected script exited with code: ' + code);
    child.removeListener('message', onMessage);

    function letChildDie() {
      self.running = false;
      self.forceStop = false;

      self.emit('exit', self, spinning);
    }

    function restartChild() {
      self.forceRestart = false;
      process.nextTick(function () {
        self.warn('Forever restarting script for ' + self.times + ' time');
        self.start(true);
      });
    }

    self.times++;

    if (self.forceStop || (self.times >= self.max)
      || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
      letChildDie();
    }
    else if (spinning) {
      setTimeout(restartChild, self.spinSleepTime);
    }
    else {
      restartChild();
    }
  });

  return this;
};

//
// ### function trySpawn()
// Tries to spawn the target Forever child process. Depending on
// configuration, it checks the first argument of the options
// to see if the file exists. This is useful is you are
// trying to execute a script with an env: e.g. node myfile.js
//
Monitor.prototype.trySpawn = function () {
  if (this.command === 'node' && this.checkFile && !this.childExists) {
    try {
      var stats = fs.statSync(this.args[0]);
      this.childExists = true;
    }
    catch (ex) {
      return false;
    }
  }

  this.spawnWith.cwd = this.cwd || this.spawnWith.cwd;
  this.spawnWith.env = this._getEnv();

  if (this.fork) {
    this.spawnWith.silent = true;
    this.spawnWith.command = this.command;

    if (this.forkShim) {
      this.spawnWith.env['FORK_SHIM'] = true;
      return nodeFork.shim.fork(this.args[0], this.args.slice(1), this.spawnWith);
    }

    return fork(this.args[0], this.args.slice(1), this.spawnWith);
  }

  return spawn(this.command, this.args, this.spawnWith);
};

//
// ### @data {Object}
// Responds with the appropriate information about
// this `Monitor` instance and it's associated child process.
//
Monitor.prototype.__defineGetter__('data', function () {
  var self = this,
      childData;

  if (!this.running) {
    //
    // TODO: Return settings from this forever instance
    // with a state indicator that it is currently stopped.
    //
    return {};
  }

  childData = {
    ctime: this.ctime,
    command: this.command,
    file: this.args[0],
    foreverPid: process.pid,
    logFile: this.logFile,
    options: this.args.slice(1),
    pid: this.child.pid,
    silent: this.silent,
    uid: this.uid,
    spawnWith: this.spawnWith
  };

  ['pidFile', 'outFile', 'errFile', 'env', 'cwd'].forEach(function (key) {
    if (self[key]) {
      childData[key] = self[key];
    }
  });

  if (this.sourceDir) {
    childData.sourceDir = this.sourceDir;
    childData.file = childData.file.replace(this.sourceDir + '/', '');
  }

  this.childData = childData;
  return this.childData;

  //
  // Setup the forever process to listen to
  // SIGINT and SIGTERM events so that we can
  // clean up the *.pid file
  //
  // Remark: This should work, but the fd gets screwed up
  //         with the daemon process.
  //
  // process.on('SIGINT', function () {
  //   process.exit(0);
  // });
  //
  // process.on('SIGTERM', function () {
  //   process.exit(0);
  // });
  // process.on('exit', function () {
  //   fs.unlinkSync(childPath);
  // });
});

//
// ### function restart ()
// Restarts the target script associated with this instance.
//
Monitor.prototype.restart = function () {
  this.forceRestart = true;
  return this.kill(false);
};

//
// ### function stop ()
// Stops the target script associated with this instance. Prevents it from auto-respawning
//
Monitor.prototype.stop = function () {
  return this.kill(true);
};

//
// ### function kill (forceStop)
// #### @forceStop {boolean} Value indicating whether short circuit forever auto-restart.
// Kills the ChildProcess object associated with this instance.
//
Monitor.prototype.kill = function (forceStop) {
  var self = this,
      child = this.child;

  if (!child || !this.running) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot stop process that is not running.'));
    });
  }
  else {
    //
    // Set an instance variable here to indicate this
    // stoppage is forced so that when `child.on('exit', ..)`
    // fires in `Monitor.prototype.start` we can short circuit
    // and prevent auto-restart
    //
    var toKill = [this.child.pid];
    if (forceStop) {
      this.forceStop = true;
      //
      // If we have a time before we truly kill forcefully, set up a timer
      //
      if (this.killTTL) {
        var timer = setTimeout(function () {
          toKill.forEach(function(pid) {
            try {
              process.kill(pid, 'SIGKILL');
            }
            catch (e) {
              //conditions for races may exist, this is most likely an ESRCH
              //these should be ignored, and then we should emit that it is dead
            }
          });

          self.emit('stop', this.childData);
        }, this.killTTL);

        child.on('exit', function () {
          clearTimeout(timer);
        });
      }
    }

    forever.kill(this.child.pid, this.killTree, function() {
      self.emit('stop', self.childData);
    });
  }

  return this;
};

//
// ### @private function _getEnv ()
// Returns the environment variables that should be passed along
// to the target process spawned by this instance.
//
Monitor.prototype._getEnv = function () {
  var self = this,
      merged = {};

  function addKey(key, source) {
    merged[key] = source[key];
  }

  //
  // Mixin the key:value pairs from `process.env` and the custom
  // environment variables in `this._env`.
  //
  Object.keys(process.env).forEach(function (key) {
    if (!self._hideEnv[key]) {
      addKey(key, process.env);
    }
  });

  Object.keys(this._env).forEach(function (key) {
    addKey(key, self._env);
  });

  return merged;
};
<|endoftext|>


<commit_before>(function(window, angular, undefined) {'use strict';

/**
 * @ngdoc overview
 * @name angulartics.google.analytics
 * Enables analytics support for Google Analytics (http://google.com/analytics)
 */
angular.module('angulartics.google.analytics', ['angulartics'])
.config(['$analyticsProvider', function ($analyticsProvider) {

  // GA already supports buffered invocations so we don't need
  // to wrap these inside angulartics.waitForVendorApi

  $analyticsProvider.settings.pageTracking.trackRelativePath = true;

  // Set the default settings for this module
  $analyticsProvider.settings.ga = {
    additionalAccountNames: undefined,
    disableEventTracking: null,
    disablePageTracking: null,
    userId: null
  };

  function dimensionsAndMetrics(properties) {
    if (window.ga) {
      // add custom dimensions and metrics
      var customData = {};
      var key;

      for (key in properties) {
        if (!key.indexOf('dimension') || !key.indexOf('metric')) {
          customData[key] = properties[key];
        }
      }
      return customData;
    }
  }

  $analyticsProvider.registerPageTrack(function (path, properties) {

    // Do nothing if page tracking is disabled
    if ($analyticsProvider.settings.ga.disablePageTracking) return;

    if (window._gaq) {
      _gaq.push(['_trackPageview', path]);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        _gaq.push([accountName + '._trackPageview', path]);
      });
    }
    if (window.ga) {
      var dimsAndMets = dimensionsAndMetrics(properties);
      if ($analyticsProvider.settings.ga.userId) {
        ga('set', 'userId', $analyticsProvider.settings.ga.userId);
      }
      ga('send', 'pageview', path, dimsAndMets);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'pageview', path, dimsAndMets);
      });
    }
  });

  /**
   * Track Event in GA
   * @name eventTrack
   *
   * @param {string} action Required 'action' (string) associated with the event
   * @param {object} properties Comprised of the mandatory field 'category' (string) and optional  fields 'label' (string), 'value' (integer) and 'nonInteraction' (boolean)
   *
   * @link https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#SettingUpEventTracking
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerEventTrack(eventTrack);

  function eventTrack (action, properties) {

    // Do nothing if event tracking is disabled
    if ($analyticsProvider.settings.ga.disableEventTracking) return;

    // Google Analytics requires an Event Category
    if (!properties || !properties.category) {
      properties = properties || {};
      properties.category = 'Event';
    }
    // GA requires that eventValue be an integer, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue
    // https://github.com/luisfarzati/angulartics/issues/81
    if (properties.value) {
      var parsed = parseInt(properties.value, 10);
      properties.value = isNaN(parsed) ? 0 : parsed;
    }

    // GA requires that hitCallback be an function, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#hitcallback
    if (properties.hitCallback && (typeof properties.hitCallback !== 'function')) {
      properties.hitCallback = null;
    }

    // Making nonInteraction parameter more intuitive, includes backwards compatibilty
    // https://github.com/angulartics/angulartics-google-analytics/issues/49
    if (!properties.hasOwnProperty('nonInteraction')) {
      properties.nonInteraction = properties.noninteraction;
    }

    if (window.ga) {

      var eventOptions = {
        eventCategory: properties.category,
        eventAction: action,
        eventLabel: properties.label,
        eventValue: properties.value,
        nonInteraction: properties.nonInteraction,
        page: properties.page || window.location.hash.substring(1) || window.location.pathname,
        userId: $analyticsProvider.settings.ga.userId,
        hitCallback: properties.hitCallback
      };

      // Round up any dimensions and metrics for this hit
      var dimsAndMets = dimensionsAndMetrics(properties);
      angular.extend(eventOptions, dimsAndMets);

      // Add transport settings
      if($analyticsProvider.settings.ga.transport) {
        angular.extend(eventOptions, $analyticsProvider.settings.ga.transport);
      }

      ga('send', 'event', eventOptions);

      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'event', eventOptions);
      });

    } else if (window._gaq) {
      _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value, properties.nonInteraction]);
    }

  }

  /**
   * Exception Track Event in GA
   * @name exceptionTrack
   * Sugar on top of the eventTrack method for easily handling errors
   *
   * @param {object} error An Error object to track: error.toString() used for event 'action', error.stack used for event 'label'.
   * @param {object} cause The cause of the error given from $exceptionHandler, not used.
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerExceptionTrack(function (error, cause) {
    eventTrack(error.toString(), {
      category: 'Exceptions',
      label: error.stack,
      nonInteraction: true
    });
  });

  /**
   * Set Username
   * @name setUsername
   *
   * @param {string} userId Registers User ID of user for use with other hits
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#user_id
   */
  $analyticsProvider.registerSetUsername(function (userId) {
    $analyticsProvider.settings.ga.userId = userId;
  });

  /**
   * Set User Properties
   * @name setUserProperties
   *
   * @param {object} properties Sets all properties with dimensionN or metricN to their respective values
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
   */
  $analyticsProvider.registerSetUserProperties(function (properties) {
    if(properties) {
      // add custom dimensions and metrics to each hit
      var dimsAndMets = dimensionsAndMetrics(properties);
      ga('set', dimsAndMets);
    }
  });

  /**
   * User Timings Event in GA
   * @name userTimings
   *
   * @param {object} properties Comprised of the mandatory fields:
   *     'timingCategory' (string),
   *     'timingVar' (string),
   *     'timingValue' (number)
   * Properties can also have the optional fields:
   *     'timingLabel' (string)
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings
   */
  $analyticsProvider.registerUserTimings(function (properties) {
    if (!properties || !properties.timingCategory || !properties.timingVar || typeof properties.timingValue === 'undefined') {
      console.log('Properties timingCategory, timingVar, and timingValue are required to be set.');
      return;
    }

    if(window.ga) {
      ga('send', 'timing', properties);
    }
  });

}]);
})(window, window.angular);
<commit_msg>beginning to add update<commit_after>(function(window, angular, undefined) {'use strict';

/**
 * @ngdoc overview
 * @name angulartics.google.analytics
 * Enables analytics support for Google Analytics (http://google.com/analytics)
 */
angular.module('angulartics.google.analytics', ['angulartics'])
.config(['$analyticsProvider', function ($analyticsProvider) {

  // GA already supports buffered invocations so we don't need
  // to wrap these inside angulartics.waitForVendorApi

  $analyticsProvider.settings.pageTracking.trackRelativePath = true;

  // Set the default settings for this module
  $analyticsProvider.settings.ga = {
    additionalAccountNames: undefined,
    // Select hits to send to all additional accounts
    additionalAccountConfig: {
      pageview: true,
      event: true,

    },
    disableEventTracking: null,
    disablePageTracking: null,
    userId: null
  };

  function dimensionsAndMetrics(properties) {
    if (window.ga) {
      // add custom dimensions and metrics
      var customData = {};
      var key;

      for (key in properties) {
        if (!key.indexOf('dimension') || !key.indexOf('metric')) {
          customData[key] = properties[key];
        }
      }
      return customData;
    }
  }

  function dispatchToGa(cmd) {

    if (detectUniversalAnalytics()) {

    } else if (detectClassicAnalytics()) {

    }

  }

  $analyticsProvider.registerPageTrack(function (path, properties) {

    // Do nothing if page tracking is disabled
    if ($analyticsProvider.settings.ga.disablePageTracking) return;

    if (window._gaq) {
      _gaq.push(['_trackPageview', path]);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        _gaq.push([accountName + '._trackPageview', path]);
      });
    }
    if (window.ga) {
      var dimsAndMets = dimensionsAndMetrics(properties);
      if ($analyticsProvider.settings.ga.userId) {
        ga('set', 'userId', $analyticsProvider.settings.ga.userId);
      }
      ga('send', 'pageview', path, dimsAndMets);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'pageview', path, dimsAndMets);
      });
    }
  });

  /**
   * Track Event in GA
   * @name eventTrack
   *
   * @param {string} action Required 'action' (string) associated with the event
   * @param {object} properties Comprised of the mandatory field 'category' (string) and optional  fields 'label' (string), 'value' (integer) and 'nonInteraction' (boolean)
   *
   * @link https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#SettingUpEventTracking
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerEventTrack(eventTrack);

  function eventTrack (action, properties) {

    // Do nothing if event tracking is disabled
    if ($analyticsProvider.settings.ga.disableEventTracking) return;

    // Google Analytics requires an Event Category
    if (!properties || !properties.category) {
      properties = properties || {};
      properties.category = 'Event';
    }
    // GA requires that eventValue be an integer, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue
    // https://github.com/luisfarzati/angulartics/issues/81
    if (properties.value) {
      var parsed = parseInt(properties.value, 10);
      properties.value = isNaN(parsed) ? 0 : parsed;
    }

    // GA requires that hitCallback be an function, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#hitcallback
    if (properties.hitCallback && (typeof properties.hitCallback !== 'function')) {
      properties.hitCallback = null;
    }

    // Making nonInteraction parameter more intuitive, includes backwards compatibilty
    // https://github.com/angulartics/angulartics-google-analytics/issues/49
    if (!properties.hasOwnProperty('nonInteraction')) {
      properties.nonInteraction = properties.noninteraction;
    }

    if (window.ga) {

      var eventOptions = {
        eventCategory: properties.category,
        eventAction: action,
        eventLabel: properties.label,
        eventValue: properties.value,
        nonInteraction: properties.nonInteraction,
        page: properties.page || window.location.hash.substring(1) || window.location.pathname,
        userId: $analyticsProvider.settings.ga.userId,
        hitCallback: properties.hitCallback
      };

      // Round up any dimensions and metrics for this hit
      var dimsAndMets = dimensionsAndMetrics(properties);
      angular.extend(eventOptions, dimsAndMets);

      // Add transport settings
      if($analyticsProvider.settings.ga.transport) {
        angular.extend(eventOptions, $analyticsProvider.settings.ga.transport);
      }

      ga('send', 'event', eventOptions);

      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'event', eventOptions);
      });

    } else if (window._gaq) {
      _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value, properties.nonInteraction]);
    }

  }

  /**
   * Exception Track Event in GA
   * @name exceptionTrack
   * Sugar on top of the eventTrack method for easily handling errors
   *
   * @param {object} error An Error object to track: error.toString() used for event 'action', error.stack used for event 'label'.
   * @param {object} cause The cause of the error given from $exceptionHandler, not used.
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerExceptionTrack(function (error, cause) {
    eventTrack(error.toString(), {
      category: 'Exceptions',
      label: error.stack,
      nonInteraction: true
    });
  });

  /**
   * Set Username
   * @name setUsername
   *
   * @param {string} userId Registers User ID of user for use with other hits
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#user_id
   */
  $analyticsProvider.registerSetUsername(function (userId) {
    $analyticsProvider.settings.ga.userId = userId;
  });

  /**
   * Set User Properties
   * @name setUserProperties
   *
   * @param {object} properties Sets all properties with dimensionN or metricN to their respective values
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
   */
  $analyticsProvider.registerSetUserProperties(function (properties) {
    if(properties) {
      // add custom dimensions and metrics to each hit
      var dimsAndMets = dimensionsAndMetrics(properties);
      ga('set', dimsAndMets);
    }
  });

  /**
   * User Timings Event in GA
   * @name userTimings
   *
   * @param {object} properties Comprised of the mandatory fields:
   *     'timingCategory' (string),
   *     'timingVar' (string),
   *     'timingValue' (number)
   * Properties can also have the optional fields:
   *     'timingLabel' (string)
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings
   */
  $analyticsProvider.registerUserTimings(function (properties) {
    if (!properties || !properties.timingCategory || !properties.timingVar || typeof properties.timingValue === 'undefined') {
      console.log('Properties timingCategory, timingVar, and timingValue are required to be set.');
      return;
    }

    if(window.ga) {
      ga('send', 'timing', properties);
    }
  });

}]);
})(window, window.angular);
<|endoftext|>


<commit_before>"use strict";

// jQluster Transport object
// requires: jquery, jquery.xpath, util.js

if(!jQluster) { var jQluster = {}; }

(function(my, $) {
    var myclass;
    myclass = my.Transport = function(args) {
        var self = this;
        // @params: args.my_remote_id, args.connection_object
        if(!my.defined(args.my_remote_id)) {
            throw "my_remote_id param is mandatory";
        }
        if(!my.defined(args.connection_object)) {
            throw "connection_object param is mandatory";
        }
        self.my_remote_id = args.my_remote_id;
        self.connection_object = args.connection_object;
        self.pending_request_for = {};
        self.signal_callback_for = {};

        self.connection_object.onReceive(function(message) { self._onReceive(message); });
        self.connection_object.send({
            message_id: my.uuid(), message_type: "register",
            from: self.my_remote_id, to: null,
            body: { remote_id: self.my_remote_id }
        });
    };
    myclass.prototype = {
        getMyRemoteID: function() { return this.my_remote_id; },
        selectAndGet: function(args) {
            // @params: args.eval_code, args.remote_id
            //
            // @return: Promise. In success, it is resolved and it
            // contains the obtained data. In failure it is rejected
            // and it contains the cause of the error.
            //
            // If the remote node does not exist, the returned Promise
            // will be rejected.
            //
            // If there are multiple remote nodes, the caller will
            // receive the result returned by one of the remote nodes,
            // but it is not defined exactly which remote node is
            // used.

            var self = this;
            var response_d = $.Deferred();
            try {
                if(!my.defined(args.eval_code)) {
                    throw "eval_code param is mandatory";
                }
                if(!my.defined(args.remote_id)) {
                    throw "remote_id param is mandatory";
                }
                var message = {
                    message_id: my.uuid(), message_type: "select_and_get",
                    from: self.my_remote_id, to: args.remote_id,
                    body: { eval_code: args.eval_code, remote_id: args.remote_id }
                };
                self.pending_request_for[message.message_id] = response_d;
                self.connection_object.send(message);
            }catch(e) {
                response_d.reject(e);
            }
            return response_d.promise();
        },
        selectAndListen: function(args) {
            // @params: args.eval_code (must return a jQuery object),
            //          args.method, args.options = [],
            //          args.callback, args.remote_id
            //
            // @return: Promise. It will be resolved if the request is
            // accepted by the remote node. The content of the promise
            // is meaningless in this case. In failure, it will be
            // rejected and it contains the cause of the error.
            //
            // If the remote node does not exist, the resulting
            // Promise will be rejected.
            //
            // If there are multiple remote nodes, all of them receive
            // the listen request. In this case, the resulting Promise
            // reflects one of the responses from those remote nodes,
            // but there is no guarantee on exactly which response is
            // used to affect the Promise.
            //
            // args.callback is called when the event occurs in the
            // remote node. Arguments for the args.callback is exact
            // copy of the arguments for the remote callback, except
            // that DOM elements are converted to 'Pointer
            // objects'. The structure of a Pointer object is:
            // {"remote_type": "xpath", "remote_id": REMOTE_ID,
            // "remote_xpath": XPATH_STR}. 'this' object for
            // args.callback is the copy of 'this' object for the
            // remote callback. It may be a Pointer object, too.

            var self = this;
            var result_d = $.Deferred();
            var message, callback;
            try {
                $.each(["remote_id", "eval_code", "method", "callback"], function(i, mandatory_key) {
                    if(!my.defined(args[mandatory_key])) {
                        throw mandatory_key + " param is mandatory";
                    }
                });
                callback = args.callback;
                if(!my.defined(args.options)) args.options = [];
                message = {
                    message_id: my.uuid(), message_type: "select_and_listen",
                    from: self.my_remote_id, to: args.remote_id,
                    body: {
                        remote_id: args.remote_id, eval_code: args.eval_code,
                        method: args.method, options: args.options
                    }
                };
                result_d.promise().then(function() {
                    // TODO: about remote signal handler: For now there is
                    // no way to REMOVE signal_callbacks. So, calling
                    // selectAndListen() can cause serious memory leak. We
                    // have to take care of releasing callbacks if we are
                    // serious to do remote callbacks.
                    self.signal_callback_for[message.message_id] = function(callback_this, callback_args) {
                        callback.apply(callback_this, callback_args);
                    };
                });
                self.pending_request_for[message.message_id] = result_d;
                self.connection_object.send(message);
            }catch(e) {
                result_d.reject(e);
            }
            return result_d.promise();
        },

        _onReceive: function(message) {
            // @return: nothing
            var self = this;
            if(message.message_type === "signal") {
                self._processSignal(message);
            }else if(my.defined(message.body.in_reply_to)) {
                self._onReply(message.body.in_reply_to, message);
            }else if(message.message_type === "select_and_get") {
                self._processSelectAndGet(message);
            }else if(message.message_type === "select_and_listen") {
                self._processSelectAndListen(message);
            }else {
                console.error("Unknown message received but discarded.");
                console.error(message);
            }
        },

        _KNOWN_REPLY_MESSAGE_TYPE: {
            select_and_get_reply: 1,
            select_and_listen_reply: 1
        },
        _onReply: function(in_reply_to, message) {
            var self = this;
            var pending_d = self.pending_request_for[in_reply_to];
            if(!my.defined(pending_d)) {
                return;
            }
            delete self.pending_request_for[in_reply_to];
            if(my.defined(self._KNOWN_REPLY_MESSAGE_TYPE[message.message_type])) {
                if(my.defined(message.body.error)) {
                    pending_d.reject(message.message_type + " error: " + message.body.error);
                }else {
                    pending_d.resolve(message.body.result);
                }
            }else {
                pending_d.reject("unkown message type: " + message.message_type);
            }
        },

        _processSelectAndGet: function(request_message) {
            var self = this;
            var result_p = null;
            try {
                result_p = $.when(eval(request_message.body.eval_code));
            }catch(e) {
                result_p = $.Deferred();
                result_p.reject(e);
            }
            var reply = {
                message_id: my.uuid(), message_type: "select_and_get_reply",
                from: self.my_remote_id, to: request_message.from,
                body: { "error": null,  "result": null, "in_reply_to": request_message.message_id}
            };
            result_p.then(function(result) {
                reply.body.result = result;
            }, function(error) {
                reply.body.error = error;
            }).always(function() {
                self.connection_object.send(reply);
            });
        },

        _processSignal: function(message) {
            var self = this;
            if(my.defined(message.error)) {
                console.error("signal message error:");
                console.error(message);
                return;
            }
            var callback = self.signal_callback_for[message.body.in_reply_to];
            if(!my.defined(callback)) {
                return;
            }
            callback(message.body.callback_this, message.body.callback_args);
        },

        _createRemoteDOMPointerIfDOM: function(obj) {
            if(my.isHTMLElement(obj)) {
               return {
                   remote_id: this.my_remote_id,
                   remote_type: "xpath",
                   remote_xpath: my.xpathFor($(obj))
               };
            }else {
                return obj;
            }
        },

        _processSelectAndListen: function(message) {
            var self = this;
            var jq_node;
            var args_for_method;
            var request_id = message.message_id;
            var request_from = message.from;
            var reply_sent = false;
            var try_send_reply = function(error) {
                if(reply_sent) return;
                reply_sent = true;
                self.connection_object.send({
                    message_id: my.uuid(), message_type: "select_and_listen_reply",
                    from: self.my_remote_id, to: request_from,
                    body: {error: error, result: (my.defined(error) ? "NG" : "OK"), in_reply_to: request_id}
                });
            };
            try {
                jq_node = eval(message.body.eval_code);
                args_for_method = message.body.options;
                args_for_method.push(function() {
                    var callback_this = self._createRemoteDOMPointerIfDOM(this);
                    var callback_args = [];
                    var i;
                    for(i = 0 ; i < arguments.length ; i++) {
                        callback_args.push(self._createRemoteDOMPointerIfDOM(arguments[i]));
                    }
                    try_send_reply(null);
                    self.connection_object.send({
                        message_id: my.uuid(), message_type: "signal",
                        from: self.my_remote_id, to: request_from,
                        body: { error: null, in_reply_to: request_id,
                                callback_this: callback_this, callback_args: callback_args}
                    });
                });
                jq_node[message.body.method].apply(jq_node, args_for_method);
                try_send_reply(null);
            }catch(e) {
                try_send_reply("_processSelectAndListen error: " + e);
            }
        },

        release: function() {
            if(my.defined(this.connection_object)) {
                this.connection_object.release();
            }
            this.connection_object = null;
            this.pending_request_for = {};
            this.signal_callback_for = {};
        }
    };

})(jQluster, jQuery);
<commit_msg>js/transport.js: add TODO comment on node grouping.<commit_after>"use strict";

// jQluster Transport object
// requires: jquery, jquery.xpath, util.js

if(!jQluster) { var jQluster = {}; }

(function(my, $) {
    var myclass;

    // TODO: Design and implement some mechanism for node grouping. In
    // the current implementation, it is possible for multiple nodes
    // to have the same ID. However, in this case there is no way to
    // distinguish individual nodes with that ID. With a proper
    // grouping mechanism, the user would be able to multi-cast a
    // message (e.g. a 'select_and_listen' message) and still
    // distinguish individual node in the group (e.g. the exact node
    // that sends a 'signal' message).

    myclass = my.Transport = function(args) {
        var self = this;
        // @params: args.my_remote_id, args.connection_object
        if(!my.defined(args.my_remote_id)) {
            throw "my_remote_id param is mandatory";
        }
        if(!my.defined(args.connection_object)) {
            throw "connection_object param is mandatory";
        }
        self.my_remote_id = args.my_remote_id;
        self.connection_object = args.connection_object;
        self.pending_request_for = {};
        self.signal_callback_for = {};

        self.connection_object.onReceive(function(message) { self._onReceive(message); });
        self.connection_object.send({
            message_id: my.uuid(), message_type: "register",
            from: self.my_remote_id, to: null,
            body: { remote_id: self.my_remote_id }
        });
    };
    myclass.prototype = {
        getMyRemoteID: function() { return this.my_remote_id; },
        selectAndGet: function(args) {
            // @params: args.eval_code, args.remote_id
            //
            // @return: Promise. In success, it is resolved and it
            // contains the obtained data. In failure it is rejected
            // and it contains the cause of the error.
            //
            // If the remote node does not exist, the returned Promise
            // will be rejected.
            //
            // If there are multiple remote nodes, the caller will
            // receive the result returned by one of the remote nodes,
            // but it is not defined exactly which remote node is
            // used.

            var self = this;
            var response_d = $.Deferred();
            try {
                if(!my.defined(args.eval_code)) {
                    throw "eval_code param is mandatory";
                }
                if(!my.defined(args.remote_id)) {
                    throw "remote_id param is mandatory";
                }
                var message = {
                    message_id: my.uuid(), message_type: "select_and_get",
                    from: self.my_remote_id, to: args.remote_id,
                    body: { eval_code: args.eval_code, remote_id: args.remote_id }
                };
                self.pending_request_for[message.message_id] = response_d;
                self.connection_object.send(message);
            }catch(e) {
                response_d.reject(e);
            }
            return response_d.promise();
        },
        selectAndListen: function(args) {
            // @params: args.eval_code (must return a jQuery object),
            //          args.method, args.options = [],
            //          args.callback, args.remote_id
            //
            // @return: Promise. It will be resolved if the request is
            // accepted by the remote node. The content of the promise
            // is meaningless in this case. In failure, it will be
            // rejected and it contains the cause of the error.
            //
            // If the remote node does not exist, the resulting
            // Promise will be rejected.
            //
            // If there are multiple remote nodes, all of them receive
            // the listen request. In this case, the resulting Promise
            // reflects one of the responses from those remote nodes,
            // but there is no guarantee on exactly which response is
            // used to affect the Promise.
            //
            // args.callback is called when the event occurs in the
            // remote node. Arguments for the args.callback is exact
            // copy of the arguments for the remote callback, except
            // that DOM elements are converted to 'Pointer
            // objects'. The structure of a Pointer object is:
            // {"remote_type": "xpath", "remote_id": REMOTE_ID,
            // "remote_xpath": XPATH_STR}. 'this' object for
            // args.callback is the copy of 'this' object for the
            // remote callback. It may be a Pointer object, too.

            var self = this;
            var result_d = $.Deferred();
            var message, callback;
            try {
                $.each(["remote_id", "eval_code", "method", "callback"], function(i, mandatory_key) {
                    if(!my.defined(args[mandatory_key])) {
                        throw mandatory_key + " param is mandatory";
                    }
                });
                callback = args.callback;
                if(!my.defined(args.options)) args.options = [];
                message = {
                    message_id: my.uuid(), message_type: "select_and_listen",
                    from: self.my_remote_id, to: args.remote_id,
                    body: {
                        remote_id: args.remote_id, eval_code: args.eval_code,
                        method: args.method, options: args.options
                    }
                };
                result_d.promise().then(function() {
                    // TODO: about remote signal handler: For now there is
                    // no way to REMOVE signal_callbacks. So, calling
                    // selectAndListen() can cause serious memory leak. We
                    // have to take care of releasing callbacks if we are
                    // serious to do remote callbacks.
                    self.signal_callback_for[message.message_id] = function(callback_this, callback_args) {
                        callback.apply(callback_this, callback_args);
                    };
                });
                self.pending_request_for[message.message_id] = result_d;
                self.connection_object.send(message);
            }catch(e) {
                result_d.reject(e);
            }
            return result_d.promise();
        },

        _onReceive: function(message) {
            // @return: nothing
            var self = this;
            if(message.message_type === "signal") {
                self._processSignal(message);
            }else if(my.defined(message.body.in_reply_to)) {
                self._onReply(message.body.in_reply_to, message);
            }else if(message.message_type === "select_and_get") {
                self._processSelectAndGet(message);
            }else if(message.message_type === "select_and_listen") {
                self._processSelectAndListen(message);
            }else {
                console.error("Unknown message received but discarded.");
                console.error(message);
            }
        },

        _KNOWN_REPLY_MESSAGE_TYPE: {
            select_and_get_reply: 1,
            select_and_listen_reply: 1
        },
        _onReply: function(in_reply_to, message) {
            var self = this;
            var pending_d = self.pending_request_for[in_reply_to];
            if(!my.defined(pending_d)) {
                return;
            }
            delete self.pending_request_for[in_reply_to];
            if(my.defined(self._KNOWN_REPLY_MESSAGE_TYPE[message.message_type])) {
                if(my.defined(message.body.error)) {
                    pending_d.reject(message.message_type + " error: " + message.body.error);
                }else {
                    pending_d.resolve(message.body.result);
                }
            }else {
                pending_d.reject("unkown message type: " + message.message_type);
            }
        },

        _processSelectAndGet: function(request_message) {
            var self = this;
            var result_p = null;
            try {
                result_p = $.when(eval(request_message.body.eval_code));
            }catch(e) {
                result_p = $.Deferred();
                result_p.reject(e);
            }
            var reply = {
                message_id: my.uuid(), message_type: "select_and_get_reply",
                from: self.my_remote_id, to: request_message.from,
                body: { "error": null,  "result": null, "in_reply_to": request_message.message_id}
            };
            result_p.then(function(result) {
                reply.body.result = result;
            }, function(error) {
                reply.body.error = error;
            }).always(function() {
                self.connection_object.send(reply);
            });
        },

        _processSignal: function(message) {
            var self = this;
            if(my.defined(message.error)) {
                console.error("signal message error:");
                console.error(message);
                return;
            }
            var callback = self.signal_callback_for[message.body.in_reply_to];
            if(!my.defined(callback)) {
                return;
            }
            callback(message.body.callback_this, message.body.callback_args);
        },

        _createRemoteDOMPointerIfDOM: function(obj) {
            if(my.isHTMLElement(obj)) {
               return {
                   remote_id: this.my_remote_id,
                   remote_type: "xpath",
                   remote_xpath: my.xpathFor($(obj))
               };
            }else {
                return obj;
            }
        },

        _processSelectAndListen: function(message) {
            var self = this;
            var jq_node;
            var args_for_method;
            var request_id = message.message_id;
            var request_from = message.from;
            var reply_sent = false;
            var try_send_reply = function(error) {
                if(reply_sent) return;
                reply_sent = true;
                self.connection_object.send({
                    message_id: my.uuid(), message_type: "select_and_listen_reply",
                    from: self.my_remote_id, to: request_from,
                    body: {error: error, result: (my.defined(error) ? "NG" : "OK"), in_reply_to: request_id}
                });
            };
            try {
                jq_node = eval(message.body.eval_code);
                args_for_method = message.body.options;
                args_for_method.push(function() {
                    var callback_this = self._createRemoteDOMPointerIfDOM(this);
                    var callback_args = [];
                    var i;
                    for(i = 0 ; i < arguments.length ; i++) {
                        callback_args.push(self._createRemoteDOMPointerIfDOM(arguments[i]));
                    }
                    try_send_reply(null);
                    self.connection_object.send({
                        message_id: my.uuid(), message_type: "signal",
                        from: self.my_remote_id, to: request_from,
                        body: { error: null, in_reply_to: request_id,
                                callback_this: callback_this, callback_args: callback_args}
                    });
                });
                jq_node[message.body.method].apply(jq_node, args_for_method);
                try_send_reply(null);
            }catch(e) {
                try_send_reply("_processSelectAndListen error: " + e);
            }
        },

        release: function() {
            if(my.defined(this.connection_object)) {
                this.connection_object.release();
            }
            this.connection_object = null;
            this.pending_request_for = {};
            this.signal_callback_for = {};
        }
    };

})(jQluster, jQuery);
<|endoftext|>


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

const path = require('path');
const webpackConfig = require('./webpack.config')[0];

const USING_TRAVISCI = Boolean(process.env.TRAVIS);
const USING_SL = Boolean(process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY);

const LOCAL_LAUNCHERS = {
  /** See https://github.com/travis-ci/travis-ci/issues/8836#issuecomment-348248951 */
  'ChromeHeadlessNoSandbox': {
    base: 'ChromeHeadless',
    flags: ['--no-sandbox'],
  },
};

const SAUCE_LAUNCHERS = {
  /*
   * Chrome (desktop)
   */

  'sl-chrome-stable': {
    base: 'SauceLabs',
    browserName: 'chrome',
    version: 'latest',
    platform: 'macOS 10.12',
    extendedDebugging: true,
  },
  // 'sl-chrome-beta': {
  //   base: 'SauceLabs',
  //   browserName: 'chrome',
  //   version: 'dev',
  //   platform: 'macOS 10.12',
  //   extendedDebugging: true,
  // },
  // 'sl-chrome-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'chrome',
  //   version: 'latest-1',
  //   platform: 'macOS 10.12',
  //   extendedDebugging: true,
  // },

  /*
   * Firefox
   */

  'sl-firefox-stable': {
    base: 'SauceLabs',
    browserName: 'firefox',
    version: 'latest',
    platform: 'Windows 10',
    extendedDebugging: true,
  },
  // 'sl-firefox-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'firefox',
  //   version: 'latest-1',
  //   platform: 'Windows 10',
  //   extendedDebugging: true,
  // },

  /*
   * IE
   */

  'sl-ie': {
    base: 'SauceLabs',
    browserName: 'internet explorer',
    version: '11',
    platform: 'Windows 8.1',
  },

  /*
   * Edge
   */

  // TODO(sgomes): Re-enable Edge and Safari after Sauce Labs problems are fixed.
  // 'sl-edge': {
  //   base: 'SauceLabs',
  //   browserName: 'microsoftedge',
  //   version: 'latest',
  //   platform: 'Windows 10',
  // },

  /*
   * Safari (desktop)
   */

  // 'sl-safari-stable': {
  //   base: 'SauceLabs',
  //   browserName: 'safari',
  //   version: 'latest',
  //   platform: 'macOS 10.12',
  // },
  // 'sl-safari-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'safari',
  //   version: '9.0',
  //   platform: 'OS X 10.11',
  // },

  /*
   * Safari (mobile)
   */

  'sl-ios-safari-latest': {
    base: 'SauceLabs',
    deviceName: 'iPhone Simulator',
    platformVersion: '10.0',
    platformName: 'iOS',
    browserName: 'Safari',
  },
  // 'sl-ios-safari-previous': {
  //   base: 'SauceLabs',
  //   deviceName: 'iPhone Simulator',
  //   platformVersion: '9.3',
  //   platformName: 'iOS',
  //   browserName: 'Safari',
  // },
};

const getLaunchers = () => USING_SL ? SAUCE_LAUNCHERS : LOCAL_LAUNCHERS;
const getBrowsers = () => USING_TRAVISCI ? Object.keys(getLaunchers()) : ['Chrome'];

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha'],
    files: [
      'test/unit/index.js',
    ],
    preprocessors: {
      'test/unit/index.js': ['webpack', 'sourcemap'],
    },
    reporters: ['dots', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    browsers: getBrowsers(),
    browserDisconnectTimeout: 40000,
    browserNoActivityTimeout: 120000,
    captureTimeout: 240000,
    concurrency: USING_SL ? 4 : Infinity,
    customLaunchers: getLaunchers(),

    coverageReporter: {
      dir: 'coverage',
      reporters: [
        {type: 'lcovonly', subdir: '.'},
        {type: 'json', subdir: '.', file: 'coverage.json'},
        {type: 'html'},
      ],
    },

    client: {
      mocha: {
        reporter: 'html',
        ui: 'qunit',

        // Number of milliseconds to wait for an individual `test(...)` function to complete.
        // The default is 2000.
        timeout: 10000,
      },
    },

    webpack: Object.assign({}, webpackConfig, {
      devtool: 'inline-source-map',
      module: Object.assign({}, webpackConfig.module, {
        // Cover source files when not debugging tests. Otherwise, omit coverage instrumenting to get
        // uncluttered source maps.
        rules: webpackConfig.module.rules.concat([config.singleRun ? {
          test: /\.js$/,
          include: path.resolve('./packages'),
          exclude: [
            /node_modules/,
            /adapter.js/,
          ],
          loader: 'istanbul-instrumenter-loader',
          query: {esModules: true},
        } : undefined]).filter(Boolean),
      }),
    }),

    webpackMiddleware: {
      noInfo: true,
    },
  });

  if (USING_SL) {
    const sauceLabsConfig = {
      username: process.env.SAUCE_USERNAME,
      accessKey: process.env.SAUCE_ACCESS_KEY,
    };

    if (USING_TRAVISCI) {
      // See https://github.com/karma-runner/karma-sauce-launcher/issues/73
      Object.assign(sauceLabsConfig, {
        testName: 'Material Components Web Unit Tests - CI',
        tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
        startConnect: false,
      });
    }

    config.set({
      sauceLabs: sauceLabsConfig,
      // Attempt to de-flake Sauce Labs tests on TravisCI.
      transports: ['polling'],
      browserDisconnectTolerance: 3,
    });
  }
};
<commit_msg>fix(infrastructure): update saucelabs windows 8 to windows 10 IE11 browser (#3234)<commit_after>/**
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const path = require('path');
const webpackConfig = require('./webpack.config')[0];

const USING_TRAVISCI = Boolean(process.env.TRAVIS);
const USING_SL = Boolean(process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY);

const LOCAL_LAUNCHERS = {
  /** See https://github.com/travis-ci/travis-ci/issues/8836#issuecomment-348248951 */
  'ChromeHeadlessNoSandbox': {
    base: 'ChromeHeadless',
    flags: ['--no-sandbox'],
  },
};

const SAUCE_LAUNCHERS = {
  /*
   * Chrome (desktop)
   */

  'sl-chrome-stable': {
    base: 'SauceLabs',
    browserName: 'chrome',
    version: 'latest',
    platform: 'macOS 10.12',
    extendedDebugging: true,
  },
  // 'sl-chrome-beta': {
  //   base: 'SauceLabs',
  //   browserName: 'chrome',
  //   version: 'dev',
  //   platform: 'macOS 10.12',
  //   extendedDebugging: true,
  // },
  // 'sl-chrome-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'chrome',
  //   version: 'latest-1',
  //   platform: 'macOS 10.12',
  //   extendedDebugging: true,
  // },

  /*
   * Firefox
   */

  'sl-firefox-stable': {
    base: 'SauceLabs',
    browserName: 'firefox',
    version: 'latest',
    platform: 'Windows 10',
    extendedDebugging: true,
  },
  // 'sl-firefox-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'firefox',
  //   version: 'latest-1',
  //   platform: 'Windows 10',
  //   extendedDebugging: true,
  // },

  /*
   * IE
   */

  'sl-ie': {
    base: 'SauceLabs',
    browserName: 'internet explorer',
    version: '11',
    platform: 'Windows 10',
  },

  /*
   * Edge
   */

  // TODO(sgomes): Re-enable Edge and Safari after Sauce Labs problems are fixed.
  // 'sl-edge': {
  //   base: 'SauceLabs',
  //   browserName: 'microsoftedge',
  //   version: 'latest',
  //   platform: 'Windows 10',
  // },

  /*
   * Safari (desktop)
   */

  // 'sl-safari-stable': {
  //   base: 'SauceLabs',
  //   browserName: 'safari',
  //   version: 'latest',
  //   platform: 'macOS 10.12',
  // },
  // 'sl-safari-previous': {
  //   base: 'SauceLabs',
  //   browserName: 'safari',
  //   version: '9.0',
  //   platform: 'OS X 10.11',
  // },

  /*
   * Safari (mobile)
   */

  'sl-ios-safari-latest': {
    base: 'SauceLabs',
    deviceName: 'iPhone Simulator',
    platformVersion: '10.0',
    platformName: 'iOS',
    browserName: 'Safari',
  },
  // 'sl-ios-safari-previous': {
  //   base: 'SauceLabs',
  //   deviceName: 'iPhone Simulator',
  //   platformVersion: '9.3',
  //   platformName: 'iOS',
  //   browserName: 'Safari',
  // },
};

const getLaunchers = () => USING_SL ? SAUCE_LAUNCHERS : LOCAL_LAUNCHERS;
const getBrowsers = () => USING_TRAVISCI ? Object.keys(getLaunchers()) : ['Chrome'];

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha'],
    files: [
      'test/unit/index.js',
    ],
    preprocessors: {
      'test/unit/index.js': ['webpack', 'sourcemap'],
    },
    reporters: ['dots', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    browsers: getBrowsers(),
    browserDisconnectTimeout: 40000,
    browserNoActivityTimeout: 120000,
    captureTimeout: 240000,
    concurrency: USING_SL ? 4 : Infinity,
    customLaunchers: getLaunchers(),

    coverageReporter: {
      dir: 'coverage',
      reporters: [
        {type: 'lcovonly', subdir: '.'},
        {type: 'json', subdir: '.', file: 'coverage.json'},
        {type: 'html'},
      ],
    },

    client: {
      mocha: {
        reporter: 'html',
        ui: 'qunit',

        // Number of milliseconds to wait for an individual `test(...)` function to complete.
        // The default is 2000.
        timeout: 10000,
      },
    },

    webpack: Object.assign({}, webpackConfig, {
      devtool: 'inline-source-map',
      module: Object.assign({}, webpackConfig.module, {
        // Cover source files when not debugging tests. Otherwise, omit coverage instrumenting to get
        // uncluttered source maps.
        rules: webpackConfig.module.rules.concat([config.singleRun ? {
          test: /\.js$/,
          include: path.resolve('./packages'),
          exclude: [
            /node_modules/,
            /adapter.js/,
          ],
          loader: 'istanbul-instrumenter-loader',
          query: {esModules: true},
        } : undefined]).filter(Boolean),
      }),
    }),

    webpackMiddleware: {
      noInfo: true,
    },
  });

  if (USING_SL) {
    const sauceLabsConfig = {
      username: process.env.SAUCE_USERNAME,
      accessKey: process.env.SAUCE_ACCESS_KEY,
    };

    if (USING_TRAVISCI) {
      // See https://github.com/karma-runner/karma-sauce-launcher/issues/73
      Object.assign(sauceLabsConfig, {
        testName: 'Material Components Web Unit Tests - CI',
        tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
        startConnect: false,
      });
    }

    config.set({
      sauceLabs: sauceLabsConfig,
      // Attempt to de-flake Sauce Labs tests on TravisCI.
      transports: ['polling'],
      browserDisconnectTolerance: 3,
    });
  }
};
<|endoftext|>


<commit_before>var Latexdown={
  delay: 150,        // delay after keystroke before updating

  previewid: null,
  eventtapid: null,
  preview: null,     // filled in by Init below

  timeout: null,     // store setTimout id
  mjRunning: false,  // true when MathJax is processing
  oldText: null,     // used to check if an update is needed

  Init: function (pid,eid) {
    this.previewid=pid;
    this.eventtabid=eid;
    this.preview=document.getElementById(this.previewid);
    console.log("id1"+this.previewid);
    initial=$('#'+this.preivewid+' a .active');
    this.UpdateTab(initial.attr("data-url"),initial);
  },
  Update: function () {
    if (this.timeout) {clearTimeout(this.timeout)}
    this.timeout = setTimeout(this.callback,this.delay);
  },
  PreviewHtml: function () {
    this.mjRunning = false;
  },
  PreviewMark: function () {
    this.mjRunning = false;
    text = this.preview.innerHTML;
    text = text.replace(/^>/mg, '>');
    this.preview.innerHTML = marked (text);
  },
  IsMarkdown: function(url){
    ext = url.split('.').pop().toLowerCase();
    if ($.inArray(ext, ['md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdtxt', 'mdtext']) == -1) {
        return false;
    }
    else{
        return true;
    }
  },
  UpdateTab: function(url , pane){
    $.get(url, function(data) {
        this.timeout = null;
        if (this.mjRunning) return;
        var text = data;
        if (text === this.oldtext) return;
        console.log("id2:"+this.previewid);
        this.preview=$("#my-pagination-content");
        this.preview.innerHTML = this.oldtext = text;
        this.mjRunning = true;
        if(IsMarkdown(url)){
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.preview],["PreviewMark",this]);
        }
        else{
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.preview],["PreviewHTML",this]);
        }
        makerbutton();
        pane.tab('show');
    });
  }
};
/*Latexdown.callback=MathJax.Callback(["UpdateTab",Latexdown]);
Latexdown.callback.autoReset = true; */
<commit_msg>Update updatetab.js<commit_after>var Latexdown={
  delay: 150,        // delay after keystroke before updating

  previewid: null,
  eventtapid: null,
  preview: null,     // filled in by Init below

  timeout: null,     // store setTimout id
  mjRunning: false,  // true when MathJax is processing
  oldText: null,     // used to check if an update is needed

  Init: function (pid,eid) {
    this.previewid=pid;
    this.eventtabid=eid;
    this.preview=document.getElementById(this.previewid);
    console.log("id1"+this.previewid);
    initial=$('#'+this.preivewid+' a .active');
    this.UpdateTab(initial.attr("data-url"),initial);
  },
  Update: function () {
    if (this.timeout) {clearTimeout(this.timeout)}
    this.timeout = setTimeout(this.callback,this.delay);
  },
  PreviewHtml: function () {
    this.mjRunning = false;
  },
  PreviewMark: function () {
    this.mjRunning = false;
    text = this.preview.innerHTML;
    text = text.replace(/^>/mg, '>');
    this.preview.innerHTML = marked (text);
  },
  IsMarkdown: function(url){
    ext = url.split('.').pop().toLowerCase();
    if ($.inArray(ext, ['md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdtxt', 'mdtext']) == -1) {
        return false;
    }
    else{
        return true;
    }
  },
  UpdateTab: function(url , pane){
    $.get(url, function(data) {
        this.timeout = null;
        if (this.mjRunning) return;
        var text = data;
        if (text === this.oldtext) return;
        console.log("id2:"+this.previewid);
        this.preview=$("#my-pagination-content");
        this.preview.innerHTML = this.oldtext = text;
        this.mjRunning = true;
        if(this.IsMarkdown(url)){
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.preview],["PreviewMark",this]);
        }
        else{
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.preview],["PreviewHTML",this]);
        }
        makerbutton();
        pane.tab('show');
    });
  }
};
/*Latexdown.callback=MathJax.Callback(["UpdateTab",Latexdown]);
Latexdown.callback.autoReset = true; */
<|endoftext|>


<commit_before>module.exports = function (config) {
  config.set({
    // base path used to resolve all patterns
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai'],

    // list of files/patterns to load in the browser
    files: [{ pattern: 'spec.bundle.js', watched: false }],

    // files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: { 'spec.bundle.js': ['webpack', 'sourcemap'] },

    webpack: {
      devtool: 'inline-source-map',
      module: {
        loaders: [
          { test: /\.js/, exclude: [/app\/lib/, /node_modules/], loader: 'babel' },
          { test: /\.html/, loader: 'raw' },
          { test: /\.styl$/, loader: 'style!css!stylus' },
          { test: /\.css$/, loader: 'style!css' }
        ]
      }
    },

    webpackServer: {
      noInfo: true // prevent console spamming when running in Karma!
    },

    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['mocha'],

    // web server port
    port: 9876,

    // enable colors in the output
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // toggle whether to watch files and rerun tests upon incurring changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // if true, Karma runs tests once and exits
    singleRun: true
  });
};
<commit_msg>Fix karma start, so a user can launch test via cli<commit_after>module.exports = function (config) {
  config.set({
    // base path used to resolve all patterns
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai'],

    // list of files/patterns to load in the browser
    files: [{ pattern: 'spec.bundle.js', watched: false }],

    // files to exclude
    exclude: [],

    plugins: [
      require("karma-chai"),
      require("karma-chrome-launcher"),
      require("karma-mocha"),
      require("karma-mocha-reporter"),
      require("karma-sourcemap-loader"),
      require("karma-webpack")
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: { 'spec.bundle.js': ['webpack', 'sourcemap'] },

    webpack: {
      devtool: 'inline-source-map',
      module: {
        loaders: [
          { test: /\.js/, exclude: [/app\/lib/, /node_modules/], loader: 'babel' },
          { test: /\.html/, loader: 'raw' },
          { test: /\.styl$/, loader: 'style!css!stylus' },
          { test: /\.css$/, loader: 'style!css' }
        ]
      }
    },

    webpackServer: {
      noInfo: true // prevent console spamming when running in Karma!
    },

    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['mocha'],

    // web server port
    port: 9876,

    // enable colors in the output
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // toggle whether to watch files and rerun tests upon incurring changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // if true, Karma runs tests once and exits
    singleRun: true
  });
};
<|endoftext|>


<commit_before>var player;
var currentUrl = '';

$(function() {
  player = new CastPlayer();
});

function launchApp() {
  player.launchApp();
}

function getContentType(url) {
  var ext = url.split('.').pop();
  var formats = [
    {ext: 'mkv', type: 'video'},
    {ext: 'webm', type: 'video'},
    {ext: 'mp4', type: 'video'},
    {ext: 'm4v', type: 'video'},
    {ext: 'm4a', type: 'audio'},
    {ext: 'jpeg', type: 'image'},
    {ext: 'jpg', type: 'image'},
    {ext: 'gif', type: 'image'},
    {ext: 'png', type: 'image'},
    {ext: 'bmp', type: 'image'},
    {ext: 'webp', type: 'image'}
  ];
  for (var i = 0; i < formats.length; i++) {
    if (formats[i].ext == ext) {
      return formats[i].type + "/" + ext;
    }
  }
  // it doesn't matter now, as it's unsupported.
  return "";
}

function startPlayback() {
  if (player.session == null || $('#url').val().trim() == "") {
    return;
  }
  var url = decodeURIComponent($('#url').val());
  var contentType = getContentType(url);
  player.loadMedia(url, contentType);
  $('#player_now_playing').html(url.split(/[\\/]/).pop());
  $('#controls').show();
}

function pause() {
  if (player.session != null) {
    player.pauseMedia();
  }
}

function resume() {
  if (player.session != null) {
    player.playMedia();
  }
}

function seek(is_forward) {
  if (player.session != null) {
    player.seekMedia(parseInt($("#player_seek").val()), is_forward);
  }
}

function stop() {
  var reply = confirm("This will stop playback on the TV. Are you sure?");
  if (reply == true) {
    player.stopApp();
    $('#controls').hide();
  }
}

function volumeDown() {
  if (player.session != null) {
      player.volumeControl(false, false);
  }
}

function volumeUp() {
  if (player.session != null) {
    player.volumeControl(true, false);
  }
}

function volumeMute() {
  if (player.session != null) {
    player.volumeControl(false, true);
  }
}<commit_msg>Enableling url to be populated via URL<commit_after>var player;
var currentUrl = '';

$(function() {
  player = new CastPlayer();
  var url = getUrlParameter("url")
  if (url) {
    $('#url').val(url);
  }
});

function getUrlParameter(sParam) {
  var sPageURL = window.location.search.substring(1);
  var sURLVariables = sPageURL.split('&');
  for (var i = 0; i < sURLVariables.length; i++)  {
    var sParameterName = sURLVariables[i].split('=');
    if (sParameterName[0] == sParam) {
      return decodeURIComponent(sParameterName[1]);
    }
  }
}

function launchApp() {
  player.launchApp();
}

function getContentType(url) {
  var ext = url.split('.').pop();
  var formats = [
    {ext: 'mkv', type: 'video'},
    {ext: 'webm', type: 'video'},
    {ext: 'mp4', type: 'video'},
    {ext: 'm4v', type: 'video'},
    {ext: 'm4a', type: 'audio'},
    {ext: 'jpeg', type: 'image'},
    {ext: 'jpg', type: 'image'},
    {ext: 'gif', type: 'image'},
    {ext: 'png', type: 'image'},
    {ext: 'bmp', type: 'image'},
    {ext: 'webp', type: 'image'}
  ];
  for (var i = 0; i < formats.length; i++) {
    if (formats[i].ext == ext) {
      return formats[i].type + "/" + ext;
    }
  }
  // it doesn't matter now, as it's unsupported.
  return "";
}

function startPlayback() {
  if (player.session == null || $('#url').val().trim() == "") {
    return;
  }
  var url = decodeURIComponent($('#url').val());
  var contentType = getContentType(url);
  player.loadMedia(url, contentType);
  $('#player_now_playing').html(url.split(/[\\/]/).pop());
  $('#controls').show();
}

function pause() {
  if (player.session != null) {
    player.pauseMedia();
  }
}

function resume() {
  if (player.session != null) {
    player.playMedia();
  }
}

function seek(is_forward) {
  if (player.session != null) {
    player.seekMedia(parseInt($("#player_seek").val()), is_forward);
  }
}

function stop() {
  var reply = confirm("This will stop playback on the TV. Are you sure?");
  if (reply == true) {
    player.stopApp();
    $('#controls').hide();
  }
}

function volumeDown() {
  if (player.session != null) {
      player.volumeControl(false, false);
  }
}

function volumeUp() {
  if (player.session != null) {
    player.volumeControl(true, false);
  }
}

function volumeMute() {
  if (player.session != null) {
    player.volumeControl(false, true);
  }
}
<|endoftext|>


<commit_before>'use strict';

var async               = require('async');
var searchAndMapResults = require('./search_and_map_results');
var bucketNameComposer  = require('../private/bucket_name_composer');
var conditionsToIds     = require('../private/conditions_to_ids');

/**
 * Update all matching instances
 * @param {String} model The model name
 * @param {Object} where The search criteria
 * @param {Object} data The property/value pairs to be updated
 * @callback {Function} apiCallback Callback function
 */
module.exports = function(model, where, data, apiCallback){
  var self       = this;
  var conditions = { where: where };
  var bucketName = bucketNameComposer(this, modelName);
  var ids;

  // Loopback has this strange behavior of always querying using 'all'
  // even when searching for just one document. Since we have to use
  // the search unnecessarily if the id is provided, we're going to
  // just use the 'destroy' method if we've determined that we're
  // simply destroying a document by id
  if (isQueryingJustById()){
    updateMany();
  } else {
    search();
  }

  function search(){
    self.searchAndMapResults(bucketName, conditions, function(modelName, id, mapCallback){
      self.updateAttributes(modelName, id, data, mapCallback);
    }, apiCallback);
  }

  function isQueryingJustById(){
    ids = conditionsToIds(conditions);
    return ids && ids.length !== 0;
  }

  // map the ids to parallel update-by-id queries
  function updateMany(){
    async.map(ids, updateOne, apiCallback);
  }

  function updateOne(id, callback){
    self.destroy(modelName, id, callback);
  }
};
<commit_msg>hey maybe we should update the document instead of delete the document. kinda different<commit_after>'use strict';

var async               = require('async');
var searchAndMapResults = require('./search_and_map_results');
var bucketNameComposer  = require('../private/bucket_name_composer');
var conditionsToIds     = require('../private/conditions_to_ids');

/**
 * Update all matching instances
 * @param {String} model The model name
 * @param {Object} where The search criteria
 * @param {Object} data The property/value pairs to be updated
 * @callback {Function} apiCallback Callback function
 */
module.exports = function(modelName, where, data, apiCallback){
  var self       = this;
  var conditions = { where: where };
  var bucketName = bucketNameComposer(this, modelName);
  var ids;

  // Loopback has this strange behavior of always querying using 'all'
  // even when searching for just one document. Since we have to use
  // the search unnecessarily if the id is provided, we're going to
  // just use the 'destroy' method if we've determined that we're
  // simply destroying a document by id
  if (isQueryingJustById()){
    updateMany();
  } else {
    search();
  }

  function search(){
    self.searchAndMapResults(bucketName, conditions, function(modelName, id, mapCallback){
      self.updateAttributes(modelName, id, data, mapCallback);
    }, apiCallback);
  }

  function isQueryingJustById(){
    ids = conditionsToIds(conditions);
    return ids && ids.length !== 0;
  }

  // map the ids to parallel update-by-id queries
  function updateMany(){
    async.map(ids, updateOne, apiCallback);
  }

  function updateOne(id, callback){
    self.updateAttributes(modelName, id, callback);
  }
};
<|endoftext|>


<commit_before>/*global module*/
module.exports = function(config) {
    config.set({
        basePath: '',

        frameworks: ['jspm', 'mocha', 'sinon-chai', 'phantomjs-shim'],

        // only load polyfill, the remaining files will be loaded by jspm
        files: [
            'node_modules/karma-babel-preprocessor/node_modules/babel-core/browser-polyfill.js'
        ],

        // list of files to exclude
        exclude: [],

        // preprocess matching files before serving them to the browser
        preprocessors: {
            'public/js/**/*(!spec).js': ['babel', 'coverage'],
            'public/js/**/*.spec.js': ['babel']
        },

        babelPreprocessor: {
            options: {
                modules: 'system',
                sourceMap: 'inline'
            }
        },

        jspm: {
            loadFiles: ['public/js/**/*.spec.js'],
            serveFiles: ['public/js/**/*.js'],
                'public/test/**/*.spec.js',
                'public/js/**/*.js'
            ],
            config: 'jspm_config.js',
            packages: 'jspm_packages'
        },

        proxies: {
        },

        plugins: [
            'karma-junit-reporter',
            'karma-coverage',
            'karma-mocha',
            'karma-sinon-chai',
            'karma-coverage',
            'karma-jspm',
            'karma-phantomjs-launcher',
            'karma-phantomjs-shim',
            'karma-mocha-reporter',
            'karma-babel-preprocessor'
        ],

        // test results reporter to use
        reporters: [
            'mocha',
            'junit',
            'coverage'
        ],

        junitReporter: {
            outputFile: 'reports/test-units.xml',
            suite: ''
        },

        coverageReporter: {
            dir : 'reports/',
            reporters: [
                { type: 'html', subdir: 'report-html' },
                { type: 'cobertura', subdir: '.', file: 'cobertura.xml' }
            ]
        },

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};
<commit_msg>fixed served files<commit_after>/*global module*/
module.exports = function(config) {
    config.set({
        basePath: '',

        frameworks: ['jspm', 'mocha', 'sinon-chai', 'phantomjs-shim'],

        // only load polyfill, the remaining files will be loaded by jspm
        files: [
            'node_modules/karma-babel-preprocessor/node_modules/babel-core/browser-polyfill.js'
        ],

        // list of files to exclude
        exclude: [],

        // preprocess matching files before serving them to the browser
        preprocessors: {
            'public/js/**/*(!spec).js': ['babel', 'coverage'],
            'public/js/**/*.spec.js': ['babel']
        },

        babelPreprocessor: {
            options: {
                modules: 'system',
                sourceMap: 'inline'
            }
        },

        jspm: {
            loadFiles: ['public/js/**/*.spec.js'],
            serveFiles: ['public/js/**/*.js'],
            config: 'jspm_config.js',
            packages: 'jspm_packages'
        },

        proxies: {
        },

        plugins: [
            'karma-junit-reporter',
            'karma-coverage',
            'karma-mocha',
            'karma-sinon-chai',
            'karma-coverage',
            'karma-jspm',
            'karma-phantomjs-launcher',
            'karma-phantomjs-shim',
            'karma-mocha-reporter',
            'karma-babel-preprocessor'
        ],

        // test results reporter to use
        reporters: [
            'mocha',
            'junit',
            'coverage'
        ],

        junitReporter: {
            outputFile: 'reports/test-units.xml',
            suite: ''
        },

        coverageReporter: {
            dir : 'reports/',
            reporters: [
                { type: 'html', subdir: 'report-html' },
                { type: 'cobertura', subdir: '.', file: 'cobertura.xml' }
            ]
        },

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};
<|endoftext|>