<commit_before>var animationManager = (function(){
    var moduleOb = {};
    var registeredAnimations = [];
    var initTime = 0;
    var len = 0;
    var idled = true;
    var playingAnimationsNum = 0;

    function removeElement(ev){
        var i = 0;
        var animItem = ev.target;
        while(i<len) {
            if (registeredAnimations[i].animation === animItem) {
                registeredAnimations.splice(i, 1);
                i -= 1;
                len -= 1;
                subtractPlayingCount();
            }
            i += 1;
        }
    }

    function registerAnimation(element, animationData){
        if(!element){
            return null;
        }
        var i=0;
        while(i<len){
            if(registeredAnimations[i].elem == element && registeredAnimations[i].elem !== null ){
                return registeredAnimations[i].animation;
            }
            i+=1;
        }
        var animItem = new AnimationItem();
        animItem.setData(element, animationData);
        setupAnimation(animItem, element);
        return animItem;
    }

    function addPlayingCount(){
        playingAnimationsNum += 1;
        activate();
    }

    function subtractPlayingCount(){
        playingAnimationsNum -= 1;
        if(playingAnimationsNum === 0){
            idled = true;
        }
    }

    function setupAnimation(animItem, element){
        animItem.addEventListener('destroy',removeElement);
        animItem.addEventListener('_active',addPlayingCount);
        animItem.addEventListener('_idle',subtractPlayingCount);
        registeredAnimations.push({elem: element,animation:animItem});
        len += 1;
    }

    function loadAnimation(params){
        var animItem = new AnimationItem();
        animItem.setParams(params);
        setupAnimation(animItem, null);
        return animItem;
    }

    function setSpeed(val,animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.setSpeed(val, animation);
        }
    }

    function setDirection(val, animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.setDirection(val, animation);
        }
    }

    function play(animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.play(animation);
        }
    }

    function moveFrame (value, animation) {
        initTime = Date.now();
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.moveFrame(value,animation);
        }
    }

    function resume(nowTime) {

        var elapsedTime = nowTime - initTime;
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.advanceTime(elapsedTime);
        }
        initTime = nowTime;
        if(!idled) {
            requestAnimationFrame(resume);
        }
    }

    function first(nowTime){
        initTime = nowTime;
        requestAnimationFrame(resume);
    }

    function pause(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.pause(animation);
        }
    }

    function goToAndStop(value,isFrame,animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.goToAndStop(value,isFrame,animation);
        }
    }

    function stop(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.stop(animation);
        }
    }

    function togglePause(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.togglePause(animation);
        }
    }

    function destroy(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.destroy(animation);
        }
    }

    function searchAnimations(animationData, standalone, renderer){
        var animElements = document.getElementsByClassName('bodymovin');
        var i, len = animElements.length;
        for(i=0;i<len;i+=1){
            if(renderer){
                animElements[i].setAttribute('data-bm-type',renderer);
            }
            registerAnimation(animElements[i], animationData);
        }
        if(standalone && len === 0){
            if(!renderer){
                renderer = 'svg';
            }
            var body = document.getElementsByTagName('body')[0];
            body.innerHTML = '';
            var div = document.createElement('div');
            div.style.width = '100%';
            div.style.height = '100%';
            div.setAttribute('data-bm-type',renderer);
            body.appendChild(div);
            registerAnimation(div, animationData);
        }
    }

    function resize(){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.resize();
        }
    }

    function start(){
        requestAnimationFrame(first);
    }

    function activate(){
        if(idled){
            idled = false;
            if(Performance && Performance.now){
                first(Performance.now);
            } else {
                requestAnimationFrame(first);
            }
        }
    }

    //start();

    setTimeout(start,0);

    moduleOb.registerAnimation = registerAnimation;
    moduleOb.loadAnimation = loadAnimation;
    moduleOb.setSpeed = setSpeed;
    moduleOb.setDirection = setDirection;
    moduleOb.play = play;
    moduleOb.moveFrame = moveFrame;
    moduleOb.pause = pause;
    moduleOb.stop = stop;
    moduleOb.togglePause = togglePause;
    moduleOb.searchAnimations = searchAnimations;
    moduleOb.resize = resize;
    moduleOb.start = start;
    moduleOb.goToAndStop = goToAndStop;
    moduleOb.destroy = destroy;
    return moduleOb;
}());<commit_msg>addind listeners to anim before params<commit_after>var animationManager = (function(){
    var moduleOb = {};
    var registeredAnimations = [];
    var initTime = 0;
    var len = 0;
    var idled = true;
    var playingAnimationsNum = 0;

    function removeElement(ev){
        var i = 0;
        var animItem = ev.target;
        while(i<len) {
            if (registeredAnimations[i].animation === animItem) {
                registeredAnimations.splice(i, 1);
                i -= 1;
                len -= 1;
                subtractPlayingCount();
            }
            i += 1;
        }
    }

    function registerAnimation(element, animationData){
        if(!element){
            return null;
        }
        var i=0;
        while(i<len){
            if(registeredAnimations[i].elem == element && registeredAnimations[i].elem !== null ){
                return registeredAnimations[i].animation;
            }
            i+=1;
        }
        var animItem = new AnimationItem();
        animItem.setData(element, animationData);
        setupAnimation(animItem, element);
        return animItem;
    }

    function addPlayingCount(){
        playingAnimationsNum += 1;
        activate();
    }

    function subtractPlayingCount(){
        playingAnimationsNum -= 1;
        if(playingAnimationsNum === 0){
            idled = true;
        }
    }

    function setupAnimation(animItem, element){
        animItem.addEventListener('destroy',removeElement);
        animItem.addEventListener('_active',addPlayingCount);
        animItem.addEventListener('_idle',subtractPlayingCount);
        registeredAnimations.push({elem: element,animation:animItem});
        len += 1;
    }

    function loadAnimation(params){
        var animItem = new AnimationItem();
        setupAnimation(animItem, null);
        animItem.setParams(params);
        return animItem;
    }

    function setSpeed(val,animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.setSpeed(val, animation);
        }
    }

    function setDirection(val, animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.setDirection(val, animation);
        }
    }

    function play(animation){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.play(animation);
        }
    }

    function moveFrame (value, animation) {
        initTime = Date.now();
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.moveFrame(value,animation);
        }
    }

    function resume(nowTime) {

        var elapsedTime = nowTime - initTime;
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.advanceTime(elapsedTime);
        }
        initTime = nowTime;
        if(!idled) {
            requestAnimationFrame(resume);
        }
    }

    function first(nowTime){
        initTime = nowTime;
        requestAnimationFrame(resume);
    }

    function pause(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.pause(animation);
        }
    }

    function goToAndStop(value,isFrame,animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.goToAndStop(value,isFrame,animation);
        }
    }

    function stop(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.stop(animation);
        }
    }

    function togglePause(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.togglePause(animation);
        }
    }

    function destroy(animation) {
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.destroy(animation);
        }
    }

    function searchAnimations(animationData, standalone, renderer){
        var animElements = document.getElementsByClassName('bodymovin');
        var i, len = animElements.length;
        for(i=0;i<len;i+=1){
            if(renderer){
                animElements[i].setAttribute('data-bm-type',renderer);
            }
            registerAnimation(animElements[i], animationData);
        }
        if(standalone && len === 0){
            if(!renderer){
                renderer = 'svg';
            }
            var body = document.getElementsByTagName('body')[0];
            body.innerHTML = '';
            var div = document.createElement('div');
            div.style.width = '100%';
            div.style.height = '100%';
            div.setAttribute('data-bm-type',renderer);
            body.appendChild(div);
            registerAnimation(div, animationData);
        }
    }

    function resize(){
        var i;
        for(i=0;i<len;i+=1){
            registeredAnimations[i].animation.resize();
        }
    }

    function start(){
        requestAnimationFrame(first);
    }

    function activate(){
        if(idled){
            idled = false;
            if(Performance && Performance.now){
                first(Performance.now);
            } else {
                requestAnimationFrame(first);
            }
        }
    }

    //start();

    setTimeout(start,0);

    moduleOb.registerAnimation = registerAnimation;
    moduleOb.loadAnimation = loadAnimation;
    moduleOb.setSpeed = setSpeed;
    moduleOb.setDirection = setDirection;
    moduleOb.play = play;
    moduleOb.moveFrame = moveFrame;
    moduleOb.pause = pause;
    moduleOb.stop = stop;
    moduleOb.togglePause = togglePause;
    moduleOb.searchAnimations = searchAnimations;
    moduleOb.resize = resize;
    moduleOb.start = start;
    moduleOb.goToAndStop = goToAndStop;
    moduleOb.destroy = destroy;
    return moduleOb;
}());<|endoftext|>


<commit_before>"use strict";

var Pouch = function Pouch(name, opts, callback) {

  if (!(this instanceof Pouch)) {
    return new Pouch(name, opts, callback);
  }

  if (typeof opts === 'function' || typeof opts === 'undefined') {
    callback = opts;
    opts = {};
  }

  var backend = Pouch.parseAdapter(opts.name || name);
  opts.name = backend.name;
  opts.adapter = opts.adapter || backend.adapter;

  if (!Pouch.adapters[backend.adapter]) {
    throw 'Adapter is missing';
  }

  if (!Pouch.adapters[backend.adapter].valid()) {
    throw 'Invalid Adapter';
  }

  var adapter = Pouch.adapters[backend.adapter](opts, function(err, db) {
    if (err) {
      if (callback) callback(err);
      return;
    }
    for (var plugin in Pouch.plugins) {
      // In future these will likely need to be async to allow the plugin
      // to initialise
      var pluginObj = Pouch.plugins[plugin](db);
      for (var api in pluginObj) {
        // We let things like the http adapter use its own implementation
        // as it shares a lot of code
        if (!(api in db)) {
          db[api] = pluginObj[api];
        }
      }
    }
    callback(null, db);
  });
  for (var j in adapter) {
    this[j] = adapter[j];
  }
};

Pouch.DEBUG = false;

Pouch.adapters = {};
Pouch.plugins = {};

Pouch.parseAdapter = function(name) {

  var match = name.match(/([a-z\-]*):\/\/(.*)/);
  if (match) {
    // the http adapter expects the fully qualified name
    name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2];
    var adapter = match[1];
    if (!Pouch.adapters[adapter].valid()) {
      throw 'Invalid adapter';
    }
    return {name: name, adapter: match[1]};
  }

  var rank = {'idb': 1, 'leveldb': 2, 'websql': 3, 'http': 4, 'https': 4};
  var rankedAdapter = Object.keys(Pouch.adapters).sort(function(a, b) {
    return rank[a] - rank[b];
  })[0];

  return {
    name: name,
    adapter: rankedAdapter
  };

  throw 'No Valid Adapter.';
};

Pouch.destroy = function(name, callback) {
  for (var plugin in Pouch.plugins) {
    Pouch.plugins[plugin]._delete(name);
  }
  var opts = Pouch.parseAdapter(name);
  Pouch.adapters[opts.adapter].destroy(opts.name, callback);
};

Pouch.adapter = function (id, obj) {
  if (obj.valid()) {
    Pouch.adapters[id] = obj;
  }
};

Pouch.plugin = function(id, obj) {
  Pouch.plugins[id] = obj;
};

// Enumerate errors, add the status code so we can reflect the HTTP api
// in future
Pouch.Errors = {
  MISSING_BULK_DOCS: {
    status: 400,
    error: 'bad_request',
    reason: "Missing JSON list of 'docs'"
  },
  MISSING_DOC: {
    status: 404,
    error: 'not_found',
    reason: 'missing'
  },
  REV_CONFLICT: {
    status: 409,
    error: 'conflict',
    reason: 'Document update conflict'
  },
  INVALID_ID: {
    status: 400,
    error: 'invalid_id',
    reason: '_id field must contain a string'
  },
  MISSING_ID: {
    status: 412,
    error: 'missing_id',
    reason: '_id is required for puts'
  },
  RESERVED_ID: {
    status: 400,
    error: 'bad_request',
    reason: 'Only reserved document ids may start with underscore.'
  },
  NOT_OPEN: {
    status: 412,
    error: 'precondition_failed',
    reason: 'Database not open so cannot close'
  },
  UNKNOWN_ERROR: {
    status: 500,
    error: 'unknown_error',
    reason: 'Database encountered an unknown error'
  }
};

if (typeof module !== 'undefined' && module.exports) {
  global.Pouch = Pouch;
  Pouch.merge = require('./pouch.merge.js').merge;
  Pouch.collate = require('./pouch.collate.js').collate;
  Pouch.replicate = require('./pouch.replicate.js').replicate;
  Pouch.utils = require('./pouch.utils.js');
  module.exports = Pouch;

  // load adapters known to work under node
  var adapters = ['leveldb', 'http'];
  adapters.map(function(adapter) {
    var adapter_path = './adapters/pouch.'+adapter+'.js';
    require(adapter_path);
  });
  require('./plugins/pouchdb.mapreduce.js');
} else {
  this.Pouch = Pouch;
}
<commit_msg>Read config from supplied opts, if specified<commit_after>"use strict";

var Pouch = function Pouch(name, opts, callback) {

  if (!(this instanceof Pouch)) {
    return new Pouch(name, opts, callback);
  }

  if (typeof opts === 'function' || typeof opts === 'undefined') {
    callback = opts;
    opts = {};
  }

  if (typeof name === 'object') {
    opts = name;
    name = undefined;
  }

  var backend = Pouch.parseAdapter(opts.name || name);
  opts.name = opts.name || backend.name;
  opts.adapter = opts.adapter || backend.adapter;

  if (!Pouch.adapters[opts.adapter]) {
    throw 'Adapter is missing';
  }

  if (!Pouch.adapters[opts.adapter].valid()) {
    throw 'Invalid Adapter';
  }

  var adapter = Pouch.adapters[opts.adapter](opts, function(err, db) {
    if (err) {
      if (callback) callback(err);
      return;
    }
    for (var plugin in Pouch.plugins) {
      // In future these will likely need to be async to allow the plugin
      // to initialise
      var pluginObj = Pouch.plugins[plugin](db);
      for (var api in pluginObj) {
        // We let things like the http adapter use its own implementation
        // as it shares a lot of code
        if (!(api in db)) {
          db[api] = pluginObj[api];
        }
      }
    }
    callback(null, db);
  });
  for (var j in adapter) {
    this[j] = adapter[j];
  }
};

Pouch.DEBUG = false;

Pouch.adapters = {};
Pouch.plugins = {};

Pouch.parseAdapter = function(name) {

  var match = name.match(/([a-z\-]*):\/\/(.*)/);
  if (match) {
    // the http adapter expects the fully qualified name
    name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2];
    var adapter = match[1];
    if (!Pouch.adapters[adapter].valid()) {
      throw 'Invalid adapter';
    }
    return {name: name, adapter: match[1]};
  }

  var rank = {'idb': 1, 'leveldb': 2, 'websql': 3, 'http': 4, 'https': 4};
  var rankedAdapter = Object.keys(Pouch.adapters).sort(function(a, b) {
    return rank[a] - rank[b];
  })[0];

  return {
    name: name,
    adapter: rankedAdapter
  };

  throw 'No Valid Adapter.';
};

Pouch.destroy = function(name, callback) {
  for (var plugin in Pouch.plugins) {
    Pouch.plugins[plugin]._delete(name);
  }
  var opts = Pouch.parseAdapter(name);
  Pouch.adapters[opts.adapter].destroy(opts.name, callback);
};

Pouch.adapter = function (id, obj) {
  if (obj.valid()) {
    Pouch.adapters[id] = obj;
  }
};

Pouch.plugin = function(id, obj) {
  Pouch.plugins[id] = obj;
};

// Enumerate errors, add the status code so we can reflect the HTTP api
// in future
Pouch.Errors = {
  MISSING_BULK_DOCS: {
    status: 400,
    error: 'bad_request',
    reason: "Missing JSON list of 'docs'"
  },
  MISSING_DOC: {
    status: 404,
    error: 'not_found',
    reason: 'missing'
  },
  REV_CONFLICT: {
    status: 409,
    error: 'conflict',
    reason: 'Document update conflict'
  },
  INVALID_ID: {
    status: 400,
    error: 'invalid_id',
    reason: '_id field must contain a string'
  },
  MISSING_ID: {
    status: 412,
    error: 'missing_id',
    reason: '_id is required for puts'
  },
  RESERVED_ID: {
    status: 400,
    error: 'bad_request',
    reason: 'Only reserved document ids may start with underscore.'
  },
  NOT_OPEN: {
    status: 412,
    error: 'precondition_failed',
    reason: 'Database not open so cannot close'
  },
  UNKNOWN_ERROR: {
    status: 500,
    error: 'unknown_error',
    reason: 'Database encountered an unknown error'
  }
};

if (typeof module !== 'undefined' && module.exports) {
  global.Pouch = Pouch;
  Pouch.merge = require('./pouch.merge.js').merge;
  Pouch.collate = require('./pouch.collate.js').collate;
  Pouch.replicate = require('./pouch.replicate.js').replicate;
  Pouch.utils = require('./pouch.utils.js');
  module.exports = Pouch;

  // load adapters known to work under node
  var adapters = ['leveldb', 'http'];
  adapters.map(function(adapter) {
    var adapter_path = './adapters/pouch.'+adapter+'.js';
    require(adapter_path);
  });
  require('./plugins/pouchdb.mapreduce.js');
} else {
  this.Pouch = Pouch;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 Thiago Bellini <hackedbellini@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Most of this code was forked from media-player-indicator:
 *   https://extensions.gnome.org/extension/55/media-player-indicator/
 *
 */

const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const Gettext = imports.gettext.domain('gnome-shell-notifications-alert');
const _ = Gettext.gettext;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Lib = Me.imports.lib;

let settings;
let boolSettings;
let intSettings;
let colorSettings;

function _createBoolSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: boolSettings[setting].label,
                                    xalign: 0});

  let settingSwitch = new Gtk.Switch({active: settings.get_boolean(setting)});
  settingSwitch.connect('notify::active', function(button) {
    settings.set_boolean(setting, button.active);
  });

  if (boolSettings[setting].help) {
    settingLabel.set_tooltip_text(boolSettings[setting].help);
    settingSwitch.set_tooltip_text(boolSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(settingSwitch);

  return hbox;
}

function _createIntSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: intSettings[setting].label,
                                    xalign: 0});

  let spinButton = new Gtk.SpinButton.new_with_range(
    intSettings[setting].min,
    intSettings[setting].max,
    intSettings[setting].step)
  spinButton.set_value(settings.get_int(setting));
  spinButton.connect('notify::value', function(spin) {
    settings.set_int(setting, spin.get_value_as_int());
  });

  if (intSettings[setting].help) {
    settingLabel.set_tooltip_text(intSettings[setting].help);
    spinButton.set_tooltip_text(intSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(spinButton);

  return hbox;
}

function _createColorSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: colorSettings[setting].label,
                                    xalign: 0});

  let color = Lib.getColorByHexadecimal(settings.get_string(setting));
  let colorButton = new Gtk.ColorButton();
  colorButton.set_color(color);
  colorButton.connect('notify::color', function(button) {
    let hex = Lib.getHexadecimalByColor(button.get_color());
    settings.set_string(setting, hex);
  });

  if (colorSettings[setting].help) {
    settingLabel.set_tooltip_text(colorSettings[setting].help);
    colorButton.set_tooltip_text(colorSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(colorButton);

  return hbox;
}

/*
   Shell-extensions handlers
*/

function init() {
  Lib.initTranslations(Me);
  settings = Lib.getSettings(Me);

  colorSettings = {
    color: {
      label: _("Alert color"),
      help: _("The color used to paint the message on user's menu")
    },
  };

  intSettings = {
    blinkrate: {
      label: _("Blink rate"),
      help: _("The rate that the alert blinks, in ms. 0 means no blink (default: 800)"),
      min: 0,
      max: 10000,
      step: 1
    },
  };

  boolSettings = {
    chatonly: {
      label: _("Only alert for chat notifications"),
      help: _("Only chat notifications (like Empathy ones) will get alerted (default: OFF)")
    },
    force: {
      label: _("Force alerting even when notifications are set to OFF"),
      help: _("Alert even if you set notifications to OFF on user menu (default: OFF)")
    },
  };
}

function buildPrefsWidget() {
  let frame = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL,
                           border_width: 10});
  let vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL,
                          margin: 20, margin_top: 10});

  // Add all color settings
  for (setting in colorSettings) {
    let hbox = _createColorSetting(setting);
    vbox.add(hbox);
  }
  // Add all bool settings
  for (setting in boolSettings) {
    let hbox = _createBoolSetting(setting);
    vbox.add(hbox);
  }
  // Add all int settings
  for (setting in intSettings) {
    let hbox = _createIntSetting(setting);
    vbox.add(hbox);
  }

  frame.add(vbox);
  frame.show_all();

  return frame;
}
<commit_msg>Be more explicative on blink label<commit_after>/*
 * Copyright (C) 2012 Thiago Bellini <hackedbellini@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Most of this code was forked from media-player-indicator:
 *   https://extensions.gnome.org/extension/55/media-player-indicator/
 *
 */

const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const Gettext = imports.gettext.domain('gnome-shell-notifications-alert');
const _ = Gettext.gettext;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Lib = Me.imports.lib;

let settings;
let boolSettings;
let intSettings;
let colorSettings;

function _createBoolSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: boolSettings[setting].label,
                                    xalign: 0});

  let settingSwitch = new Gtk.Switch({active: settings.get_boolean(setting)});
  settingSwitch.connect('notify::active', function(button) {
    settings.set_boolean(setting, button.active);
  });

  if (boolSettings[setting].help) {
    settingLabel.set_tooltip_text(boolSettings[setting].help);
    settingSwitch.set_tooltip_text(boolSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(settingSwitch);

  return hbox;
}

function _createIntSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: intSettings[setting].label,
                                    xalign: 0});

  let spinButton = new Gtk.SpinButton.new_with_range(
    intSettings[setting].min,
    intSettings[setting].max,
    intSettings[setting].step)
  spinButton.set_value(settings.get_int(setting));
  spinButton.connect('notify::value', function(spin) {
    settings.set_int(setting, spin.get_value_as_int());
  });

  if (intSettings[setting].help) {
    settingLabel.set_tooltip_text(intSettings[setting].help);
    spinButton.set_tooltip_text(intSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(spinButton);

  return hbox;
}

function _createColorSetting(setting) {
  let hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL});

  let settingLabel = new Gtk.Label({label: colorSettings[setting].label,
                                    xalign: 0});

  let color = Lib.getColorByHexadecimal(settings.get_string(setting));
  let colorButton = new Gtk.ColorButton();
  colorButton.set_color(color);
  colorButton.connect('notify::color', function(button) {
    let hex = Lib.getHexadecimalByColor(button.get_color());
    settings.set_string(setting, hex);
  });

  if (colorSettings[setting].help) {
    settingLabel.set_tooltip_text(colorSettings[setting].help);
    colorButton.set_tooltip_text(colorSettings[setting].help);
  }

  hbox.pack_start(settingLabel, true, true, 0);
  hbox.add(colorButton);

  return hbox;
}

/*
   Shell-extensions handlers
*/

function init() {
  Lib.initTranslations(Me);
  settings = Lib.getSettings(Me);

  colorSettings = {
    color: {
      label: _("Alert color"),
      help: _("The color used to paint the message on user's menu")
    },
  };

  intSettings = {
    blinkrate: {
      label: _("Blink rate (in ms)"),
      help: _("The rate that the alert blinks, in ms. 0 means no blink (default: 800)"),
      min: 0,
      max: 10000,
      step: 1
    },
  };

  boolSettings = {
    chatonly: {
      label: _("Only alert for chat notifications"),
      help: _("Only chat notifications (like Empathy ones) will get alerted (default: OFF)")
    },
    force: {
      label: _("Force alerting even when notifications are set to OFF"),
      help: _("Alert even if you set notifications to OFF on user menu (default: OFF)")
    },
  };
}

function buildPrefsWidget() {
  let frame = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL,
                           border_width: 10});
  let vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL,
                          margin: 20, margin_top: 10});

  // Add all color settings
  for (setting in colorSettings) {
    let hbox = _createColorSetting(setting);
    vbox.add(hbox);
  }
  // Add all bool settings
  for (setting in boolSettings) {
    let hbox = _createBoolSetting(setting);
    vbox.add(hbox);
  }
  // Add all int settings
  for (setting in intSettings) {
    let hbox = _createIntSetting(setting);
    vbox.add(hbox);
  }

  frame.add(vbox);
  frame.show_all();

  return frame;
}
<|endoftext|>


<commit_before>/**
 * Pulse.js - Beats per minute (BPM) automatic detection with Web Audio API.
 * @param  {object} global - The window object
 * @return {void}
 */
(function(global) {

    "use strict";

    var self,

    /**
     * Pulse constructor
     * @constructor
     */
    Pulse = function(options) {

        self = this;

        this.audioContext = this.getAudioContext();
        this.buffer = null;
        this.setOptions(options || {});

        this.WEB_AUDIO_API_NOT_SUPPORTED = 1001;
        this.REQUEST_PROGRESS = 101;
        this.REQUEST_LOAD = 102;
        this.REQUEST_ERROR = 1102;
        this.REQUEST_ABORT = 104;

        var notifier = Object.getNotifier(this);

        Object.defineProperty(this, 'status', {
            get: function() {
                return parseInt(status, 10);
            },
            set: function(s) {
                if (s == status) {
                    return;
                }

                notifier.notify({
                    type: 'update',
                    name: 'status',
                    oldValue: s
                });

                status = s;
            }
        });

        Object.observe(this, function(changes) {
            for(var i = 0; i < changes.length; i++) {
                if(
                    changes[i].type === 'update' &&
                    changes[i].name === 'status' &&
                    changes[i].lastValue != self.status
                ) {
                    switch(self.status) {
                        case self.WEB_AUDIO_API_NOT_SUPPORTED :
                            console.error("WEB_AUDIO_API_NOT_SUPPORTED");
                        break;

                        case self.REQUEST_PROGRESS :
                            console.log("REQUEST_PROGRESS");
                        break;

                        case self.REQUEST_LOAD :
                            console.log("REQUEST_LOAD");
                        break;

                        case self.REQUEST_ERROR :
                            console.error("REQUEST_ERROR");
                        break;

                        case self.REQUEST_ABORT :
                            console.error("REQUEST_ABORT");
                        break;

                        default :
                            console.error("STATUS NOT IMPLEMENTED");
                        break;
                    }
                }
            }
        });
    };

    /**
     * getAudioContext description
     * @return {object} AudioContext
     */
    Pulse.prototype.getAudioContext = function() {
        try {
            global.AudioContext = global.AudioContext || global.webkitAudioContext;
            return new global.AudioContext();
        } catch(e) {
            this.status = this.WEB_AUDIO_API_NOT_SUPPORTED;
            return null;
        }
    };

    Pulse.prototype.setOptions = function(options) {
        var defaultOptions = this.getDefaultOptions();
        for(var i in defaultOptions) {
            if(options[i] == undefined) {
                options[i] = defaultOptions[i];
            }
        }

        this.options = options;
    };

    Pulse.prototype.getDefaultOptions = function() {
        return {
            onsuccess: function() {},
            onerror: function() {},
            convertToMilliseconds: true,
            removeDuplicates: true,
        };
    };

    /**
     * LoadBufferFromURI description
     * @param  {string} uri - The URI of the song
     * @param  {string} options - Options
     * @return {object}
     */
    Pulse.prototype.loadBufferFromURI = function(uri, options) {

        if(this.audioContext === null) {
            return false;
        }

        var request = new XMLHttpRequest();

        request.open("GET", uri, true);
        request.responseType = "arraybuffer";

        request.addEventListener("progress", function(event) {
            self._requestProgress(event);
        }, false);

        request.addEventListener("load", function(event) {
            self._requestLoad(event, this);
        }, false);

        request.addEventListener("error", function() {
            self._requestError(event, this);
        }, false);

        request.addEventListener("abort", function() {
            self._requestAbort();
        }, false);

        request.send(null);

        return true;
    };

    Pulse.prototype._requestProgress = function(event) {
        this.status = this.REQUEST_PROGRESS;

        if(event.lengthComputable) {
            //console.log(event.loaded + ' / ' + event.total);
        }
    };

    Pulse.prototype._requestLoad = function(event, request) {
        if(request.readyState === request.DONE) {
            this.buffer = request.response;
            this.status = this.REQUEST_LOAD;
            this.options.onsuccess(this);
        } else {
            this._requestError(event, request, this.options);
        }
    };

    Pulse.prototype._requestError = function(event, request) {
        this.status = this.REQUEST_ERROR;
        this.options.onerror(request);
    };

    Pulse.prototype._requestAbort = function() {
        this.status = this.REQUEST_ABORT;
    };

    Pulse.prototype.process = function() {
        this.audioContext.decodeAudioData(
            this.buffer,
            this.processBpm,
            function(error) {
                self.status = self.DECODING_ERROR;
            }
        );
    };

    Pulse.prototype.getOfflineContext = function(buffer) {
        var offlineContext = new global.OfflineAudioContext(1, buffer.length, buffer.sampleRate),
            source = offlineContext.createBufferSource(),
            filter = offlineContext.createBiquadFilter();

        source.buffer = buffer;
        filter.type = "lowpass";

        source.connect(filter);
        filter.connect(offlineContext.destination);

        source.start(0);
        return offlineContext;
    };

    Pulse.prototype.processBpm = function(buffer) {

        var offlineContext = self.getOfflineContext(buffer);

        offlineContext.oncomplete = function(event) {
            self.peaks = self.getPeaks(event);
            console.log(self.peaks);
        };

        offlineContext.startRendering();
    };

    Pulse.prototype.getChannelDataMinMax = function(channelData) {
        var length = channelData.length,
            min = channelData[0],
            max = channelData[0],
            j;

        for(j = 1; j < length; j++) {
            min = Math.min(min, channelData[j]);
            max = Math.max(max, channelData[j]);
        }

        return {
            min: min,
            max: max
        };
    };

    Pulse.prototype.getPeaks = function(event) {

        var renderedBuffer = event.renderedBuffer,
            channelData = renderedBuffer.getChannelData(0),
            limit = this.getChannelDataMinMax(channelData),
            intervalMin = 230, // ms, max tempo = 260 bpm
            amplitude = Math.abs(limit.min) + Math.abs(limit.max),
            maxThreshold = limit.min + amplitude * 0.9, // 90% uppest beats
            minThreshold = limit.min + amplitude * 0.3, // 30% uppest beats
            threshold = maxThreshold,
            acuracy = renderedBuffer.sampleRate * (intervalMin / 1000),
            peakFilter = [],
            duration = parseInt(renderedBuffer.duration, 10),
            length = channelData.length,
            j;

        // grab peaks
        while (
            threshold >= minThreshold &&
            peakFilter.length <= duration
        ) {
            j = 0;
            for(; j < length; j++) {
                if (channelData[j] > threshold) {
                    peakFilter.push(j);

                    j += acuracy;
                }
            }
            threshold -= 0.05; // -5% every interation
        }

        peakFilter.sort(function(a, b) {
            return a - b;
        });

        if(self.options.convertToMilliseconds) {
            peakFilter = self.convertHertzToMilliseconds(peakFilter, renderedBuffer.sampleRate);
        }

        if(self.options.removeDuplicates) {
            // remove all duplicates and 0 values
            peakFilter = peakFilter.filter(function(item, pos) {
                return (!pos || item  > peakFilter[pos - 1]) && item > 0;
            });
        }

        return peakFilter;
    };

    Pulse.prototype.convertHertzToMilliseconds = function(array, rate) {
        for (var i in array) {
            array[i] = Math.floor((array[i] / rate) * 1000);
        }

        return array;
    };

    /*Pulse.prototype.get = function() {
        this.audioContext.decodeAudioData(
            response,
            this.processBpm,
            function(error) {
                alert("Decoding error:" + error);
            }
        );
    };

    Pulse.prototype.getPeaks = function() {
        return this._peaks;
    };

    Pulse.prototype.getSimulatedPeaks = function() {
        return this._peaks;
    };

    Pulse.prototype.getPulseTempo44 = function() {
        return this._peaks;
    };*/

    /*
    Pulse.prototype.processBpm = function(buffer) {
        if(!global.fingr.checkAudioBuffer(buffer)) {
            global.alert("Nope...");
            return false;
        }

        var self = this;

        var offlineContext = new global.OfflineAudioContext(1, buffer.length, buffer.sampleRate);

        var source = offlineContext.createBufferSource();
        source.buffer = buffer;

        var filter = offlineContext.createBiquadFilter();
        filter.type = "lowpass";

        source.connect(filter);
        filter.connect(offlineContext.destination);

        source.start(0);
        offlineContext.startRendering();

        var playbackTempo = [];

        offlineContext.oncomplete = function(e) {
            var filteredBuffer = e.renderedBuffer;

            var data = filteredBuffer.getChannelData(0);
            var length = data.length;

            var min = data[0], max = data[0];
            for(var j = 1; j < length; j++) {
                min = Math.min(min, data[j]);
                max = Math.max(max, data[j]);
            }

            var intervalMin = 230; // ms, max tempo = 260 bpm

            var amplitude = Math.abs(min) + Math.abs(max);
            var maxThreshold = min + amplitude * 0.9; // 90% uppest beats
            var threshold = maxThreshold;
            var minThreshold = min + amplitude * 0.3; // 30% uppest beats

            var acuracy = filteredBuffer.sampleRate * (intervalMin / 1000);
            var peakFilter = [];
            var hiBeats = [];

            // grab peaks
            while (threshold >= minThreshold && peakFilter.length <= parseInt(buffer.duration, 10)) {
                j = 0;
                for(; j < length; j++) {
                    if (data[j] > threshold) {
                        peakFilter.push(j);

                        j += acuracy;
                    }
                }
                threshold -= 0.05; // -5% every interation
            }

            // sort peaks desc
            peakFilter.sort(function(a, b) {
                return a - b;
            });

            // convert from hz to to milliseconds
            for (var i in peakFilter) {
                peakFilter[i] = Math.floor((peakFilter[i] / filteredBuffer.sampleRate) * 1000);
            }

            // remove all duplicates and 0 values
            peakFilter = peakFilter.filter(function(item, pos) {
                return (!pos || item  > peakFilter[pos - 1]) && item > 0;
            });

            // count interval durations between each peak
            var intervals = {};
            for (var i = 1; i < peakFilter.length; i++) {
                for (var j = 0; j < i; j++) {

                    // assuming intervals must be less than 260 bpm (more than ~230 ms)
                    if (peakFilter[i] - peakFilter[j] >= 230) {
                        if (intervals[peakFilter[i] - peakFilter[j]] === undefined) {
                            intervals[peakFilter[i] - peakFilter[j]] = 0;
                        }
                        intervals[peakFilter[i] - peakFilter[j]]++;
                    }
                }
            }

            // quadratic mean to compute the average power
            var square = 0;
            var count = 0;
            for (var i in intervals) {
                square += Math.pow(intervals[i], 2);
                count++;
            }

            var avgCountInterval = Math.sqrt(square / count);

            // get max beats between an interval (1000 ms)
            var max = 0
            var ms = 0;
            var msBetween = [];
            var k;
            for (var i in intervals) {
                if (intervals[i] > avgCountInterval) {
                    if (intervals[i] > max) {
                        max = intervals[i];
                        ms = parseInt(i, 10);
                    }

                    k = Math.floor(i / 500); // segmentation by 500, this needs to be computed
                    if (msBetween[k] === undefined) {
                        msBetween.push({ max: 0, ms: parseInt(i, 10) });
                    }

                    if (msBetween[k] !== undefined && intervals[i] > msBetween[k].max) {
                        msBetween[k] = { max: intervals[i], ms: parseInt(i, 10) };
                    }
                }
            }

            // compare ms with all other time beats
            var referenceMs = msBetween.slice(0, 3);
            var sumMargins = [];

            for (var i = 0; i < referenceMs.length; i++) {
                sumMargins.push(0);
                for (var j in msBetween) {
                    sumMargins[i] += msBetween[j].ms % referenceMs[i].ms;
                }
            }

            var minMarginIndex = 0;
            var minMargin = sumMargins[minMarginIndex];
            for (var i = 1; i < sumMargins.length; i++) {
                if (minMargin > sumMargins[i]) {
                    minMargin = sumMargins[i];
                    minMarginIndex = i;
                }
            }

            console.log("ms = " + referenceMs[minMarginIndex].ms);
            console.log("tempo = " + 60000 / referenceMs[minMarginIndex].ms);

            // find the start beat of tempo
            var tempo = Math.round(60000 / referenceMs[minMarginIndex].ms);
            var tempoMs = referenceMs[minMarginIndex].ms;

            document.getElementById("pulse").style.webkitAnimationDuration = tempoMs + "ms";
            document.getElementById("pulse").innerHTML = tempo;

            for (var i = 0; i < peakFilter.length; i++) {
                for (var j = 0; j < i; j++) {
                    if(peakFilter[i] - peakFilter[j] == tempoMs) {
                       playbackTempo.push(peakFilter[i]);
                       playbackTempo.push(peakFilter[j]);
                    }
                }
            }

            playbackTempo.sort(function(a, b) {
                return a - b;
            });

            playbackTempo = playbackTempo.filter(function(item, pos) {
                return (!pos || item > playbackTempo[pos - 1] + 230) && item > 0;
            });

            // detect chained
            var chainedKeys = [];
            var chainedKeysMax = [];
            var errorMs = 2;
            for (var i = 0; i < playbackTempo.length - 1; i++) {
                if (playbackTempo[i] + tempoMs <= playbackTempo[i + 1] + errorMs && playbackTempo[i] + tempoMs >= playbackTempo[i + 1] - errorMs) {
                    chainedKeys.push(i);
                } else {
                    if (chainedKeysMax.length < chainedKeys.length) {
                        chainedKeysMax = chainedKeys;
                    }

                    chainedKeys = [];
                }
            }

            var simulatedBeats = [];

            //console.log(playbackTempo);
            //console.log(chainedKeysMax);
            //console.log(peakFilter);

            if (chainedKeysMax.length) {
                var minChained = playbackTempo[chainedKeysMax[0]];
                while (minChained > tempoMs) {
                    minChained -= tempoMs;
                }

                var maxChained = playbackTempo[chainedKeysMax.length - 1];
                while (maxChained < buffer.duration * 1000) {
                    maxChained += tempoMs;
                }

                for (var i = minChained; i <= maxChained; i += tempoMs) {
                    simulatedBeats.push(i);
                };
            }

            // to sec
            for (var i = 0; i < simulatedBeats.length; i++) {
                simulatedBeats[i] -= global.fingr.msMoveDown;
                simulatedBeats[i] /= 1000;
            }

            // to sec
            for (var i = 0; i < peakFilter.length; i++) {
                peakFilter[i] -= global.fingr.msMoveDown;
                peakFilter[i] /= 1000;
            }

            console.log(peakFilter);
            console.log(simulatedBeats);

            global.fingr.beatPerMS = tempoMs;

            global.fingr.dance(buffer, peakFilter, simulatedBeats);
        };
    };
    */

    global.Pulse = Pulse;

}(window));<commit_msg>Add intervals computations<commit_after>/**
 * Pulse.js - Beats per minute (BPM) automatic detection with Web Audio API.
 * @param  {object} global - The window object
 * @return {void}
 */
(function(global) {

    "use strict";

    var self,

    /**
     * Pulse constructor
     * @constructor
     */
    Pulse = function(options) {

        self = this;

        this.audioContext = this.getAudioContext();
        this.buffer = null;
        this.options = options || {};

        this.WEB_AUDIO_API_NOT_SUPPORTED = 1001;
        this.REQUEST_PROGRESS = 101;
        this.REQUEST_LOAD = 102;
        this.REQUEST_ERROR = 1102;
        this.REQUEST_ABORT = 104;

        var notifier = Object.getNotifier(this);

        Object.defineProperty(this, 'status', {
            get: function() {
                return parseInt(status, 10);
            },
            set: function(s) {
                if (s == status) {
                    return;
                }

                notifier.notify({
                    type: 'update',
                    name: 'status',
                    oldValue: s
                });

                status = s;
            }
        });

        Object.defineProperty(this, 'options', {
            get: function() {
                return options;
            },
            set: function(o) {
                var defaultOptions = this.getDefaultOptions();
                for(var i in defaultOptions) {
                    if(o[i] == undefined) {
                        o[i] = defaultOptions[i];
                    }
                }

                options = o;
            }
        });

        Object.observe(this, function(changes) {
            for(var i = 0; i < changes.length; i++) {
                if(
                    changes[i].type === 'update' &&
                    changes[i].name === 'status' &&
                    changes[i].lastValue != self.status
                ) {
                    switch(self.status) {
                        case self.WEB_AUDIO_API_NOT_SUPPORTED :
                            console.error("WEB_AUDIO_API_NOT_SUPPORTED");
                        break;

                        case self.REQUEST_PROGRESS :
                            console.log("REQUEST_PROGRESS");
                        break;

                        case self.REQUEST_LOAD :
                            console.log("REQUEST_LOAD");
                        break;

                        case self.REQUEST_ERROR :
                            console.error("REQUEST_ERROR");
                        break;

                        case self.REQUEST_ABORT :
                            console.error("REQUEST_ABORT");
                        break;

                        default :
                            console.error("STATUS NOT IMPLEMENTED");
                        break;
                    }
                }
            }
        });
    };

    /**
     * getAudioContext description
     * @return {object} AudioContext
     */
    Pulse.prototype.getAudioContext = function() {
        try {
            global.AudioContext = global.AudioContext || global.webkitAudioContext;
            return new global.AudioContext();
        } catch(e) {
            this.status = this.WEB_AUDIO_API_NOT_SUPPORTED;
            return null;
        }
    };

    Pulse.prototype.getDefaultOptions = function() {
        return {
            onsuccess: function() {},
            onerror: function() {},
            convertToMilliseconds: true,
            removeDuplicates: true,
        };
    };

    /**
     * LoadBufferFromURI description
     * @param  {string} uri - The URI of the song
     * @param  {string} options - Options
     * @return {object}
     */
    Pulse.prototype.loadBufferFromURI = function(uri, options) {

        if(this.audioContext === null) {
            return false;
        }

        var request = new XMLHttpRequest();

        request.open("GET", uri, true);
        request.responseType = "arraybuffer";

        request.addEventListener("progress", function(event) {
            self._requestProgress(event);
        }, false);

        request.addEventListener("load", function(event) {
            self._requestLoad(event, this);
        }, false);

        request.addEventListener("error", function() {
            self._requestError(event, this);
        }, false);

        request.addEventListener("abort", function() {
            self._requestAbort();
        }, false);

        request.send(null);

        return true;
    };

    Pulse.prototype._requestProgress = function(event) {
        this.status = this.REQUEST_PROGRESS;

        if(event.lengthComputable) {
            //console.log(event.loaded + ' / ' + event.total);
        }
    };

    Pulse.prototype._requestLoad = function(event, request) {
        if(request.readyState === request.DONE) {
            this.buffer = request.response;
            this.status = this.REQUEST_LOAD;
            this.options.onsuccess(this);
        } else {
            this._requestError(event, request, this.options);
        }
    };

    Pulse.prototype._requestError = function(event, request) {
        this.status = this.REQUEST_ERROR;
        this.options.onerror(request);
    };

    Pulse.prototype._requestAbort = function() {
        this.status = this.REQUEST_ABORT;
    };

    Pulse.prototype.process = function() {
        this.audioContext.decodeAudioData(
            this.buffer,
            this.processBpm,
            function(error) {
                self.status = self.DECODING_ERROR;
            }
        );
    };

    Pulse.prototype.getOfflineContext = function(buffer) {
        var offlineContext = new global.OfflineAudioContext(1, buffer.length, buffer.sampleRate),
            source = offlineContext.createBufferSource(),
            filter = offlineContext.createBiquadFilter();

        source.buffer = buffer;
        filter.type = "lowpass";

        source.connect(filter);
        filter.connect(offlineContext.destination);

        source.start(0);
        return offlineContext;
    };

    Pulse.prototype.processBpm = function(buffer) {

        var offlineContext = self.getOfflineContext(buffer);

        offlineContext.oncomplete = function(event) {
            self.peaks = self.getPeaks(event);
            console.log(self.peaks);
        };

        offlineContext.startRendering();
    };

    Pulse.prototype.getChannelDataMinMax = function(channelData) {
        var length = channelData.length,
            min = channelData[0],
            max = channelData[0],
            j;

        for(j = 1; j < length; j++) {
            min = Math.min(min, channelData[j]);
            max = Math.max(max, channelData[j]);
        }

        return {
            min: min,
            max: max
        };
    };

    Pulse.prototype.getPeaks = function(event) {

        var renderedBuffer = event.renderedBuffer,
            channelData = renderedBuffer.getChannelData(0),
            limit = this.getChannelDataMinMax(channelData),
            intervalMin = 230, // ms, max tempo = 260 bpm
            amplitude = Math.abs(limit.min) + Math.abs(limit.max),
            maxThreshold = limit.min + amplitude * 0.9, // 90% uppest beats
            minThreshold = limit.min + amplitude * 0.3, // 30% uppest beats
            threshold = maxThreshold,
            acuracy = renderedBuffer.sampleRate * (intervalMin / 1000),
            peakFilter = [],
            duration = parseInt(renderedBuffer.duration, 10),
            length = channelData.length,
            j;

        // grab peaks
        while (
            threshold >= minThreshold &&
            peakFilter.length <= duration
        ) {
            j = 0;
            for(; j < length; j++) {
                if (channelData[j] > threshold) {
                    peakFilter.push(j);

                    j += acuracy;
                }
            }
            threshold -= 0.05; // -5% every interation
        }

        peakFilter.sort(function(a, b) {
            return a - b;
        });

        if(self.options.convertToMilliseconds) {
            for (var i in peakFilter) {
                peakFilter[i] = Math.floor((peakFilter[i] / renderedBuffer.sampleRate) * 1000);
            }
        }

        if(self.options.removeDuplicates) {
            // remove all duplicates and 0 values
            peakFilter = peakFilter.filter(function(item, pos) {
                return (!pos || item  > peakFilter[pos - 1]) && item > 0;
            });
        }

        return peakFilter;
    };

    Pulse.prototype.getInterval = function() {
        // count interval durations between each peak
        var intervals = {};
        for (var i = 1; i < peakFilter.length; i++) {
            for (var j = 0; j < i; j++) {

                // assuming intervals must be less than 260 bpm (more than ~230 ms)
                if (peakFilter[i] - peakFilter[j] >= 230) {
                    if (intervals[peakFilter[i] - peakFilter[j]] === undefined) {
                        intervals[peakFilter[i] - peakFilter[j]] = 0;
                    }
                    intervals[peakFilter[i] - peakFilter[j]]++;
                }
            }
        }
    };

    /*Pulse.prototype.get = function() {
        this.audioContext.decodeAudioData(
            response,
            this.processBpm,
            function(error) {
                alert("Decoding error:" + error);
            }
        );
    };

    Pulse.prototype.getPeaks = function() {
        return this._peaks;
    };

    Pulse.prototype.getSimulatedPeaks = function() {
        return this._peaks;
    };

    Pulse.prototype.getPulseTempo44 = function() {
        return this._peaks;
    };*/

    /*
    Pulse.prototype.processBpm = function(buffer) {
        if(!global.fingr.checkAudioBuffer(buffer)) {
            global.alert("Nope...");
            return false;
        }

        var self = this;

        var offlineContext = new global.OfflineAudioContext(1, buffer.length, buffer.sampleRate);

        var source = offlineContext.createBufferSource();
        source.buffer = buffer;

        var filter = offlineContext.createBiquadFilter();
        filter.type = "lowpass";

        source.connect(filter);
        filter.connect(offlineContext.destination);

        source.start(0);
        offlineContext.startRendering();

        var playbackTempo = [];

        offlineContext.oncomplete = function(e) {
            var filteredBuffer = e.renderedBuffer;

            var data = filteredBuffer.getChannelData(0);
            var length = data.length;

            var min = data[0], max = data[0];
            for(var j = 1; j < length; j++) {
                min = Math.min(min, data[j]);
                max = Math.max(max, data[j]);
            }

            var intervalMin = 230; // ms, max tempo = 260 bpm

            var amplitude = Math.abs(min) + Math.abs(max);
            var maxThreshold = min + amplitude * 0.9; // 90% uppest beats
            var threshold = maxThreshold;
            var minThreshold = min + amplitude * 0.3; // 30% uppest beats

            var acuracy = filteredBuffer.sampleRate * (intervalMin / 1000);
            var peakFilter = [];
            var hiBeats = [];

            // grab peaks
            while (threshold >= minThreshold && peakFilter.length <= parseInt(buffer.duration, 10)) {
                j = 0;
                for(; j < length; j++) {
                    if (data[j] > threshold) {
                        peakFilter.push(j);

                        j += acuracy;
                    }
                }
                threshold -= 0.05; // -5% every interation
            }

            // sort peaks desc
            peakFilter.sort(function(a, b) {
                return a - b;
            });

            // convert from hz to to milliseconds
            for (var i in peakFilter) {
                peakFilter[i] = Math.floor((peakFilter[i] / filteredBuffer.sampleRate) * 1000);
            }

            // remove all duplicates and 0 values
            peakFilter = peakFilter.filter(function(item, pos) {
                return (!pos || item  > peakFilter[pos - 1]) && item > 0;
            });

            // count interval durations between each peak
            var intervals = {};
            for (var i = 1; i < peakFilter.length; i++) {
                for (var j = 0; j < i; j++) {

                    // assuming intervals must be less than 260 bpm (more than ~230 ms)
                    if (peakFilter[i] - peakFilter[j] >= 230) {
                        if (intervals[peakFilter[i] - peakFilter[j]] === undefined) {
                            intervals[peakFilter[i] - peakFilter[j]] = 0;
                        }
                        intervals[peakFilter[i] - peakFilter[j]]++;
                    }
                }
            }

            // quadratic mean to compute the average power
            var square = 0;
            var count = 0;
            for (var i in intervals) {
                square += Math.pow(intervals[i], 2);
                count++;
            }

            var avgCountInterval = Math.sqrt(square / count);

            // get max beats between an interval (1000 ms)
            var max = 0
            var ms = 0;
            var msBetween = [];
            var k;
            for (var i in intervals) {
                if (intervals[i] > avgCountInterval) {
                    if (intervals[i] > max) {
                        max = intervals[i];
                        ms = parseInt(i, 10);
                    }

                    k = Math.floor(i / 500); // segmentation by 500, this needs to be computed
                    if (msBetween[k] === undefined) {
                        msBetween.push({ max: 0, ms: parseInt(i, 10) });
                    }

                    if (msBetween[k] !== undefined && intervals[i] > msBetween[k].max) {
                        msBetween[k] = { max: intervals[i], ms: parseInt(i, 10) };
                    }
                }
            }

            // compare ms with all other time beats
            var referenceMs = msBetween.slice(0, 3);
            var sumMargins = [];

            for (var i = 0; i < referenceMs.length; i++) {
                sumMargins.push(0);
                for (var j in msBetween) {
                    sumMargins[i] += msBetween[j].ms % referenceMs[i].ms;
                }
            }

            var minMarginIndex = 0;
            var minMargin = sumMargins[minMarginIndex];
            for (var i = 1; i < sumMargins.length; i++) {
                if (minMargin > sumMargins[i]) {
                    minMargin = sumMargins[i];
                    minMarginIndex = i;
                }
            }

            console.log("ms = " + referenceMs[minMarginIndex].ms);
            console.log("tempo = " + 60000 / referenceMs[minMarginIndex].ms);

            // find the start beat of tempo
            var tempo = Math.round(60000 / referenceMs[minMarginIndex].ms);
            var tempoMs = referenceMs[minMarginIndex].ms;

            document.getElementById("pulse").style.webkitAnimationDuration = tempoMs + "ms";
            document.getElementById("pulse").innerHTML = tempo;

            for (var i = 0; i < peakFilter.length; i++) {
                for (var j = 0; j < i; j++) {
                    if(peakFilter[i] - peakFilter[j] == tempoMs) {
                       playbackTempo.push(peakFilter[i]);
                       playbackTempo.push(peakFilter[j]);
                    }
                }
            }

            playbackTempo.sort(function(a, b) {
                return a - b;
            });

            playbackTempo = playbackTempo.filter(function(item, pos) {
                return (!pos || item > playbackTempo[pos - 1] + 230) && item > 0;
            });

            // detect chained
            var chainedKeys = [];
            var chainedKeysMax = [];
            var errorMs = 2;
            for (var i = 0; i < playbackTempo.length - 1; i++) {
                if (playbackTempo[i] + tempoMs <= playbackTempo[i + 1] + errorMs && playbackTempo[i] + tempoMs >= playbackTempo[i + 1] - errorMs) {
                    chainedKeys.push(i);
                } else {
                    if (chainedKeysMax.length < chainedKeys.length) {
                        chainedKeysMax = chainedKeys;
                    }

                    chainedKeys = [];
                }
            }

            var simulatedBeats = [];

            //console.log(playbackTempo);
            //console.log(chainedKeysMax);
            //console.log(peakFilter);

            if (chainedKeysMax.length) {
                var minChained = playbackTempo[chainedKeysMax[0]];
                while (minChained > tempoMs) {
                    minChained -= tempoMs;
                }

                var maxChained = playbackTempo[chainedKeysMax.length - 1];
                while (maxChained < buffer.duration * 1000) {
                    maxChained += tempoMs;
                }

                for (var i = minChained; i <= maxChained; i += tempoMs) {
                    simulatedBeats.push(i);
                };
            }

            // to sec
            for (var i = 0; i < simulatedBeats.length; i++) {
                simulatedBeats[i] -= global.fingr.msMoveDown;
                simulatedBeats[i] /= 1000;
            }

            // to sec
            for (var i = 0; i < peakFilter.length; i++) {
                peakFilter[i] -= global.fingr.msMoveDown;
                peakFilter[i] /= 1000;
            }

            console.log(peakFilter);
            console.log(simulatedBeats);

            global.fingr.beatPerMS = tempoMs;

            global.fingr.dance(buffer, peakFilter, simulatedBeats);
        };
    };
    */

    global.Pulse = Pulse;

}(window));<|endoftext|>


<commit_before>function init(container, width, height) {
    // This code is executed once and it should initialize the graph, the
    // available parameters are (container, width, height)

    // container: d3 selection of the contaner div for the graph
    // width: width of the container
    // height: height of the container

    /*
        select    0.01 * item as x
                , 0.01 * item + 0.1 * sin(0.1 * item) as y1
        from integer where item >= 0 and item <= 200
    */

    // The function must then return an object with the following callbacks:

    var margin = { top: 20, right: 20, bottom: 30, left: 40 }; 	// physical margins in px
    var cWidth, cHeight;							// main physical content size in px
    var xScale, yScale;
    var xAxisGroup;
    var yAxisGroup;

    var xMin = 1e100, xMax = -1e100;
    var yMin = 1e100, yMax = -1e100;
    var xAllowance = 0.05;
    var yAllowance = 0.05;
    var xAxis, xText = "x-Value";
    var yAxis, yText = "y-Value";
    var radius = 3;

    var firstData = true;
    var svg = container.append('svg');
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var idVal = function(d) {
        return d.id;
    }

    var xVal = function(d) {
        return parseFloat(d.x_1);
    }

    var yVal = function(d) {
        return parseFloat(d.y1_2);
    }

    var circleAttrs = function(d) {
        return {
            cx: xScale(xVal(d)),
            cy: yScale(yVal(d)),
            r: radius
        };
    };

    var circleStyles = function(d) {
        var obj = {
            fill: "steelblue"
        };
        return obj;
    };

    function xGrow(xMinNew,xMaxNew) {
        if (xMinNew < xMin) {
            xMin = xMinNew - xAllowance * (xMaxNew-xMinNew);
        }
        if (xMaxNew > xMax) {
            xMax = xMaxNew + xAllowance * (xMaxNew-xMinNew);
        }
        return;
    }

    function yGrow(yMinNew,yMaxNew) {
        if (yMinNew < yMin) {
            yMin = yMinNew - yAllowance * (yMaxNew-yMinNew);
        }
        if (yMaxNew > yMax) {
            yMax = yMaxNew + yAllowance * (yMaxNew-yMinNew);
        }
        return;
    }

    function resize(w, h) {
        console.log("resize called");
        width = w;
        height = h;
        cWidth = width - margin.left - margin.right;
        cHeight = height - margin.top - margin.bottom;
        svg.attr('width', width).attr('height', height);
        if (firstData === false) {
            rescale();
            xAxisGroup.remove();
            xAxisGroup = g.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + cHeight + ")")
                .call(xAxis);

            xAxisGroup.append("text")
                .attr("x", cWidth)
                .attr("dx", "-0.71em")
                .attr("dy", "-0.71em")
                .style("text-anchor", "end")
                .style('stroke', 'Black')
                .text(xText);

            // xAxisGroup
            //     .attr("transform", "translate(0," + cHeight + ")")
            //     .transition().call(xAxis);  // Update X-Axis
            // xAxisGroup.selectAll("text")
            //     .attr("x", cWidth);

            yAxisGroup.transition().call(yAxis);  // Update Y-Axis
            var circles = g.selectAll("circle");
            circles.transition()
                .attr("cx", function(d) { return xScale(xVal(d)); })
                .attr("cy", function(d) { return yScale(yVal(d)); });
        }
    }

    function rescale() {
        if (xMin >= xMax) {
            xScale = d3.scaleLinear().domain([xMin-1, xMax+1]).range([0, cWidth]);
        } else {
            xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, cWidth]);
        }

        if (yMin >= yMax) {
            yScale = d3.scaleLinear().domain([yMin-1, yMax+1]).range([cHeight, 0]);
        } else {
            yScale = d3.scaleLinear().domain([yMin, yMax]).range([cHeight, 0]);
        }

        xAxis = d3.axisBottom(xScale).ticks(10);
        yAxis = d3.axisLeft(yScale).ticks(10);      // , "%"
    }

    resize(width, height);

    return {

        on_data: function(data) {

            if (data.length === 0) {return;}

            xGrow(Math.min(xMin, d3.min(data, xVal)), Math.max(xMax, d3.max(data, xVal)));
            yGrow(Math.min(yMin, d3.min(data, yVal)), Math.max(yMax, d3.max(data, yVal)));

            rescale();

            if (firstData) {

                firstData = false;

                xAxisGroup = g.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + cHeight + ")")
                    .call(xAxis);

                xAxisGroup.append("text")
                    .attr("x", cWidth)
                    .attr("dx", "-0.71em")
                    .attr("dy", "-0.71em")
                    .style("text-anchor", "end")
                    .style('stroke', 'Black')
                    .text(xText);

                yAxisGroup = g.append("g")
                    .attr("class", "y axis")
                    .call(yAxis);

                yAxisGroup.append("text")
                    .attr("transform", "rotate(-90)")
                    .attr("y", 6)
                    .attr("dx", "-0.71em")
                    .attr("dy", ".71em")
                    .style("text-anchor", "end")
                    .style('stroke', 'Black')
                    .text(yText);

            } else {
                xAxisGroup
                    .attr("transform", "translate(0," + cHeight + ")")
                    .transition().call(xAxis);  // Update X-Axis
                yAxisGroup.transition().call(yAxis);  // Update Y-Axis
            }

            var circles = g.selectAll("circle");

            circles.transition()
                .attr("cx", function(d) { return xScale(xVal(d)); })
                .attr("cy", function(d) { return yScale(yVal(d)); });

            circles.data(data, idVal)
                .enter()
                .append("svg:circle")
                .attrs(circleAttrs)
                .styles(circleStyles)
                ;

        },

        on_resize: function(w, h) {
            resize(w, h);
        },

        on_reset: function() {
            g.selectAll('svg > g > *').remove();
            firstData = true;
        }
    };
}<commit_msg>Add fixed scale options<commit_after>function init(container, width, height) {
    // This code is executed once and it should initialize the graph, the
    // available parameters are (container, width, height)

    // container: d3 selection of the contaner div for the graph
    // width: width of the container
    // height: height of the container

    /*
        select    0.01 * item as x
                , 0.01 * item + 0.1 * sin(0.1 * item) as y1
        from integer where item >= 0 and item <= 200
    */

    // The function must then return an object with the following callbacks:

    var margin = { top: 20, right: 20, bottom: 30, left: 40 }; 	// physical margins in px
    var cWidth, cHeight;							// main physical content size in px
    var xScale, yScale;
    var xAxisGroup;
    var yAxisGroup;

    var xMin = 1e100, xMax = -1e100;    // autoscale defaults
    var yMin = 1e100, yMax = -1e100;    // autoscale defaults

    // xMin = ..., xMax = ....;         // set fixed initial values here
    // yMin = ..., yMax = ....;         // set fixed initial values here
    var xAutoscale = true;
    var yAutoscale = true;
    var xAllowance = 0.05;
    var yAllowance = 0.05;
    var xAxis, xText = "x-Value";
    var yAxis, yText = "y-Value";
    var radius = 3;

    var firstData = true;
    var svg = container.append('svg');
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var idVal = function(d) {
        return d.id;
    }

    var xVal = function(d) {
        return parseFloat(d.x_1);
    }

    var yVal = function(d) {
        return parseFloat(d.y1_2);
    }

    var circleAttrs = function(d) {
        return {
            cx: xScale(xVal(d)),
            cy: yScale(yVal(d)),
            r: radius
        };
    };

    var circleStyles = function(d) {
        var obj = {
            fill: "steelblue"
        };
        return obj;
    };

    function xGrow(xMinNew,xMaxNew) {
        if (xAutoscale) {
            if (xMinNew < xMin) {
                xMin = xMinNew - xAllowance * (xMaxNew-xMinNew);
            }
            if (xMaxNew > xMax) {
                xMax = xMaxNew + xAllowance * (xMaxNew-xMinNew);
            }
        }
        return;
    }

    function yGrow(yMinNew,yMaxNew) {
        if (yAutoscale) {
            if (yMinNew < yMin) {
                yMin = yMinNew - yAllowance * (yMaxNew-yMinNew);
            }
            if (yMaxNew > yMax) {
                yMax = yMaxNew + yAllowance * (yMaxNew-yMinNew);
            }
        }
        return;
    }

    function resize(w, h) {
        console.log("resize called");
        width = w;
        height = h;
        cWidth = width - margin.left - margin.right;
        cHeight = height - margin.top - margin.bottom;
        svg.attr('width', width).attr('height', height);
        if (firstData === false) {
            rescale();
            xAxisGroup.remove();
            xAxisGroup = g.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + cHeight + ")")
                .call(xAxis);

            xAxisGroup.append("text")
                .attr("x", cWidth)
                .attr("dx", "-0.71em")
                .attr("dy", "-0.71em")
                .style("text-anchor", "end")
                .style('stroke', 'Black')
                .text(xText);

            // xAxisGroup
            //     .attr("transform", "translate(0," + cHeight + ")")
            //     .transition().call(xAxis);  // Update X-Axis
            // xAxisGroup.selectAll("text")
            //     .attr("x", cWidth);

            yAxisGroup.transition().call(yAxis);  // Update Y-Axis
            var circles = g.selectAll("circle");
            circles.transition()
                .attr("cx", function(d) { return xScale(xVal(d)); })
                .attr("cy", function(d) { return yScale(yVal(d)); });
        }
    }

    function rescale() {
        if (xMin >= xMax) {
            xScale = d3.scaleLinear().domain([xMin-1, xMax+1]).range([0, cWidth]);
        } else {
            xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, cWidth]);
        }

        if (yMin >= yMax) {
            yScale = d3.scaleLinear().domain([yMin-1, yMax+1]).range([cHeight, 0]);
        } else {
            yScale = d3.scaleLinear().domain([yMin, yMax]).range([cHeight, 0]);
        }

        xAxis = d3.axisBottom(xScale).ticks(10);
        yAxis = d3.axisLeft(yScale).ticks(10);      // , "%"
    }

    resize(width, height);

    return {

        on_data: function(data) {

            if (data.length === 0) {return;}

            xGrow(Math.min(xMin, d3.min(data, xVal)), Math.max(xMax, d3.max(data, xVal)));
            yGrow(Math.min(yMin, d3.min(data, yVal)), Math.max(yMax, d3.max(data, yVal)));

            rescale();

            if (firstData) {

                firstData = false;

                xAxisGroup = g.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + cHeight + ")")
                    .call(xAxis);

                xAxisGroup.append("text")
                    .attr("x", cWidth)
                    .attr("dx", "-0.71em")
                    .attr("dy", "-0.71em")
                    .style("text-anchor", "end")
                    .style('stroke', 'Black')
                    .text(xText);

                yAxisGroup = g.append("g")
                    .attr("class", "y axis")
                    .call(yAxis);

                yAxisGroup.append("text")
                    .attr("transform", "rotate(-90)")
                    .attr("y", 6)
                    .attr("dx", "-0.71em")
                    .attr("dy", ".71em")
                    .style("text-anchor", "end")
                    .style('stroke', 'Black')
                    .text(yText);

            } else {
                xAxisGroup
                    .attr("transform", "translate(0," + cHeight + ")")
                    .transition().call(xAxis);  // Update X-Axis
                yAxisGroup.transition().call(yAxis);  // Update Y-Axis
            }

            var circles = g.selectAll("circle");

            circles.transition()
                .attr("cx", function(d) { return xScale(xVal(d)); })
                .attr("cy", function(d) { return yScale(yVal(d)); });

            circles.data(data, idVal)
                .enter()
                .append("svg:circle")
                .attrs(circleAttrs)
                .styles(circleStyles)
                ;

        },

        on_resize: function(w, h) {
            resize(w, h);
        },

        on_reset: function() {
            g.selectAll('svg > g > *').remove();
            firstData = true;
        }
    };
}<|endoftext|>


<commit_before>/*!
 * Firebase Queue is a fault-tolerant, multi-worker, multi-stage job pipeline
 * built on Firebase.
 *
 * Firebase Queue 0.0.0
 * https://github.com/firebase/firebase-queue/
 * License: MIT
 */
'use strict';

var _ = require('lodash'),
    RSVP = require('rsvp'),
    logger = require('winston'),
    QueueWorker = require('./lib/queue_worker.js');

var DEFAULT_NUM_WORKERS = 1,
    DEFAULT_SANITIZE = true,
    DEFAULT_SUPPRESS_STACK = false,
    DEFAULT_TASK_SPEC = {
      inProgressState: 'in_progress',
      timeout: 300000 // 5 minutes
    };

/**
 * @constructor
 * @param {Firebase} ref A Firebase reference to the queue.
 * @param {Object} options (optional) Object containing possible keys:
 *   - specId: {String} the task specification ID for the workers.
 *   - numWorkers: {Number} The number of workers to create for this task.
 *   - sanitize: {Boolean} Whether to sanitize the 'data' passed to the
 *       processing function of internal queue keys.
 * @param {Function} processingFunction A function that is called each time to
 *   process a task. This function is passed four parameters:
 *     - data {Object} The current data at the location.
 *     - progress {Function} A function to update the progress percent of the
 *         task for informational purposes. Pass it a number between 0 and 100.
 *         Returns a promise of whether the operation was completed
 *         successfully.
 *     - resolve {Function} An asychronous callback function - call this
 *         function when the processingFunction completes successfully. This
 *         takes an optional Object parameter that, if passed, will overwrite
 *         the data at the task location, and returns a promise of whether the
 *         operation was successful.
 *     - reject {Function} An asynchronous callback function - call this
 *         function if the processingFunction encounters an error. This takes
 *         an optional String or Object parameter that will be stored in the
 *         '_error_details/error' location in the task and returns a promise
 *         of whether the operation was successful.
 * @returns {Object} The new Queue object.
 */
function Queue() {
  var self = this;
  var constructorArguments = arguments;

  var error;
  self.numWorkers = DEFAULT_NUM_WORKERS;
  self.sanitize = DEFAULT_SANITIZE;
  self.suppressStack = DEFAULT_SUPPRESS_STACK;
  self.initialized = false;

  self.specChangeListener = null;

  if (constructorArguments.length < 2) {
    error = 'Queue must at least have the queueRef and ' +
      'processingFunction arguments.';
    logger.debug('Queue(): Error during initialization', error);
    throw new Error(error);
  } else if (constructorArguments.length === 2) {
    self.processingFunction = constructorArguments[1];
  } else if (constructorArguments.length === 3) {
    var options = constructorArguments[1];
    if (!_.isPlainObject(options)) {
      error = 'Options parameter must be a plain object.';
      logger.debug('Queue(): Error during initialization', error);
      throw new Error(error);
    }
    if (!_.isUndefined(options.specId)) {
      if (_.isString(options.specId)) {
        self.specId = options.specId;
      } else {
        error = 'options.specId must be a String.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.numWorkers)) {
      if (_.isNumber(options.numWorkers) &&
          options.numWorkers > 0 &&
          options.numWorkers % 1 === 0) {
        self.numWorkers = options.numWorkers;
      } else {
        error = 'options.numWorkers must be a positive integer.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.sanitize)) {
      if (_.isBoolean(options.sanitize)) {
        self.sanitize = options.sanitize;
      } else {
        error = 'options.sanitize must be a boolean.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.suppressStack)) {
      if (_.isBoolean(options.suppressStack)) {
        self.suppressStack = options.suppressStack;
      } else {
        error = 'options.suppressStack must be a boolean.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    self.processingFunction = constructorArguments[2];
  } else {
    error = 'Queue can only take at most three arguments - queueRef, ' +
      'options (optional), and processingFunction.';
    logger.debug('Queue(): Error during initialization', error);
    throw new Error(error);
  }

  if (_.isPlainObject(constructorArguments[0])) {
    if (_.isUndefined(constructorArguments[0].tasksRef) || _.isUndefined(constructorArguments[0].specsRef)) {
      error = 'When queueRef is an object then it must specify tasksRef and specsRef.';
      logger.debug('Queue(): Error during initialization', error);
      throw new Error(error);
    }
    self.tasksRef = constructorArguments[0].tasksRef;
    self.specsRef = constructorArguments[0].specsRef;
  } else {
    self.tasksRef = constructorArguments[0].child('tasks');
    self.specsRef = constructorArguments[0].child('specs');
  }

  self.workers = [];
  for (var i = 0; i < self.numWorkers; i++) {
    var processId = (self.specId ? self.specId + ':' : '') + i;
    self.workers.push(new QueueWorker(
      self.tasksRef,
      processId,
      self.sanitize,
      self.suppressStack,
      self.processingFunction
    ));
  }

  if (_.isUndefined(self.specId)) {
    for (var j = 0; j < self.numWorkers; j++) {
      self.workers[j].setTaskSpec(DEFAULT_TASK_SPEC);
    }
    self.initialized = true;
  } else {
    self.specChangeListener = self.specsRef.child(self.specId).on(
      'value',
      function(taskSpecSnap) {
        var taskSpec = {
              startState: taskSpecSnap.child('start_state').val(),
              inProgressState: taskSpecSnap.child('in_progress_state').val(),
              finishedState: taskSpecSnap.child('finished_state').val(),
              errorState: taskSpecSnap.child('error_state').val(),
              timeout: taskSpecSnap.child('timeout').val(),
              retries: taskSpecSnap.child('retries').val()
            };

        for (var i = 0; i < self.numWorkers; i++) {
          self.workers[i].setTaskSpec(taskSpec);
        }
        self.initialized = true;
      }, /* istanbul ignore next */ function(error) {
        logger.debug('Queue(): Error connecting to Firebase reference',
          error.message);
      });
  }

  return self;
}

/**
 * Gracefully shuts down a queue.
 * @returns {RSVP.Promise} A promise fulfilled when all the worker processes
 *   have finished their current tasks and are no longer listening for new ones.
 */
Queue.prototype.shutdown = function() {
  var self = this;

  logger.debug('Queue: Shutting down');
  if (!_.isNull(self.specChangeListener)) {
    self.specsRef.child(self.specId).off('value',
      self.specChangeListener);
    self.specChangeListener = null;
  }

  return RSVP.all(_.map(self.workers, function(worker) {
    return worker.shutdown();
  }));
};

module.exports = Queue;
<commit_msg>Simplified validation of paths configuration<commit_after>/*!
 * Firebase Queue is a fault-tolerant, multi-worker, multi-stage job pipeline
 * built on Firebase.
 *
 * Firebase Queue 0.0.0
 * https://github.com/firebase/firebase-queue/
 * License: MIT
 */
'use strict';

var _ = require('lodash'),
    RSVP = require('rsvp'),
    logger = require('winston'),
    QueueWorker = require('./lib/queue_worker.js');

var DEFAULT_NUM_WORKERS = 1,
    DEFAULT_SANITIZE = true,
    DEFAULT_SUPPRESS_STACK = false,
    DEFAULT_TASK_SPEC = {
      inProgressState: 'in_progress',
      timeout: 300000 // 5 minutes
    };

/**
 * @constructor
 * @param {Firebase} ref A Firebase reference to the queue.
 * @param {Object} options (optional) Object containing possible keys:
 *   - specId: {String} the task specification ID for the workers.
 *   - numWorkers: {Number} The number of workers to create for this task.
 *   - sanitize: {Boolean} Whether to sanitize the 'data' passed to the
 *       processing function of internal queue keys.
 * @param {Function} processingFunction A function that is called each time to
 *   process a task. This function is passed four parameters:
 *     - data {Object} The current data at the location.
 *     - progress {Function} A function to update the progress percent of the
 *         task for informational purposes. Pass it a number between 0 and 100.
 *         Returns a promise of whether the operation was completed
 *         successfully.
 *     - resolve {Function} An asychronous callback function - call this
 *         function when the processingFunction completes successfully. This
 *         takes an optional Object parameter that, if passed, will overwrite
 *         the data at the task location, and returns a promise of whether the
 *         operation was successful.
 *     - reject {Function} An asynchronous callback function - call this
 *         function if the processingFunction encounters an error. This takes
 *         an optional String or Object parameter that will be stored in the
 *         '_error_details/error' location in the task and returns a promise
 *         of whether the operation was successful.
 * @returns {Object} The new Queue object.
 */
function Queue() {
  var self = this;
  var constructorArguments = arguments;

  var error;
  self.numWorkers = DEFAULT_NUM_WORKERS;
  self.sanitize = DEFAULT_SANITIZE;
  self.suppressStack = DEFAULT_SUPPRESS_STACK;
  self.initialized = false;

  self.specChangeListener = null;

  if (constructorArguments.length < 2) {
    error = 'Queue must at least have the queueRef and ' +
      'processingFunction arguments.';
    logger.debug('Queue(): Error during initialization', error);
    throw new Error(error);
  } else if (constructorArguments.length === 2) {
    self.processingFunction = constructorArguments[1];
  } else if (constructorArguments.length === 3) {
    var options = constructorArguments[1];
    if (!_.isPlainObject(options)) {
      error = 'Options parameter must be a plain object.';
      logger.debug('Queue(): Error during initialization', error);
      throw new Error(error);
    }
    if (!_.isUndefined(options.specId)) {
      if (_.isString(options.specId)) {
        self.specId = options.specId;
      } else {
        error = 'options.specId must be a String.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.numWorkers)) {
      if (_.isNumber(options.numWorkers) &&
          options.numWorkers > 0 &&
          options.numWorkers % 1 === 0) {
        self.numWorkers = options.numWorkers;
      } else {
        error = 'options.numWorkers must be a positive integer.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.sanitize)) {
      if (_.isBoolean(options.sanitize)) {
        self.sanitize = options.sanitize;
      } else {
        error = 'options.sanitize must be a boolean.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    if (!_.isUndefined(options.suppressStack)) {
      if (_.isBoolean(options.suppressStack)) {
        self.suppressStack = options.suppressStack;
      } else {
        error = 'options.suppressStack must be a boolean.';
        logger.debug('Queue(): Error during initialization', error);
        throw new Error(error);
      }
    }
    self.processingFunction = constructorArguments[2];
  } else {
    error = 'Queue can only take at most three arguments - queueRef, ' +
      'options (optional), and processingFunction.';
    logger.debug('Queue(): Error during initialization', error);
    throw new Error(error);
  }

  if (_.has(constructorArguments[0], ['tasksRef', 'specsRef'])) {
    self.tasksRef = constructorArguments[0].tasksRef;
    self.specsRef = constructorArguments[0].specsRef;
  } else if (_.isPlainObject(constructorArguments[0])) {
    error = "When ref is an object it must contain both keys 'tasksRef' and 'specsRef'";
    logger.debug('Queue(): Error during initialization', error);
    throw new Error(error);
  } else {
    self.tasksRef = constructorArguments[0].child('tasks');
    self.specsRef = constructorArguments[0].child('specs');
  }

  self.workers = [];
  for (var i = 0; i < self.numWorkers; i++) {
    var processId = (self.specId ? self.specId + ':' : '') + i;
    self.workers.push(new QueueWorker(
      self.tasksRef,
      processId,
      self.sanitize,
      self.suppressStack,
      self.processingFunction
    ));
  }

  if (_.isUndefined(self.specId)) {
    for (var j = 0; j < self.numWorkers; j++) {
      self.workers[j].setTaskSpec(DEFAULT_TASK_SPEC);
    }
    self.initialized = true;
  } else {
    self.specChangeListener = self.specsRef.child(self.specId).on(
      'value',
      function(taskSpecSnap) {
        var taskSpec = {
              startState: taskSpecSnap.child('start_state').val(),
              inProgressState: taskSpecSnap.child('in_progress_state').val(),
              finishedState: taskSpecSnap.child('finished_state').val(),
              errorState: taskSpecSnap.child('error_state').val(),
              timeout: taskSpecSnap.child('timeout').val(),
              retries: taskSpecSnap.child('retries').val()
            };

        for (var i = 0; i < self.numWorkers; i++) {
          self.workers[i].setTaskSpec(taskSpec);
        }
        self.initialized = true;
      }, /* istanbul ignore next */ function(error) {
        logger.debug('Queue(): Error connecting to Firebase reference',
          error.message);
      });
  }

  return self;
}

/**
 * Gracefully shuts down a queue.
 * @returns {RSVP.Promise} A promise fulfilled when all the worker processes
 *   have finished their current tasks and are no longer listening for new ones.
 */
Queue.prototype.shutdown = function() {
  var self = this;

  logger.debug('Queue: Shutting down');
  if (!_.isNull(self.specChangeListener)) {
    self.specsRef.child(self.specId).off('value',
      self.specChangeListener);
    self.specChangeListener = null;
  }

  return RSVP.all(_.map(self.workers, function(worker) {
    return worker.shutdown();
  }));
};

module.exports = Queue;
<|endoftext|>


<commit_before>import path from 'path'
import spawn from 'cross-spawn'
import fs from 'fs'
import jsonfile from 'jsonfile'
import Util from 'gulp-util'
//import stringify from 'stringify-object'

const BaseDirectoriesCache = `.gulp-pipeline-rails.json`
const GemfileLock = `Gemfile.lock`

const Rails = class {
  static enumerateEngines() {
    let results = spawn.sync(this.filePath('railsRunner.sh'), [this.filePath('enumerateEngines.rb')], {sdtio: 'inherit'})

    //Util.log(stringify(results))
    if(results.stderr != ''){
      throw new Error(`Ruby script error: \n${results.stderr}`)
    }
    return JSON.parse(results.stdout)
  }

  static filePath(name) {
    return path.join(__dirname, `rails/${name}`) // eslint-disable-line no-undef
  }

  /**
   * Return the baseDirectories to search for assets such as images.  In rails, this means
   *  enumerating rails engines and collecting their root paths.  This is a lengthy process
   *  because you have to startup a rails environment to enumerate the engines, so we cache
   *  the baseDirectories in a file and compare it to the Gemfile.lock's modified time.  If
   *  the Gemfile.lock changes, we throw out the cache, enumerate the engines again and write
   *  a new cache file.
   *
   * @returns {{baseDirectories: string[]}}
   */
  static baseDirectories() {
    if (!this.changed(GemfileLock, BaseDirectoriesCache)) {
      return jsonfile.readFileSync(BaseDirectoriesCache)
    }
    else {
      Util.log(`Generating baseDirectories cache...`)
      try {
        fs.unlinkSync(BaseDirectoriesCache)
      } catch (error) {
        //ignore
      }

      Util.log(`Enumerating rails engines...`)
      let engines = Rails.enumerateEngines()
      //console.log(stringify(engines))

      let baseDirectories = ['./']
      for (let key of Object.keys(engines)) {
        baseDirectories.push(engines[key])
      }

      Util.log(`Writing baseDirectories cache...`)
      let result = {baseDirectories: baseDirectories}
      jsonfile.writeFileSync(BaseDirectoriesCache, result, {spaces: 2})
      return result
    }
  }

  static changed(sourceFileName, targetFileName) {
    let sourceStat = null
    let targetStat = null
    try {
      sourceStat = fs.statSync(sourceFileName)
      targetStat = fs.statSync(targetFileName)
    }
    catch (error) {
      return true
    }

    if (sourceStat.mtime > targetStat.mtime) {
      return true
    }
    else {
      return false
    }
  }
}

export default Rails
<commit_msg>make enumerateEngines compatible with engines projects that have dummy applications in some subdirectory<commit_after>import path from 'path'
import extend from 'extend'
import glob from 'glob'
import spawn from 'cross-spawn'
import fs from 'fs'
import jsonfile from 'jsonfile'
import Util from 'gulp-util'
import stringify from 'stringify-object'

const BaseDirectoriesCache = `.gulp-pipeline-rails.json`
const GemfileLock = `Gemfile.lock`

const Rails = class {
  static enumerateEngines() {

    let results = spawn.sync(this.localPath('railsRunner.sh'), [this.localPath('enumerateEngines.rb')], {
      sdtio: 'inherit',
      cwd: this.railsAppCwd()
    })

    Util.log(stringify(results))
    if (results.stderr != '') {
      throw new Error(`Ruby script error: \n${results.stderr}`)
    }
    return JSON.parse(results.stdout)
  }

  /**
   * We need a rails app to run our rails script runner.  Since this project could be a rails engine, find a rails app somewhere in or under the cwd.
   */
  static railsAppCwd() {
    let entries = glob.sync('**/bin/rails', {realpath: true})
    if (!entries || entries.length <= 0) {
      throw new Error(`Unable to find Rails application directory based on existence of 'bin/rails'`)
    }

    if (entries.length > 1) {
      throw new Error(`railsAppCwd() should only find one rails application but found ${entries}`)
    }
    return path.join(entries[0], '../..')
  }

  static localPath(name) {
    return path.join(__dirname, `rails/${name}`) // eslint-disable-line no-undef
  }

  /**
   * Return the baseDirectories to search for assets such as images.  In rails, this means
   *  enumerating rails engines and collecting their root paths.  This is a lengthy process
   *  because you have to startup a rails environment to enumerate the engines, so we cache
   *  the baseDirectories in a file and compare it to the Gemfile.lock's modified time.  If
   *  the Gemfile.lock changes, we throw out the cache, enumerate the engines again and write
   *  a new cache file.
   *
   * @returns {{baseDirectories: string[]}}
   */
  static baseDirectories() {
    if (!this.changed(GemfileLock, BaseDirectoriesCache)) {
      return jsonfile.readFileSync(BaseDirectoriesCache)
    }
    else {
      Util.log(`Generating baseDirectories cache...`)
      try {
        fs.unlinkSync(BaseDirectoriesCache)
      } catch (error) {
        //ignore
      }

      Util.log(`Enumerating rails engines...`)
      let engines = Rails.enumerateEngines()
      //console.log(stringify(engines))

      let baseDirectories = ['./']
      for (let key of Object.keys(engines)) {
        baseDirectories.push(engines[key])
      }

      Util.log(`Writing baseDirectories cache...`)
      let result = {baseDirectories: baseDirectories}
      jsonfile.writeFileSync(BaseDirectoriesCache, result, {spaces: 2})
      return result
    }
  }

  static changed(sourceFileName, targetFileName) {
    let sourceStat = null
    let targetStat = null
    try {
      sourceStat = fs.statSync(sourceFileName)
      targetStat = fs.statSync(targetFileName)
    }
    catch (error) {
      return true
    }

    if (sourceStat.mtime > targetStat.mtime) {
      return true
    }
    else {
      return false
    }
  }
}

export default Rails
<|endoftext|>