<commit_before>/*! NProgress (c) 2013, Rico Sta. Cruz
 *  http://ricostacruz.com/nprogress */

;(function(factory) {

  if (typeof module === 'function') {
    module.exports = factory(this.jQuery || require('jquery'));
  } else {
    this.NProgress = factory(this.jQuery);
  }

})(function($) {
  var NProgress = {};

  NProgress.version = '0.1.2';

  var Settings = NProgress.settings = {
    minimum: 0.08,
    easing: 'ease',
    positionUsing: '',
    speed: 200,
    trickle: true,
    trickleRate: 0.02,
    trickleSpeed: 800,
    showSpinner: true,
    template: '

'
  };

  /**
   * Updates configuration.
   *
   *     NProgress.configure({
   *       minimum: 0.1
   *     });
   */
  NProgress.configure = function(options) {
    $.extend(Settings, options);
    return this;
  };

  /**
   * Last number.
   */

  NProgress.status = null;

  /**
   * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
   *
   *     NProgress.set(0.4);
   *     NProgress.set(1.0);
   */

  NProgress.set = function(n) {
    var started = NProgress.isStarted();

    n = clamp(n, Settings.minimum, 1);
    NProgress.status = (n === 1 ? null : n);

    var $progress = NProgress.render(!started),
        $bar      = $progress.find('[role="bar"]'),
        speed     = Settings.speed,
        ease      = Settings.easing;

    $progress[0].offsetWidth; /* Repaint */

    $progress.queue(function(next) {
      // Set positionUsing if it hasn't already been set
      if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();

      // Add transition
      $bar.css(barPositionCSS(n, speed, ease));

      if (n === 1) {
        // Fade out
        $progress.css({ transition: 'none', opacity: 1 });
        $progress[0].offsetWidth; /* Repaint */

        setTimeout(function() {
          $progress.css({ transition: 'all '+speed+'ms linear', opacity: 0 });
          setTimeout(function() {
            NProgress.remove();
            next();
          }, speed);
        }, speed);
      } else {
        setTimeout(next, speed);
      }
    });

    return this;
  };

  NProgress.isStarted = function() {
    return typeof NProgress.status === 'number';
  };

  /**
   * Shows the progress bar.
   * This is the same as setting the status to 0%, except that it doesn't go backwards.
   *
   *     NProgress.start();
   *
   */
  NProgress.start = function() {
    if (!NProgress.status) NProgress.set(0);

    var work = function() {
      setTimeout(function() {
        if (!NProgress.status) return;
        NProgress.trickle();
        work();
      }, Settings.trickleSpeed);
    };

    if (Settings.trickle) work();

    return this;
  };

  /**
   * Hides the progress bar.
   * This is the *sort of* the same as setting the status to 100%, with the
   * difference being `done()` makes some placebo effect of some realistic motion.
   *
   *     NProgress.done();
   *
   * If `true` is passed, it will show the progress bar even if its hidden.
   *
   *     NProgress.done(true);
   */

  NProgress.done = function(force) {
    if (!force && !NProgress.status) return this;

    return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
  };

  /**
   * Increments by a random amount.
   */

  NProgress.inc = function(amount) {
    var n = NProgress.status;

    if (!n) {
      return NProgress.start();
    } else {
      if (typeof amount !== 'number') {
        amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
      }

      n = clamp(n + amount, 0, 0.994);
      return NProgress.set(n);
    }
  };

  NProgress.trickle = function() {
    return NProgress.inc(Math.random() * Settings.trickleRate);
  };

  /**
   * (Internal) renders the progress bar markup based on the `template`
   * setting.
   */

  NProgress.render = function(fromStart) {
    if (NProgress.isRendered()) return $("#nprogress");
    $('html').addClass('nprogress-busy');

    var $el = $("
")
      .html(Settings.template);

    var perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0);

    $el.find('[role="bar"]').css({
      transition: 'all 0 linear',
      transform: 'translate3d('+perc+'%,0,0)'
    });

    if (!Settings.showSpinner)
      $el.find('[role="spinner"]').remove();

    $el.appendTo(document.body);

    return $el;
  };

  /**
   * Removes the element. Opposite of render().
   */

  NProgress.remove = function() {
    $('html').removeClass('nprogress-busy');
    $('#nprogress').remove();
  };

  /**
   * Checks if the progress bar is rendered.
   */

  NProgress.isRendered = function() {
    return ($("#nprogress").length > 0);
  };

  /**
   * Determine which positioning CSS rule to use.
   */

  NProgress.getPositioningCSS = function() {
    // Sniff on document.body.style
    var bodyStyle = document.body.style;

    // Sniff prefixes
    var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
                       ('MozTransform' in bodyStyle) ? 'Moz' :
                       ('msTransform' in bodyStyle) ? 'ms' :
                       ('OTransform' in bodyStyle) ? 'O' : '';

    if (vendorPrefix + 'Perspective' in bodyStyle) {
      // Modern browsers with 3D support, e.g. Webkit, IE10
      return 'translate3d';
    } else if (vendorPrefix + 'Transform' in bodyStyle) {
      // Browsers without 3D support, e.g. IE9
      return 'translate';
    } else {
      // Browsers without translate() support, e.g. IE7-8
      return 'margin';
    }
  };

  /**
   * Helpers
   */

  function clamp(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
  }

  /**
   * (Internal) converts a percentage (`0..1`) to a bar translateX
   * percentage (`-100%..0%`).
   */

  function toBarPerc(n) {
    return (-1 + n) * 100;
  }

  /**
   * (Internal) returns the correct CSS for changing the bar's
   * position given an n percentage, and speed and ease from Settings
   */

  function barPositionCSS(n, speed, ease) {
    var barCSS;

    if (Settings.positionUsing === 'translate3d') {
      barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
    } else if (Settings.positionUsing === 'translate') {
      barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
    } else {
      barCSS = { 'margin-left': toBarPerc(n)+'%' };
    }

    barCSS.transition = 'all '+speed+'ms '+ease;

    return barCSS;
  }

  return NProgress;
});

<commit_msg>add support for AMD<commit_after>/*! NProgress (c) 2013, Rico Sta. Cruz
 *  http://ricostacruz.com/nprogress */

;(function(factory) {

  if (typeof module === 'function') {
    module.exports = factory(this.jQuery || require('jquery'));
  } else if (typeof define === 'function' && define.amd) {
    define(['jquery'], function($) {
      return factory($);
    });
  } else {
    this.NProgress = factory(this.jQuery);
  }

})(function($) {
  var NProgress = {};

  NProgress.version = '0.1.2';

  var Settings = NProgress.settings = {
    minimum: 0.08,
    easing: 'ease',
    positionUsing: '',
    speed: 200,
    trickle: true,
    trickleRate: 0.02,
    trickleSpeed: 800,
    showSpinner: true,
    template: '

'
  };

  /**
   * Updates configuration.
   *
   *     NProgress.configure({
   *       minimum: 0.1
   *     });
   */
  NProgress.configure = function(options) {
    $.extend(Settings, options);
    return this;
  };

  /**
   * Last number.
   */

  NProgress.status = null;

  /**
   * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
   *
   *     NProgress.set(0.4);
   *     NProgress.set(1.0);
   */

  NProgress.set = function(n) {
    var started = NProgress.isStarted();

    n = clamp(n, Settings.minimum, 1);
    NProgress.status = (n === 1 ? null : n);

    var $progress = NProgress.render(!started),
        $bar      = $progress.find('[role="bar"]'),
        speed     = Settings.speed,
        ease      = Settings.easing;

    $progress[0].offsetWidth; /* Repaint */

    $progress.queue(function(next) {
      // Set positionUsing if it hasn't already been set
      if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();

      // Add transition
      $bar.css(barPositionCSS(n, speed, ease));

      if (n === 1) {
        // Fade out
        $progress.css({ transition: 'none', opacity: 1 });
        $progress[0].offsetWidth; /* Repaint */

        setTimeout(function() {
          $progress.css({ transition: 'all '+speed+'ms linear', opacity: 0 });
          setTimeout(function() {
            NProgress.remove();
            next();
          }, speed);
        }, speed);
      } else {
        setTimeout(next, speed);
      }
    });

    return this;
  };

  NProgress.isStarted = function() {
    return typeof NProgress.status === 'number';
  };

  /**
   * Shows the progress bar.
   * This is the same as setting the status to 0%, except that it doesn't go backwards.
   *
   *     NProgress.start();
   *
   */
  NProgress.start = function() {
    if (!NProgress.status) NProgress.set(0);

    var work = function() {
      setTimeout(function() {
        if (!NProgress.status) return;
        NProgress.trickle();
        work();
      }, Settings.trickleSpeed);
    };

    if (Settings.trickle) work();

    return this;
  };

  /**
   * Hides the progress bar.
   * This is the *sort of* the same as setting the status to 100%, with the
   * difference being `done()` makes some placebo effect of some realistic motion.
   *
   *     NProgress.done();
   *
   * If `true` is passed, it will show the progress bar even if its hidden.
   *
   *     NProgress.done(true);
   */

  NProgress.done = function(force) {
    if (!force && !NProgress.status) return this;

    return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
  };

  /**
   * Increments by a random amount.
   */

  NProgress.inc = function(amount) {
    var n = NProgress.status;

    if (!n) {
      return NProgress.start();
    } else {
      if (typeof amount !== 'number') {
        amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
      }

      n = clamp(n + amount, 0, 0.994);
      return NProgress.set(n);
    }
  };

  NProgress.trickle = function() {
    return NProgress.inc(Math.random() * Settings.trickleRate);
  };

  /**
   * (Internal) renders the progress bar markup based on the `template`
   * setting.
   */

  NProgress.render = function(fromStart) {
    if (NProgress.isRendered()) return $("#nprogress");
    $('html').addClass('nprogress-busy');

    var $el = $("
")
      .html(Settings.template);

    var perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0);

    $el.find('[role="bar"]').css({
      transition: 'all 0 linear',
      transform: 'translate3d('+perc+'%,0,0)'
    });

    if (!Settings.showSpinner)
      $el.find('[role="spinner"]').remove();

    $el.appendTo(document.body);

    return $el;
  };

  /**
   * Removes the element. Opposite of render().
   */

  NProgress.remove = function() {
    $('html').removeClass('nprogress-busy');
    $('#nprogress').remove();
  };

  /**
   * Checks if the progress bar is rendered.
   */

  NProgress.isRendered = function() {
    return ($("#nprogress").length > 0);
  };

  /**
   * Determine which positioning CSS rule to use.
   */

  NProgress.getPositioningCSS = function() {
    // Sniff on document.body.style
    var bodyStyle = document.body.style;

    // Sniff prefixes
    var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
                       ('MozTransform' in bodyStyle) ? 'Moz' :
                       ('msTransform' in bodyStyle) ? 'ms' :
                       ('OTransform' in bodyStyle) ? 'O' : '';

    if (vendorPrefix + 'Perspective' in bodyStyle) {
      // Modern browsers with 3D support, e.g. Webkit, IE10
      return 'translate3d';
    } else if (vendorPrefix + 'Transform' in bodyStyle) {
      // Browsers without 3D support, e.g. IE9
      return 'translate';
    } else {
      // Browsers without translate() support, e.g. IE7-8
      return 'margin';
    }
  };

  /**
   * Helpers
   */

  function clamp(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
  }

  /**
   * (Internal) converts a percentage (`0..1`) to a bar translateX
   * percentage (`-100%..0%`).
   */

  function toBarPerc(n) {
    return (-1 + n) * 100;
  }

  /**
   * (Internal) returns the correct CSS for changing the bar's
   * position given an n percentage, and speed and ease from Settings
   */

  function barPositionCSS(n, speed, ease) {
    var barCSS;

    if (Settings.positionUsing === 'translate3d') {
      barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
    } else if (Settings.positionUsing === 'translate') {
      barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
    } else {
      barCSS = { 'margin-left': toBarPerc(n)+'%' };
    }

    barCSS.transition = 'all '+speed+'ms '+ease;

    return barCSS;
  }

  return NProgress;
});

<|endoftext|>


<commit_before>var webpage = require('webpage');
var system = require('system');

var url;

if (system.args.length < 2) {
    console.log('Need URL!');
    phantom.exit();
}

url = system.args[1];

var page = webpage.create();
page.onConsoleMessage = function(msg) {
    if (msg === 'PHANTOMJS EXITING') phantom.exit();
    console.log(msg);
};
page.open(url, function() {
    setTimeout(function() {
        page.evaluate(function() {
            node.events.ee.ng.on(
                'GAME_OVER', function() { console.log('PHANTOMJS EXITING'); });
        });
    }, 1000);
});
<commit_msg>debug for travis<commit_after>var webpage = require('webpage');
var system = require('system');

console.log('1');

var url;

if (system.args.length < 2) {
    console.log('Need URL!');
    phantom.exit();
}

url = system.args[1];

console.log('2');

var page = webpage.create();

console.log('3');

page.onConsoleMessage = function(msg) {

    console.log(msg);

    if (msg === 'PHANTOMJS EXITING') phantom.exit();
    console.log(msg);
};

console.log('4');

page.open(url, function() {

console.log('5');

    setTimeout(function() {

        console.log('6');

        page.evaluate(function() {

            console.log('7');

            node.events.ee.ng.on(
                'GAME_OVER', function() { console.log('PHANTOMJS EXITING'); });
        });
    }, 1000);
});
<|endoftext|>


<commit_before>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require('request');
var net = require('net')
/* TODO:
 * - send keepalive
 * - auto reconnect on disconnect
 * - wait for success after command and then send the next
 * - support more devices
 */

var DEBUG = true;

PilightDeviceTypes = {
  RAW: 0,
  SWITCH: 1,
  DIMMER: 2,
  WEATHER: 3,
  RELAY: 4,
  SCREEN: 5,
  CONTACT: 6,
  PENDINGSW: 7,
  DATETIME: 8,
  XBMC: 9,
  LIRC: 10,
  WEBCAM: 11
}

function PilightPlatform(log, config){
  this.name = config['name']
  this.host = config['host']
  this.port = config['port']
  this.protocol = config['protocol']
  this.http_port = config['http-port']

  this.log = log;
}

function PilightClient(log, platform) {
    var that = this;

    this.log = log;
    this.platform = platform;
    this.registry = [];

    this.connect();
    this.startHeartBeat();
}

PilightClient.prototype = {
  send: function(cmd) {
    var msg = JSON.stringify(cmd);
    this.log("[PilightClient.send] "+msg);
    this.socket.write(msg);
  },

  register: function(idx, accessory) {
    this.registry[idx] = accessory;
  },

  updateDevices: function(devices, values) {
    for(var device of devices) {
      var dev = this.registry[device]
      if(dev === undefined || dev.length === 0) {
        continue
      }
      this.log("[PilightClient.updateDevices] External update on device "
               + device);
      dev.updateCharacteristics(values);
    }
  },

  startHeartBeat: function() {
    var that = this;
    var cb = function() { that.socket.write("HEART\n", "utf-8"); };
    this.heartBeatInterval = setInterval(cb, 20000);
  },

  onBeat: function() {
    if(DEBUG) { this.log("BEAT");}

    if(this.heartBeatTimeout) {
      clearTimeout(this.heartBeatTimeout);
    }

    var that = this;
    var reconnectDelay = 30000;
    var reconnect = function() {
      that.log('[PilightClient.reconnect] Trying reconnect');
      try{
        that.socket.end();
        that.socket.destroy();
      }
      finally {
        that.connect();
      }
    }
    this.heartBeatTimeout = setTimeout(reconnect, reconnectDelay);
  },

  connect: function() {
    var that = this;
    var bootstrap = function() {
      that.log('connected to server!');
      var cmd = {
        "action": "identify",
        "options": {
          "config": 1
        }
      };
      that.send(cmd);
    }

    this.socket = net.connect({host:this.platform.host, port:this.platform.port}, bootstrap);

    this.socket.on('data', function(data) {
      data = data.toString();
      for(msg of data.split('\n')) {
        if(msg.length === 0) { continue; }

        if(msg === "BEAT") {
          that.onBeat();
          continue;
        }

        try {
          data = JSON.parse(data);
        } catch(e) {
          if (e instanceof SyntaxError) { that.log("SyntaxError in " + msg); }
          else { that.log("Error " + JSON.stringify(e)); }
          continue;
        }
        if(data['origin'] !== 'update') {
          continue;
        }
        that.updateDevices(data['devices'], data['values']);
      }
    });

    this.socket.on('end', function() {
      clearTimeout(that.heartBeatTimeout);
      clearTimeout(that.heartBeatInterval);
      that.log('disconnected from server');
    });
  }
}

PilightPlatform.prototype = {
  accessories: function(callback) {
    // XXX request config over socket crashes pilight
    var url = this.protocol + '://' + this.host + ':'
                + this.http_port +'/config';

    this.log("[PilightPlatform.accessories] Fetching pilight devices.");
    this.log("[PilightPlatform.accessories] Fetching from: "+url);

    var collected = [];
    var that = this;
    var client = new PilightClient(this.log, this);

    request.get({url: url, json:true}, function(err, response, json) {
      var accessory = null;

      if (!err && response.statusCode == 200) {

        for (var device_name in json['gui']) {

          var gui = json['gui'][device_name]
          var device = json['devices'][device_name]
          var accessory;

          for (var proto of device['protocol']) {

            that.log("[PilightPlatform.accessories] " + gui['name'] + " : " + proto);

            switch(proto) {
              case "elro_800_switch":
                accessory = PilightSwitch;
                break;
              case 'openweathermap':
                accessory = PilightThermostat;
                break;
              default: // arping, sunriseset, datetime, generic_label
                that.log("Unsupported pilight device (" + gui['name']
                         + ") with protocol " + device['protocol']);
                continue;
            }
            that.log("Adding pilight device (" + gui['name'] + ") with protocol "
                     + device['protocol']);
            collected.push(
                    new accessory(that.log, device_name, gui['name'], proto, device, client));
            break;  // we found a supported protocol, so we are done with this device
          }
        }
        callback(collected);
      } else {
        that.log("There was a problem requesting the configuration from pilight.");
      }
    });
  }
}

function PilightSwitch(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

PilightSwitch.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightSwitch.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue = (values['state'] === 'on')*1;
    for(var c of this.registry) {
      if(c.value === newValue) {
        this.log("[PilightSwitch.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightSwitch.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
      c.updateValue(newValue);
    }
  },

  setPowerState: function(powerOn) {
    var that = this;
    this.log("[PilightSwitch.setPowerState] "+powerOn);
    this.client.send({
          "action": "control",
          "code": {
            "device": that.idx,
            "state": (powerOn)?"on":"off"
          }});
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "pilight",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "BIVAB-PO-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.OUTLET_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + " outlet service",
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.POWER_STATE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
        perms: ["pw","pr","ev"],
        format: "bool",
        initialValue: (this.state==='on')?1:0,
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Change the power state of the outlet",
        designedMaxLength: 1
      },{
        cType: types.OUTLET_IN_USE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        perms: ["pr","ev"],
        format: "bool",
        initialValue: true,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Turn On the Light",
        designedMaxLength: 1
      }]
    }];
  }
};

// XXX refactory and merge with Switch
function PilightThermostat(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

// XXX merge with Switch prototype and extract common prototype
PilightThermostat.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightThermostat.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue;
    for(var c of this.registry) {
      switch(c.type) {
        case types.CURRENT_TEMPERATURE_CTYPE:
          newValue = values['temperature'];
          break;
        case types.CURRENT_RELATIVE_HUMIDITY_CTYPE:
          newValue = values['humidity'];
          break;
      }
      if(newValue === undefined || c.value === newValue) {
        this.log("[PilightThermostat.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightThermostat.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
    }
    c.updateValue(newValue);
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + "Thermostat 1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
      },{
      sType: types.THERMOSTAT_STYPE,
      characteristics: [{
          cType: types.NAME_CTYPE,
          onUpdate: null,
          perms: ["pr"],
          format: "string",
          initialValue: that.name + "Thermostat Control",
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Bla",
          designedMaxLength: 255
        },{
          cType: types.CURRENTHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Mode",
          designedMaxLength: 1,
          designedMinValue: 0,
          designedMaxValue: 2,
          designedMinStep: 1,
        },{
          cType: types.TARGETHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Mode",
          designedMinValue: 0,
          designedMaxValue: 3,
          designedMinStep: 1,
        },{
          cType: types.CURRENT_TEMPERATURE_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Temperature",
          unit: "celsius"
        },{
          cType: types.TARGET_TEMPERATURE_CTYPE,
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Temperature",
          designedMinValue: 16,
          designedMaxValue: 38,
          designedMinStep: 1,
          unit: "celsius"
        },{
          cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "float",
          initialValue: parseFloat(that.info['humidity']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current relative humidity",
          unit: "percentage",
          designedMinValue: 0,
          designedMaxValue: 100,
          designedMinStep: 1
        },{
          cType: types.TEMPERATURE_UNITS_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Unit",
        }]
      }];
  }
};
module.exports.accessory = [PilightSwitch, PilightThermostat];
module.exports.platform = PilightPlatform;
<commit_msg>Added brennenstuhl protocol as a pilight switch<commit_after>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require('request');
var net = require('net')
/* TODO:
 * - send keepalive
 * - auto reconnect on disconnect
 * - wait for success after command and then send the next
 * - support more devices
 */

var DEBUG = true;

PilightDeviceTypes = {
  RAW: 0,
  SWITCH: 1,
  DIMMER: 2,
  WEATHER: 3,
  RELAY: 4,
  SCREEN: 5,
  CONTACT: 6,
  PENDINGSW: 7,
  DATETIME: 8,
  XBMC: 9,
  LIRC: 10,
  WEBCAM: 11
}

function PilightPlatform(log, config){
  this.name = config['name']
  this.host = config['host']
  this.port = config['port']
  this.protocol = config['protocol']
  this.http_port = config['http-port']

  this.log = log;
}

function PilightClient(log, platform) {
    var that = this;

    this.log = log;
    this.platform = platform;
    this.registry = [];

    this.connect();
    this.startHeartBeat();
}

PilightClient.prototype = {
  send: function(cmd) {
    var msg = JSON.stringify(cmd);
    this.log("[PilightClient.send] "+msg);
    this.socket.write(msg);
  },

  register: function(idx, accessory) {
    this.registry[idx] = accessory;
  },

  updateDevices: function(devices, values) {
    for(var device of devices) {
      var dev = this.registry[device]
      if(dev === undefined || dev.length === 0) {
        continue
      }
      this.log("[PilightClient.updateDevices] External update on device "
               + device);
      dev.updateCharacteristics(values);
    }
  },

  startHeartBeat: function() {
    var that = this;
    var cb = function() { that.socket.write("HEART\n", "utf-8"); };
    this.heartBeatInterval = setInterval(cb, 20000);
  },

  onBeat: function() {
    if(DEBUG) { this.log("BEAT");}

    if(this.heartBeatTimeout) {
      clearTimeout(this.heartBeatTimeout);
    }

    var that = this;
    var reconnectDelay = 30000;
    var reconnect = function() {
      that.log('[PilightClient.reconnect] Trying reconnect');
      try{
        that.socket.end();
        that.socket.destroy();
      }
      finally {
        that.connect();
      }
    }
    this.heartBeatTimeout = setTimeout(reconnect, reconnectDelay);
  },

  connect: function() {
    var that = this;
    var bootstrap = function() {
      that.log('connected to server!');
      var cmd = {
        "action": "identify",
        "options": {
          "config": 1
        }
      };
      that.send(cmd);
    }

    this.socket = net.connect({host:this.platform.host, port:this.platform.port}, bootstrap);

    this.socket.on('data', function(data) {
      data = data.toString();
      for(msg of data.split('\n')) {
        if(msg.length === 0) { continue; }

        if(msg === "BEAT") {
          that.onBeat();
          continue;
        }

        try {
          data = JSON.parse(data);
        } catch(e) {
          if (e instanceof SyntaxError) { that.log("SyntaxError in " + msg); }
          else { that.log("Error " + JSON.stringify(e)); }
          continue;
        }
        if(data['origin'] !== 'update') {
          continue;
        }
        that.updateDevices(data['devices'], data['values']);
      }
    });

    this.socket.on('end', function() {
      clearTimeout(that.heartBeatTimeout);
      clearTimeout(that.heartBeatInterval);
      that.log('disconnected from server');
    });
  }
}

PilightPlatform.prototype = {
  accessories: function(callback) {
    // XXX request config over socket crashes pilight
    var url = this.protocol + '://' + this.host + ':'
                + this.http_port +'/config';

    this.log("[PilightPlatform.accessories] Fetching pilight devices.");
    this.log("[PilightPlatform.accessories] Fetching from: "+url);

    var collected = [];
    var that = this;
    var client = new PilightClient(this.log, this);

    request.get({url: url, json:true}, function(err, response, json) {
      var accessory = null;

      if (!err && response.statusCode == 200) {

        for (var device_name in json['gui']) {

          var gui = json['gui'][device_name]
          var device = json['devices'][device_name]
          var accessory;

          for (var proto of device['protocol']) {

            that.log("[PilightPlatform.accessories] " + gui['name'] + " : " + proto);

            switch(proto) {
              case "brennenstuhl":
              case "elro_800_switch":
                accessory = PilightSwitch;
                break;
              case 'openweathermap':
                accessory = PilightThermostat;
                break;
              default: // arping, sunriseset, datetime, generic_label
                that.log("Unsupported pilight device (" + gui['name']
                         + ") with protocol " + device['protocol']);
                continue;
            }
            that.log("Adding pilight device (" + gui['name'] + ") with protocol "
                     + device['protocol']);
            collected.push(
                    new accessory(that.log, device_name, gui['name'], proto, device, client));
            break;  // we found a supported protocol, so we are done with this device
          }
        }
        callback(collected);
      } else {
        that.log("There was a problem requesting the configuration from pilight.");
      }
    });
  }
}

function PilightSwitch(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

PilightSwitch.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightSwitch.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue = (values['state'] === 'on')*1;
    for(var c of this.registry) {
      if(c.value === newValue) {
        this.log("[PilightSwitch.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightSwitch.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
      c.updateValue(newValue);
    }
  },

  setPowerState: function(powerOn) {
    var that = this;
    this.log("[PilightSwitch.setPowerState] "+powerOn);
    this.client.send({
          "action": "control",
          "code": {
            "device": that.idx,
            "state": (powerOn)?"on":"off"
          }});
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "pilight",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "BIVAB-PO-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.OUTLET_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + " outlet service",
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.POWER_STATE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
        perms: ["pw","pr","ev"],
        format: "bool",
        initialValue: (this.state==='on')?1:0,
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Change the power state of the outlet",
        designedMaxLength: 1
      },{
        cType: types.OUTLET_IN_USE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        perms: ["pr","ev"],
        format: "bool",
        initialValue: true,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Turn On the Light",
        designedMaxLength: 1
      }]
    }];
  }
};

// XXX refactory and merge with Switch
function PilightThermostat(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

// XXX merge with Switch prototype and extract common prototype
PilightThermostat.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightThermostat.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue;
    for(var c of this.registry) {
      switch(c.type) {
        case types.CURRENT_TEMPERATURE_CTYPE:
          newValue = values['temperature'];
          break;
        case types.CURRENT_RELATIVE_HUMIDITY_CTYPE:
          newValue = values['humidity'];
          break;
      }
      if(newValue === undefined || c.value === newValue) {
        this.log("[PilightThermostat.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightThermostat.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
    }
    c.updateValue(newValue);
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + "Thermostat 1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
      },{
      sType: types.THERMOSTAT_STYPE,
      characteristics: [{
          cType: types.NAME_CTYPE,
          onUpdate: null,
          perms: ["pr"],
          format: "string",
          initialValue: that.name + "Thermostat Control",
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Bla",
          designedMaxLength: 255
        },{
          cType: types.CURRENTHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Mode",
          designedMaxLength: 1,
          designedMinValue: 0,
          designedMaxValue: 2,
          designedMinStep: 1,
        },{
          cType: types.TARGETHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Mode",
          designedMinValue: 0,
          designedMaxValue: 3,
          designedMinStep: 1,
        },{
          cType: types.CURRENT_TEMPERATURE_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Temperature",
          unit: "celsius"
        },{
          cType: types.TARGET_TEMPERATURE_CTYPE,
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Temperature",
          designedMinValue: 16,
          designedMaxValue: 38,
          designedMinStep: 1,
          unit: "celsius"
        },{
          cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "float",
          initialValue: parseFloat(that.info['humidity']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current relative humidity",
          unit: "percentage",
          designedMinValue: 0,
          designedMaxValue: 100,
          designedMinStep: 1
        },{
          cType: types.TEMPERATURE_UNITS_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Unit",
        }]
      }];
  }
};
module.exports.accessory = [PilightSwitch, PilightThermostat];
module.exports.platform = PilightPlatform;
<|endoftext|>


<commit_before>/**
 *    CoTag Orbicular
 *    A more or less pure CSS, circular, progress bar
 *
 *   Copyright (c) 2014 CoTag Media.
 *
 *    @author     Stephen von Takach <steve@cotag.me>
 *    @copyright  2014 cotag.me
 *
 *
 *     References:
 *        * http://fromanegg.com/post/41302147556/100-pure-css-radial-progress-bar
 *        * https://medium.com/@andsens/radial-progress-indicator-using-css-a917b80c43f9
 *
 **/

(function () {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define('Orbicular', ['angular'], angularOrbicular);
  } else {
    angularOrbicular(angular);
  }

  function angularOrbicular(angular) {
    return angular.module('Orbicular', []).

      // isolated circular progress bar
      directive('orbicular', ['$window', function (windowRef) {

        // Constants here reduce memory requirements of each circle
        var $window = angular.element(windowRef),

          // Cache event strings
          resizeEvents = 'orientationchange resize',

          // Set the rotation of the square
          updateProgress = function (circles, fix, pos) {
            // If total is 0 or negative it can lead to Infinity or NaN values
            if (!isFinite(pos)) {
              pos = 0;
            }

            circles.css({
              '-webkit-transform': 'rotate(' + pos + 'deg)',
              '-moz-transform': 'rotate(' + pos + 'deg)',
              '-ms-transform': 'rotate(' + pos + 'deg)',
              '-o-transform': 'rotate(' + pos + 'deg)',
              'transform': 'rotate(' + pos + 'deg)'
            });
            pos = pos * 2;
            fix.css({
              '-webkit-transform': 'rotate(' + pos + 'deg)',
              '-moz-transform': 'rotate(' + pos + 'deg)',
              '-ms-transform': 'rotate(' + pos + 'deg)',
              '-o-transform': 'rotate(' + pos + 'deg)',
              'transform': 'rotate(' + pos + 'deg)'
            });
          };

        return {
          template:
              '
' +
                '

' +
              '
' +
              '
' +
                '

' +
                '

' +
              '
' +

              // optional shadow
              '

' +
              '
' +
                '
' +
                  '
' +
                    '

' +
                  '
' +
                '
' +
              '
',
          transclude: true,
          restrict: 'EA',
          scope: {
            current: '=progression',
            total: '='
          },
          link: function (scope, element, attrs) {
            var circles = [],
              fix,

              // Width must be an even number of pixels for the effect to work.
              setWidth = function () {
                var width = element.prop('offsetWidth');
                element.css('font-size', width - (width % 2) + 'px');
              },

              update = function () {
                updateProgress(circles, fix,
                  Math.min(1, scope.current / scope.total) * 180.0
                );
              };

            // circles = element.find('div.co-full, div.co-fill')
            circles.push(element.children()[0]);
            circles.push(
              angular.element(
                element.children()[0]
              ).children()[0]
            );
            circles.push(
              angular.element(
                element.children()[1]
              ).children()[0]
            );
            circles = angular.element(circles);

            // fix = element.find('div.co-fix')
            fix = angular.element(
              angular.element(
                element.children()[1]
              ).children()[1]
            );

            // we have to use em's for the clip function to work like a percentage
            // so we have to manually perform the resize based on width
            setWidth();
            scope.$on('orb width', setWidth); // for programmatic resizing

            // Optionally resize
            if (attrs.hasOwnProperty('resize')) {
              scope.$on('$destroy', function () {
                $window.off(resizeEvents, setWidth);
              });
              $window.on(resizeEvents, setWidth);
            }

            // we watch for changes to the progress indicator of the parent scope
            scope.$watch('current', update);
            scope.$watch('total', update);
          }
        };
      }]);
  }
}());
<commit_msg>Added 'direction' attribute to choose clockwise or counterclockwise<commit_after>/**
 *    CoTag Orbicular
 *    A more or less pure CSS, circular, progress bar
 *
 *   Copyright (c) 2014 CoTag Media.
 *
 *    @author     Stephen von Takach <steve@cotag.me>
 *    @copyright  2014 cotag.me
 *
 *
 *     References:
 *        * http://fromanegg.com/post/41302147556/100-pure-css-radial-progress-bar
 *        * https://medium.com/@andsens/radial-progress-indicator-using-css-a917b80c43f9
 *
 **/

(function () {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define('Orbicular', ['angular'], angularOrbicular);
  } else {
    angularOrbicular(angular);
  }

  function angularOrbicular(angular) {
    return angular.module('Orbicular', []).

      // isolated circular progress bar
      directive('orbicular', ['$window', function (windowRef) {

        // Constants here reduce memory requirements of each circle
        var $window = angular.element(windowRef),

          // Cache event strings
          resizeEvents = 'orientationchange resize',

          // Set the rotation of the square
          updateProgress = function (circles, pos, direction) {
            // If total is 0 or negative it can lead to Infinity or NaN values
            if (!isFinite(pos)) {
              pos = 0;
            }

            /**
             * circles are:
             * 0: .co-full
             * 1: .co-full > .co-fill
             * 2: .co-half
             * 3: .co-half > .co-fill
             * 4: .co-half > .co-fix
             **/
            if ( direction !== undefined && direction == 'counterclockwise' )
              var modifiers = [-2,1,-1,1,2];
            else
              var modifiers = [1,1,0,1,2];

            var modifiedPos, circle;
            for (var i=0;i<circles.length; i++) {
              if (pos==0) continue;
              modifiedPos = pos * modifiers[i];
              circle = angular.element(circles[i]);
              circle.css({
                '-webkit-transform': 'translate3d(0, 0, 0) rotate(' + modifiedPos + 'deg)',
                '-moz-transform': 'translate3d(0, 0, 0) rotate(' + modifiedPos + 'deg)',
                '-ms-transform': 'translate3d(0, 0, 0) rotate(' + modifiedPos + 'deg)',
                '-o-transform': 'translate3d(0, 0, 0) rotate(' + modifiedPos + 'deg)',
                'transform': 'translate3d(0, 0, 0) rotate(' + modifiedPos + 'deg)'
              });
            }
          };

        return {
          template:
              '
' +
                '

' +
              '
' +
              '
' +
                '

' +
                '

' +
              '
' +

              // optional shadow
              '

' +
              '
' +
                '
' +
                  '
' +
                    '

' +
                  '
' +
                '
' +
              '
',
          transclude: true,
          restrict: 'EA',
          scope: {
            current: '=progression',
            total: '='
          },
          link: function (scope, element, attrs) {
            var circles = [],
              // Width must be an even number of pixels for the effect to work.
              setWidth = function () {
                var width = element.prop('offsetWidth');
                element.css('font-size', width - (width % 2) + 'px');
              },

              update = function () {
                updateProgress(circles,
                  Math.min(1, scope.current / scope.total) * 180.0,
                  attrs.direction
                );
              };

            // circles = element.find('div.co-full, div.co-fill')
            circles.push(element.children()[0]);
            circles.push(
              angular.element(
                element.children()[0]
              ).children()[0]
            );
            circles.push(element.children()[1]);
            circles.push(
              angular.element(
                element.children()[1]
              ).children()[0]
            );
            // fix = element.find('div.co-fix')
            circles.push(
              angular.element(
                element.children()[1]
              ).children()[1]
            );

            //circles = angular.element(circles);

            // we have to use em's for the clip function to work like a percentage
            // so we have to manually perform the resize based on width
            setWidth();
            scope.$on('orb width', setWidth); // for programmatic resizing

            // Optionally resize
            if (attrs.hasOwnProperty('resize')) {
              scope.$on('$destroy', function () {
                $window.off(resizeEvents, setWidth);
              });
              $window.on(resizeEvents, setWidth);
            }

            // we watch for changes to the progress indicator of the parent scope
            scope.$watch('current', update);
            scope.$watch('total', update);
          }
        };
      }]);
  }
}());
<|endoftext|>


<commit_before>(function(window, factory) {
	var dom = factory(window, window.document);
	window.dom = dom;
	if(typeof module == 'object' && module.exports){
		module.exports = dom;
	}
}(typeof window != 'undefined' ? window : this , function(window, document) {
	'use strict';
	var slice = [].slice;
	var dom = function(elements){

		if(typeof elements == 'string'){
			return dom.q(elements);
		}
		if(!this){
			return new dom(elements);
		}

		if(!Array.isArray(elements)){
			if(!elements){
				elements = [];
			} else if(elements.nodeName || !('length' in elements) || elements == window){
				elements = [elements];
			} else {
				elements = slice.call(elements);
			}
		}

		this.elements = elements;
		this.length = this.elements.length || 0;
	};
	var fn = dom.prototype;
	var tween = function(element, endProps, options){

		var easing, isStopped, isJumpToEnd, bezierArgs;
		var start = Date.now();
		var elementStyle = element.style;
		var startProps = {};
		var stop = function(clearQueue, jumpToEnd){
			isStopped = true;
			if(jumpToEnd){
				isJumpToEnd = true;
				step();
			}
		};
		var step = function(){
			var prop, value, eased;
			var pos = (Date.now() - start) / options.duration;
			if(pos > 1 || isJumpToEnd){
				pos = 1;
			}

			if(!isStopped){
				eased = easing(pos);

				for(prop in startProps){
					value = (endProps[prop] - startProps[prop]) * eased + startProps[prop];

					if(prop in elementStyle){
						elementStyle[prop] = value +'px';
					} else {
						element[prop] = value;
					}
				}
			}

			if(pos < 1){
				if(!isStopped){
					requestAnimationFrame(step);
				} else if(options.always){
					options.always.call(element);
				}
			} else{
				if(element._rbTweenStop){
					delete element._rbTweenStop;
				}
				if(options.complete){
					options.complete.call(element);
				}
				if(options.always){
					options.always.call(element);
				}
			}
		};

		tween.getStartValues(element, elementStyle, startProps, endProps);

		options = Object.assign({duration: 400, easing: 'ease'}, options || {});

		if(window.BezierEasing && !BezierEasing.css[options.easing] && (bezierArgs = options.easing.match(/([0-9\.]+)/g)) && bezierArgs.length == 4){
			bezierArgs = bezierArgs.map(function(str){
				return parseFloat(str);
			});
			BezierEasing.css[options.easing] = BezierEasing.apply(this, bezierArgs);
		}

		easing = window.BezierEasing && (BezierEasing.css[options.easing] || BezierEasing.css.ease) || function(pos){
				return pos;
			};

		element._rbTweenStop = stop;

		requestAnimationFrame(step);
	};

	tween.getStartValues = function(element, elementStyle, startProps, endProps){
		var prop;
		for(prop in endProps){
			if(prop in elementStyle){
				startProps[prop] = parseFloat(getComputedStyle(element).getPropertyValue(prop));
			} else {
				startProps[prop] = element[prop];
			}
		}
	};

	dom.fn = dom.prototype;

	dom.q = function(sel, context){
		return new dom( (context || document).querySelectorAll(sel) );
	};

	dom.Event = function(type, options){
		var event;
		if(!options){
			options = {};
		}

		if(options.bubbles == null){
			options.bubbles = true;
		}
		event = new CustomEvent(type, options);

		if(!event.isDefaultPrevented){
			event.isDefaultPrevented = function(){
				return event.defaultPrevented;
			};
		}

		return event;
	};

	fn.find = function(sel){
		var array = [];
		this.elements.forEach(function(elem){
			var i, len;
			var elements = elem.querySelectorAll(sel);
			for(i = 0, len = elements.length; i < len; i++){
				if(array.indexOf(elements[i]) == -1){
					array.push(elements[i]);
				}
			}
		});

		return new dom( array );
	};

	fn.closest = function(sel){
		var array = [];
		this.elements.forEach(function(elem){
			var element = elem.closest(sel);
			if(array.indexOf(element) == -1){
				array.push(element);
			}
		});
		return new dom( array );
	};

	fn.get = function(number){
		return arguments.length ? this.elements[number] : this.elements;
	};

	[['on', 'addEventListener'], ['off', 'removeEventListener']].forEach(function(action){
		dom.fn[action[0]] = function(type, sel, fn, capture){
			var useFn;
			if(typeof sel == 'function'){
				capture = fn;
				fn = sel;
				sel = null;
			}

			if(sel){
				if(!fn[sel]){
					fn[sel] = function(e){
						e.delegateTarget = e.target.closest(sel);
						if(!e.delegateTarget){return;}
						return fn.apply(this, arguments);
					};
				}
				useFn = fn[sel];
			} else {
				useFn = fn;
			}

			this.elements.forEach(function(elem){
				elem[action[1]](type, useFn, capture || false);
			});

			return this;
		};
	});

	fn.css = function(style, pseudo){
		var elem;
		if(typeof style == 'string'){
			elem = this.elements[0];
			return elem && getComputedStyle(elem, pseudo).getPropertyValue(style);
		}
		this.elements.forEach(function(elem){
			var prop;
			var eStyle = elem.style;
			for(prop in style){
				eStyle[prop] = style[prop];
			}
		});
		return this;
	};

	fn.prop = function(props){
		var elem;
		if(typeof props == 'string'){
			elem = this.elements[0];
			return elem && elem[props];
		}
		this.elements.forEach(function(elem){
			var prop;
			for(prop in props){
				elem[prop] = props[prop];
			}
		});
		return this;
	};

	fn.attr = function(attrs){
		var elem;
		if(typeof attrs == 'string'){
			elem = this.elements[0];
			return elem && elem.getAttribute(attrs);
		}
		this.elements.forEach(function(elem){
			var attr;
			for(attr in attrs){
				elem.setAttribute(attr, attrs[attr]);
			}
		});
		return this;
	};

	fn.removeAttr = function(attr){
		this.elements.forEach(function(elem){
			elem.removeAttribute(attr);
		});
		return this;
	};

	['add', 'remove'].forEach(function(action){
		fn[action + 'Class'] =  function(cl){
			this.elements.forEach(function(elem){
				elem.classList[action](cl);
			});
			return this;
		};
	});

	fn.is = function(sel){
		return this.elements.some(function(elem){
			return elem.matches(sel);
		});
	};

	fn.html = function(html){
		var elem;
		var isString = typeof html == 'string';
		if(!arguments.length){
			elem = this.elements[0];
			return elem && elem.innerHTML || '';
		}
		this.elements.forEach(function(elem){
			if(isString){
				elem.innerHTML = html;
			} else {
				elem.innerHTML = '';
				elem.appendChild(html);
			}
		});
		return this;
	};

	fn.before = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			var parentElement;
			if (isString){
				elem.insertAdjacentHTML('beforebegin', html);
			} else {
				parentElement = elem.parentNode;
				if(parentElement){
					parentElement.insertBefore(html, elem);
				}
			}
		});
		return this;
	};

	fn.prepend = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			if (isString){
				elem.insertAdjacentHTML('afterbegin', html);
			} else {
				elem.insertBefore(html, elem.firstChild);
			}
		});
		return this;
	};

	fn.append = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			if (isString){
				elem.insertAdjacentHTML('beforeend', html);
			} else {
				elem.insertBefore(html, null);
			}
		});
		return this;
	};

	fn.after = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			var parentElement;
			if (isString){
				elem.insertAdjacentHTML('afterend', html);
			} else {
				parentElement = elem.parentNode;
				if(parentElement){
					parentElement.insertBefore(html, elem.nextElementSibling);
				}
			}
		});
		return this;
	};

	fn.each = function(cb){
		this.elements.forEach(function(elem, index){
			cb.call(elem, index, elem);
		});
		return this;
	};

	fn.trigger = function(type, options){
		var firstEvent;

		if(typeof type == 'object'){
			firstEvent = type;
			type = firstEvent.type;
		}

		if(!options){
			options = {};
		}

		if(options.bubbles == null){
			options.bubbles = true;
		}

		this.elements.forEach(function(elem){
			var event = firstEvent || new CustomEvent(type, options);
			firstEvent = null;
			elem.dispatchEvent(event);
		});

		return this;
	};

	fn.animate = function(endProps, options){
		this.elements.forEach(function(elem){
			tween(elem, endProps, options);
		});
		return this;
	};

	fn.stop = function(clearQueue, jumpToEnd){
		this.elements.forEach(function(elem) {
			if (elem._rbTweenStop) {
				elem._rbTweenStop(clearQueue, jumpToEnd);
			}
		});
		return this;
	};

	//new array or returns array
	['map', 'filter'].forEach(function(name){
		fn[name] = function(fn){
			var sel;

			if(typeof fn == 'string'){
				sel = fn;
				fn = function(){
					return this.matches(sel);
				};
			}
			return new dom(this.elements[name](function(elem, index){
				return fn.call(elem, index, elem);
			}));
		};
	});

	['every', 'findIndex', 'includes', 'indexOf', 'lastIndexOf', 'some'].forEach(function(name){
		fn[name] = function(){
			return this.elements[name].apply(this.elements, arguments);
		};
	});

	return dom;
}));
<commit_msg>fix stop<commit_after>(function(window, factory) {
	var dom = factory(window, window.document);
	window.dom = dom;
	if(typeof module == 'object' && module.exports){
		module.exports = dom;
	}
}(typeof window != 'undefined' ? window : this , function(window, document) {
	'use strict';
	var slice = [].slice;
	var dom = function(elements){

		if(typeof elements == 'string'){
			return dom.q(elements);
		}
		if(!this){
			return new dom(elements);
		}

		if(!Array.isArray(elements)){
			if(!elements){
				elements = [];
			} else if(elements.nodeName || !('length' in elements) || elements == window){
				elements = [elements];
			} else {
				elements = slice.call(elements);
			}
		}

		this.elements = elements;
		this.length = this.elements.length || 0;
	};
	var fn = dom.prototype;
	var tween = function(element, endProps, options){

		var easing, isStopped, isJumpToEnd, bezierArgs;
		var start = Date.now();
		var elementStyle = element.style;
		var startProps = {};
		var stop = function(clearQueue, jumpToEnd){
			isStopped = true;
			if(jumpToEnd){
				isJumpToEnd = true;
			}
			step();
		};
		var step = function(){
			var prop, value, eased;
			var pos = (Date.now() - start) / options.duration;
			if(pos > 1 || isJumpToEnd){
				pos = 1;
			}

			if(!isStopped){
				eased = easing(pos);

				for(prop in startProps){
					value = (endProps[prop] - startProps[prop]) * eased + startProps[prop];

					if(prop in elementStyle){
						elementStyle[prop] = value +'px';
					} else {
						element[prop] = value;
					}
				}
			}

			if(pos < 1){
				if(!isStopped){
					requestAnimationFrame(step);
				} else if(options.always){
					options.always.call(element);
				}
			} else{
				if(element._rbTweenStop){
					delete element._rbTweenStop;
				}
				if(options.complete){
					options.complete.call(element);
				}
				if(options.always){
					options.always.call(element);
				}
			}
		};

		tween.getStartValues(element, elementStyle, startProps, endProps);

		options = Object.assign({duration: 400, easing: 'ease'}, options || {});

		if(window.BezierEasing && !BezierEasing.css[options.easing] && (bezierArgs = options.easing.match(/([0-9\.]+)/g)) && bezierArgs.length == 4){
			bezierArgs = bezierArgs.map(function(str){
				return parseFloat(str);
			});
			BezierEasing.css[options.easing] = BezierEasing.apply(this, bezierArgs);
		}

		easing = window.BezierEasing && (BezierEasing.css[options.easing] || BezierEasing.css.ease) || function(pos){
				return pos;
			};

		element._rbTweenStop = stop;

		requestAnimationFrame(step);
	};

	tween.getStartValues = function(element, elementStyle, startProps, endProps){
		var prop;
		for(prop in endProps){
			if(prop in elementStyle){
				startProps[prop] = parseFloat(getComputedStyle(element).getPropertyValue(prop));
			} else {
				startProps[prop] = element[prop];
			}
		}
	};

	dom.fn = dom.prototype;

	dom.q = function(sel, context){
		return new dom( (context || document).querySelectorAll(sel) );
	};

	dom.Event = function(type, options){
		var event;
		if(!options){
			options = {};
		}

		if(options.bubbles == null){
			options.bubbles = true;
		}
		event = new CustomEvent(type, options);

		if(!event.isDefaultPrevented){
			event.isDefaultPrevented = function(){
				return event.defaultPrevented;
			};
		}

		return event;
	};

	fn.find = function(sel){
		var array = [];
		this.elements.forEach(function(elem){
			var i, len;
			var elements = elem.querySelectorAll(sel);
			for(i = 0, len = elements.length; i < len; i++){
				if(array.indexOf(elements[i]) == -1){
					array.push(elements[i]);
				}
			}
		});

		return new dom( array );
	};

	fn.closest = function(sel){
		var array = [];
		this.elements.forEach(function(elem){
			var element = elem.closest(sel);
			if(array.indexOf(element) == -1){
				array.push(element);
			}
		});
		return new dom( array );
	};

	fn.get = function(number){
		return arguments.length ? this.elements[number] : this.elements;
	};

	[['on', 'addEventListener'], ['off', 'removeEventListener']].forEach(function(action){
		dom.fn[action[0]] = function(type, sel, fn, capture){
			var useFn;
			if(typeof sel == 'function'){
				capture = fn;
				fn = sel;
				sel = null;
			}

			if(sel){
				if(!fn[sel]){
					fn[sel] = function(e){
						e.delegateTarget = e.target.closest(sel);
						if(!e.delegateTarget){return;}
						return fn.apply(this, arguments);
					};
				}
				useFn = fn[sel];
			} else {
				useFn = fn;
			}

			this.elements.forEach(function(elem){
				elem[action[1]](type, useFn, capture || false);
			});

			return this;
		};
	});

	fn.css = function(style, pseudo){
		var elem;
		if(typeof style == 'string'){
			elem = this.elements[0];
			return elem && getComputedStyle(elem, pseudo).getPropertyValue(style);
		}
		this.elements.forEach(function(elem){
			var prop;
			var eStyle = elem.style;
			for(prop in style){
				eStyle[prop] = style[prop];
			}
		});
		return this;
	};

	fn.prop = function(props){
		var elem;
		if(typeof props == 'string'){
			elem = this.elements[0];
			return elem && elem[props];
		}
		this.elements.forEach(function(elem){
			var prop;
			for(prop in props){
				elem[prop] = props[prop];
			}
		});
		return this;
	};

	fn.attr = function(attrs){
		var elem;
		if(typeof attrs == 'string'){
			elem = this.elements[0];
			return elem && elem.getAttribute(attrs);
		}
		this.elements.forEach(function(elem){
			var attr;
			for(attr in attrs){
				elem.setAttribute(attr, attrs[attr]);
			}
		});
		return this;
	};

	fn.removeAttr = function(attr){
		this.elements.forEach(function(elem){
			elem.removeAttribute(attr);
		});
		return this;
	};

	['add', 'remove'].forEach(function(action){
		fn[action + 'Class'] =  function(cl){
			this.elements.forEach(function(elem){
				elem.classList[action](cl);
			});
			return this;
		};
	});

	fn.is = function(sel){
		return this.elements.some(function(elem){
			return elem.matches(sel);
		});
	};

	fn.html = function(html){
		var elem;
		var isString = typeof html == 'string';
		if(!arguments.length){
			elem = this.elements[0];
			return elem && elem.innerHTML || '';
		}
		this.elements.forEach(function(elem){
			if(isString){
				elem.innerHTML = html;
			} else {
				elem.innerHTML = '';
				elem.appendChild(html);
			}
		});
		return this;
	};

	fn.before = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			var parentElement;
			if (isString){
				elem.insertAdjacentHTML('beforebegin', html);
			} else {
				parentElement = elem.parentNode;
				if(parentElement){
					parentElement.insertBefore(html, elem);
				}
			}
		});
		return this;
	};

	fn.prepend = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			if (isString){
				elem.insertAdjacentHTML('afterbegin', html);
			} else {
				elem.insertBefore(html, elem.firstChild);
			}
		});
		return this;
	};

	fn.append = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			if (isString){
				elem.insertAdjacentHTML('beforeend', html);
			} else {
				elem.insertBefore(html, null);
			}
		});
		return this;
	};

	fn.after = function(html){
		var isString = typeof html == 'string';
		this.elements.forEach(function(elem){
			var parentElement;
			if (isString){
				elem.insertAdjacentHTML('afterend', html);
			} else {
				parentElement = elem.parentNode;
				if(parentElement){
					parentElement.insertBefore(html, elem.nextElementSibling);
				}
			}
		});
		return this;
	};

	fn.each = function(cb){
		this.elements.forEach(function(elem, index){
			cb.call(elem, index, elem);
		});
		return this;
	};

	fn.trigger = function(type, options){
		var firstEvent;

		if(typeof type == 'object'){
			firstEvent = type;
			type = firstEvent.type;
		}

		if(!options){
			options = {};
		}

		if(options.bubbles == null){
			options.bubbles = true;
		}

		this.elements.forEach(function(elem){
			var event = firstEvent || new CustomEvent(type, options);
			firstEvent = null;
			elem.dispatchEvent(event);
		});

		return this;
	};

	fn.animate = function(endProps, options){
		this.elements.forEach(function(elem){
			tween(elem, endProps, options);
		});
		return this;
	};

	fn.stop = function(clearQueue, jumpToEnd){
		this.elements.forEach(function(elem) {
			if (elem._rbTweenStop) {
				elem._rbTweenStop(clearQueue, jumpToEnd);
			}
		});
		return this;
	};

	//new array or returns array
	['map', 'filter'].forEach(function(name){
		fn[name] = function(fn){
			var sel;

			if(typeof fn == 'string'){
				sel = fn;
				fn = function(){
					return this.matches(sel);
				};
			}
			return new dom(this.elements[name](function(elem, index){
				return fn.call(elem, index, elem);
			}));
		};
	});

	['every', 'findIndex', 'includes', 'indexOf', 'lastIndexOf', 'some'].forEach(function(name){
		fn[name] = function(){
			return this.elements[name].apply(this.elements, arguments);
		};
	});

	return dom;
}));
<|endoftext|>


<commit_before>export function centerH(rect, el) {
	el.style.left = `${(rect.width - el.clientWidth)/2}px`;
}

export function centerV(rect, el) {
	el.style.top = `${(rect.height - el.clientHeight)/2}px`;
}

export function center(rect, el) {
	centerH(rect, el);
	centerV(rect, el);
}
<commit_msg>Add eventCoordinates function<commit_after>import clamp from 'lodash.clamp';

export function centerH(rect, el) {
	el.style.left = `${(rect.width - el.clientWidth)/2}px`;
}

export function centerV(rect, el) {
	el.style.top = `${(rect.height - el.clientHeight)/2}px`;
}

export function center(rect, el) {
	centerH(rect, el);
	centerV(rect, el);
}

export function eventCoordinates(el, ev) {
	return {
		x: clamp(ev.offsetX, 0, el.clientWidth),
		y: clamp(ev.offsetY, 0, el.clientHeight)
	};
}
<|endoftext|>


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

  var root = this;

  define([
      'underscore',
      'jquery',
      'util',
      'sqlite3'
    ], function(_, $, util, sqlite3) {
      // on memory (test)
      var sqlite = sqlite3.verbose();
      var TABLE_NAME = 'items';
      var STORAGE_TARGET = 'js2ch.db';
      var callbacks = new $.Callbacks('once');
      var fired = false;

      // ストレージ操作用のクラス
      var Storage = function() {
        // テーブルを作成する
        // （作成が済むまで他の操作はcallbacksにストックしておく）
        var db = new sqlite.Database(STORAGE_TARGET);
        db.get('SELECT COUNT(*) FROM sqlite_master WHERE type=\'table\' and name=\'' + TABLE_NAME + '\'', function(error, row) {
          var result = row['COUNT(*)'];
          if (result === 0) {
            db.run('CREATE TABLE ' + TABLE_NAME + ' (key, value)', function() {
              callbacks.fire();
              db.close();
            });
          } else {
            callbacks.fire();
          }
        });
      };

      Storage.prototype = {};
      var proto = _(Storage.prototype);

      proto.extend({
          // 与えられたキーに紐付けられたデータを取得する
          get: function get(keys, callback) {
            if (!callbacks.fired()) {
              callbacks.add(get.bind(this, keys, callback));
              return;
            }

            var default_values = {};
            var hash_table = {};

            // keysは文字列か配列かオブジェクト
            if (typeof keys === 'string') {
              keys = [keys];
              _(keys).each(function(key) {
                default_values[key] = undefined;
              });
            } else if (Array.isArray(keys)) {
              _(keys).each(function(key) {
                default_values[key] = undefined;
              })
            } else if (typeof keys === 'object') {
              default_values = _(keys).clone();
              keys = _.keys(keys);
            }

            // ハッシュテーブルに登録しておく
            _(keys).each(function(key) {
              hash_table[key] = 0;
            });

            // アイテムを取得する
            var db = new sqlite.Database(STORAGE_TARGET);
            db.all('SELECT key,value FROM ' + TABLE_NAME, function(error, rows) {
              var res = _(default_values).clone();
              var filtered = _(rows).filter(function(item) {
                return typeof hash_table[item.key] !== 'undefined';
              });
              _(filtered).each(function(item) {
                res[item.key] = JSON.parse(item.value);
              });
              db.close();
              callback(res);
            });
          }
        });

      proto.extend({
          // 与えたデータを保存する
          set: function set(items, callback) {
            if (!callbacks.fired()) {
              callbacks.add(set.bind(this, items, callback));
              return;
            }

            // 値を文字列に変換しておく
            var serialized = {};
            var keys = _.keys(items);
            _(keys).each(function(key) {
              serialized[key] = JSON.stringify(items[key]);
            });

            var db = new sqlite.Database(STORAGE_TARGET);
            db.serialize(function() {
              var deferreds = [];
              // UPDATE
              (function() {
                var deferred = new $.Deferred();
                var stmt = db.prepare('UPDATE ' + TABLE_NAME + ' SET value = ? WHERE key = ?');
                _(serialized).each(function(value, key) {
                  stmt.run(value, key, function() {
                    deferred.resolve();
                  });
                });
                stmt.finalize();
                deferreds.push(deferred);
              })();

              // 存在しないキーはINSERT
              (function() {
                _(serialized).each(function(value, key) {
                  var deferred = new $.Deferred();
                  db.get('SELECT * FROM ' + TABLE_NAME + ' WHERE key = ' + key, function(error, row) {
                    var stmt = db.prepare('INSERT INTO ' + TABLE_NAME + ' VALUES(?,?)');
                    stmt.run(key, value, function() {
                      stmt.finalize();
                      deferred.resolve();
                    });
                  });
                  deferreds.push(deferred);
                });
              })();

              $.when.apply(null, deferreds).done(function() {
                db.close();
                callback();
              });
            });

          }
        });

      proto.extend({
          // 与えられたキーに紐付けられたデータを削除する
          remove: function remove(keys, callback) {
            if (!callbacks.fired()) {
              callbacks.add(remove.bind(this, keys, callback));
              return;
            }

            if (typeof keys === 'string')
              keys = [keys];

            // アイテムを削除する
            var db = new sqlite.Database(STORAGE_TARGET);
            var stmt = db.prepare('DELETE FROM ' + TABLE_NAME + ' WHERE key = ?');
            var deferreds = [];
            _(keys).each(function(key) {
              var deferred = new $.Deferred();
              stmt.run(key, function(error, row) {
                deferred.resolve();
              });
              deferreds.push(deferred);
            });
            $.when.apply(null, deferreds).done(function() {
              stmt.finalize();
              db.close();
              callback();
            });
          }
        });

      proto.extend({
          // すべてのデータを削除する
          clear: function clear(callback) {
            if (!callbacks.fired()) {
              callbacks.add(clear.bind(this, callback));
              return;
            }

            // アイテムを削除する
            var db = new sqlite.Database(STORAGE_TARGET);
            db.run('DELETE FROM ' + TABLE_NAME, function() {
              db.close();
              callback();
            });
          }
        });

      // Deferredの設定
      var keys = ['get', 'set', 'remove', 'clear'];
      _(keys).each(function(key) {
        Storage.prototype[key] = util.getDeferredFunc(Storage.prototype[key]);
      });

      return new Storage();
    });

}).call(this);
<commit_msg>Fix: ライセンスヘッダを追加<commit_after>/* ===================================================
 * JavaScript 2ch-client Library
 * https://github.com/sh19910711/js2ch
 * ===================================================
 * Copyright (c) 2013 Hiroyuki Sano
 *
 * Licensed under MIT License.
 * http://opensource.org/licenses/MIT
 * =================================================== */
(function() {
  'use strict';

  var root = this;

  define([
      'underscore',
      'jquery',
      'util',
      'sqlite3'
    ], function(_, $, util, sqlite3) {
      // on memory (test)
      var sqlite = sqlite3.verbose();
      var TABLE_NAME = 'items';
      var STORAGE_TARGET = 'js2ch.db';
      var callbacks = new $.Callbacks('once');
      var fired = false;

      // ストレージ操作用のクラス
      var Storage = function() {
        // テーブルを作成する
        // （作成が済むまで他の操作はcallbacksにストックしておく）
        var db = new sqlite.Database(STORAGE_TARGET);
        db.get('SELECT COUNT(*) FROM sqlite_master WHERE type=\'table\' and name=\'' + TABLE_NAME + '\'', function(error, row) {
          var result = row['COUNT(*)'];
          if (result === 0) {
            db.run('CREATE TABLE ' + TABLE_NAME + ' (key, value)', function() {
              callbacks.fire();
              db.close();
            });
          } else {
            callbacks.fire();
          }
        });
      };

      Storage.prototype = {};
      var proto = _(Storage.prototype);

      proto.extend({
          // 与えられたキーに紐付けられたデータを取得する
          get: function get(keys, callback) {
            if (!callbacks.fired()) {
              callbacks.add(get.bind(this, keys, callback));
              return;
            }

            var default_values = {};
            var hash_table = {};

            // keysは文字列か配列かオブジェクト
            if (typeof keys === 'string') {
              keys = [keys];
              _(keys).each(function(key) {
                default_values[key] = undefined;
              });
            } else if (Array.isArray(keys)) {
              _(keys).each(function(key) {
                default_values[key] = undefined;
              })
            } else if (typeof keys === 'object') {
              default_values = _(keys).clone();
              keys = _.keys(keys);
            }

            // ハッシュテーブルに登録しておく
            _(keys).each(function(key) {
              hash_table[key] = 0;
            });

            // アイテムを取得する
            var db = new sqlite.Database(STORAGE_TARGET);
            db.all('SELECT key,value FROM ' + TABLE_NAME, function(error, rows) {
              var res = _(default_values).clone();
              var filtered = _(rows).filter(function(item) {
                return typeof hash_table[item.key] !== 'undefined';
              });
              _(filtered).each(function(item) {
                res[item.key] = JSON.parse(item.value);
              });
              db.close();
              callback(res);
            });
          }
        });

      proto.extend({
          // 与えたデータを保存する
          set: function set(items, callback) {
            if (!callbacks.fired()) {
              callbacks.add(set.bind(this, items, callback));
              return;
            }

            // 値を文字列に変換しておく
            var serialized = {};
            var keys = _.keys(items);
            _(keys).each(function(key) {
              serialized[key] = JSON.stringify(items[key]);
            });

            var db = new sqlite.Database(STORAGE_TARGET);
            db.serialize(function() {
              var deferreds = [];
              // UPDATE
              (function() {
                var deferred = new $.Deferred();
                var stmt = db.prepare('UPDATE ' + TABLE_NAME + ' SET value = ? WHERE key = ?');
                _(serialized).each(function(value, key) {
                  stmt.run(value, key, function() {
                    deferred.resolve();
                  });
                });
                stmt.finalize();
                deferreds.push(deferred);
              })();

              // 存在しないキーはINSERT
              (function() {
                _(serialized).each(function(value, key) {
                  var deferred = new $.Deferred();
                  db.get('SELECT * FROM ' + TABLE_NAME + ' WHERE key = ' + key, function(error, row) {
                    var stmt = db.prepare('INSERT INTO ' + TABLE_NAME + ' VALUES(?,?)');
                    stmt.run(key, value, function() {
                      stmt.finalize();
                      deferred.resolve();
                    });
                  });
                  deferreds.push(deferred);
                });
              })();

              $.when.apply(null, deferreds).done(function() {
                db.close();
                callback();
              });
            });

          }
        });

      proto.extend({
          // 与えられたキーに紐付けられたデータを削除する
          remove: function remove(keys, callback) {
            if (!callbacks.fired()) {
              callbacks.add(remove.bind(this, keys, callback));
              return;
            }

            if (typeof keys === 'string')
              keys = [keys];

            // アイテムを削除する
            var db = new sqlite.Database(STORAGE_TARGET);
            var stmt = db.prepare('DELETE FROM ' + TABLE_NAME + ' WHERE key = ?');
            var deferreds = [];
            _(keys).each(function(key) {
              var deferred = new $.Deferred();
              stmt.run(key, function(error, row) {
                deferred.resolve();
              });
              deferreds.push(deferred);
            });
            $.when.apply(null, deferreds).done(function() {
              stmt.finalize();
              db.close();
              callback();
            });
          }
        });

      proto.extend({
          // すべてのデータを削除する
          clear: function clear(callback) {
            if (!callbacks.fired()) {
              callbacks.add(clear.bind(this, callback));
              return;
            }

            // アイテムを削除する
            var db = new sqlite.Database(STORAGE_TARGET);
            db.run('DELETE FROM ' + TABLE_NAME, function() {
              db.close();
              callback();
            });
          }
        });

      // Deferredの設定
      var keys = ['get', 'set', 'remove', 'clear'];
      _(keys).each(function(key) {
        Storage.prototype[key] = util.getDeferredFunc(Storage.prototype[key]);
      });

      return new Storage();
    });

}).call(this);
<|endoftext|>


<commit_before>'use strict';

function run(context, [functionName, ...args]) {
  const func = CONDITIONALS[functionName];
  if (!func) {
    return null;
  } else {
    return func(context, ...args);
  }
};

const CONDITIONALS = module.exports = {
  run,

  always: function(req) {
    return true;
  },

  allOf: function(req, ...subItems) {
    return subItems.every(subItem => run(req, subItem));
  },

  oneOf: function(req, ...subItems) {
    return someItems.some(subItem => run(req, subItem));
  },

  not: function(req, subItem) {
    return !run(req, subItem);
  },

  pathMatch: function(req, pattern) {
  },

  pathExact: function(req, path) {
  },

  method: function(req, method) {
  },

  authScope: function(req, scope) {
  }
};
<commit_msg>Implemented more conditionals<commit_after>'use strict';

function run(context, [functionName, ...args]) {
  const func = CONDITIONALS[functionName];
  if (!func) {
    return null;
  } else {
    return func(context, ...args);
  }
};

const CONDITIONALS = module.exports = {
  run,

  always: function(req) {
    return true;
  },

  allOf: function(req, ...subItems) {
    return subItems.every(subItem => run(req, subItem));
  },

  oneOf: function(req, ...subItems) {
    return someItems.some(subItem => run(req, subItem));
  },

  not: function(req, subItem) {
    return !run(req, subItem);
  },

  pathMatch: function(req, pattern) {
    return req.originalUrl.match(new RegExp(pattern)) != null;
  },

  pathExact: function(req, path) {
    return req.originalUrl === path;
  },

  method: function(req, method) {
    if (typeof method === 'Array') {
      return method.includes(req.method);
    } else {
      return req.method === method;
    }
  },

  authScope: function(req, scope) {
    return false;
  }
};
<|endoftext|>