<commit_before>//
// 2D Layout tests
//

var deps = [
    '/stdlib/dom.js',
    '/stdlib/layout.js'
];

function onReady(dom, layout) {

    function testImg() {
        return dom.element({name: 'img', attributes: {src: 'logo.png'}, style: {border: '1px solid', padding: '5px', borderRadius: '5px'}});
    }

    function hcatTest() {
        return layout.hcat([
            dom.element({name: 'span', style: {height: '20px', width: '70px'}, contents: 'hello'}),
            layout.gap(10),
            dom.element({name: 'span', style: {height: '20px', width: '70px'}, contents: 'world'})
        ]);
    }

    function test() {
        var separator = layout.gap(30);

        function label(s, e) {
            return layout.hcat([dom.element({name: 'p', style: {width: '70px'}, contents: s}), layout.gap(10), e]);
        }

        return layout.hcat([
            layout.gap(10),
            layout.vcat([
                layout.gap(10),
                layout.vcat([
                    label('hcat', layout.hcat([testImg(), layout.gap(10), testImg(), layout.gap(10), testImg()])), separator,
                    label('vcat', layout.vcat([testImg(), layout.gap(10), testImg(), layout.gap(10), testImg()])), separator,
                ]),
                layout.gap(10)
            ]),
            layout.gap(10)
        ]);
    }

    define(test());
}

require(deps, onReady);

<commit_msg>Now that layout.js uses all immutable types, usage is much simpler.<commit_after>//
// 2D Layout tests
//

var deps = [
    '/stdlib/dom.js',
    '/stdlib/layout.js'
];

function onReady(dom, layout) {

    var gap10 = layout.gap(10);

    var testImg = dom.element({
        name: 'img',
        attributes: {src: 'logo.png'},
        style: {border: '1px solid', padding: '5px', borderRadius: '5px'}
    });

    var separator = layout.gap(30);

    function label(s, e) {
        return layout.hcat([dom.element({name: 'p', style: {width: '70px'}, contents: s}), gap10, e]);
    }

    var images = [testImg, gap10, testImg, gap10, testImg];
    var div = layout.hcat([
        gap10,
        layout.vcat([
            gap10,
            layout.vcat([
                label('hcat', layout.hcat(images)), separator,
                label('vcat', layout.vcat(images)), separator,
            ]),
            gap10
        ]),
        gap10
    ]);

    define(div);
}

require(deps, onReady);

<|endoftext|>


<commit_before>var conf = conf || {
  "queuedata-uri": "http://192.168.0.102/queue-animation-d3/test/response.json",
  "polling-interval-sec": "1", // seconds
  "response-jsonpath": {
      "occupancy": "$.queuedatavalue.occupancydatavalue.occupancyvalueval",
      "target-size": "$.targetqueuesizedatavalue.targetqueuesizevalue"
  }
}
<commit_msg>Update conf.js<commit_after>var conf = conf || {
  "queuedata-uri": "test/response.json",
  "polling-interval-sec": "1", // seconds
  "response-jsonpath": {
      "occupancy": "$.queuedatavalue.occupancydatavalue.occupancyvalueval",
      "target-size": "$.targetqueuesizedatavalue.targetqueuesizevalue"
  }
}
<|endoftext|>


<commit_before>var $username = $('#username'),
    $password = $('#password'),
    $email = $('#email'),
    $loginFeedback = $('#loginFeedback');

var $loginForm = $('#login form').submit(function (event) {
  event.preventDefault();

  var name = $username.val(),
      key = $password.val(),
      email = $email.val();

  jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: '/sethome',
    data: { name: name, key: key, email: email },
    type: 'post',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (data.ok) {
        $loginFeedback.text('Successfully tied this browser to "' + name + '".');
        setTimeout(function () {
          window.location = window.location.pathname + window.location.search;
        }, 500);
      } else {
        analytics.login(false);
        $loginFeedback.text('"' + name + '" has already been taken. Please either double check the password, or choose another username.');
      }
    }
  });
});

if ($('#homebtn').length) {
  jsbin.settings.home = document.cookie.split('home=')[1].split(';')[0];
  document.title = jsbin.settings.home + '@' + document.title;
}
<commit_msg>Sod, sethome should have been relative.<commit_after>var $username = $('#username'),
    $password = $('#password'),
    $email = $('#email'),
    $loginFeedback = $('#loginFeedback');

var $loginForm = $('#login form').submit(function (event) {
  event.preventDefault();

  var name = $username.val(),
      key = $password.val(),
      email = $email.val();

  jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: jsbin.root + '/sethome',
    data: { name: name, key: key, email: email },
    type: 'post',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (data.ok) {
        $loginFeedback.text('Successfully tied this browser to "' + name + '".');
        setTimeout(function () {
          window.location = window.location.pathname + window.location.search;
        }, 500);
      } else {
        analytics.login(false);
        $loginFeedback.text('"' + name + '" has already been taken. Please either double check the password, or choose another username.');
      }
    }
  });
});

if ($('#homebtn').length) {
  jsbin.settings.home = document.cookie.split('home=')[1].split(';')[0];
  document.title = jsbin.settings.home + '@' + document.title;
}
<|endoftext|>


<commit_before>var rp      = require("request-promise"),
    Promise = require("bluebird"),
    util    = require("./util"),
    extend  = require("util")._extend,
    debug   = require("debug")("open-shl:connection")

const baseUrl = "https://openapi.shl.se",
    auth = "/oauth2/token";

const DEFAULTS = {
    userAgent: "OpenShl.js/1.0",
    autoConnect: true
}

function Connection(options) {
    if(!options || !options.clientId || !options.clientSecret) {
        throw new Error("clientId and clientSecret must be set");
    }
    this.options = extend(DEFAULTS, options);
    debug("options: %o", {autoConnect: this.options.autoConnect, userAgent: this.options.userAgent });
}

Connection.prototype.autoConnect = function (autoConnect) {
    this.options.autoConnect = autoConnect;
}

Connection.prototype.connect = function () {
    debug("connect: %o", baseUrl + auth);
    return this._request({
        method: "POST",
        uri: baseUrl + auth,
        form: {
            client_id: this.options.clientId,
            client_secret: this.options.clientSecret,
            grant_type: "client_credentials"
        },
        headers: {
            "User-Agent": this.options.userAgent
        }
    })
    .then(JSON.parse)
    .then((body) => {
        this.accessToken = body.access_token;
        this.expires = new Date();
        this.expires.setSeconds(this.expires.getSeconds() + body.expires_in);
        debug("token: %o, expires: %o", this.accessToken, this.expires);
    });
}

Connection.prototype.get = function (path, query) {
    if(query && util.isObject(query)) {
        path += "?" + util.query.convert(query);
    }
    if(!this.isConnected() && this.options.autoConnect) {
        debug("get: no valid token. refreshing")
        return this.connect().then(() => this._fetch(path));
    }
    return this._fetch(path);
}

Connection.prototype.isConnected = function () {
    return this.accessToken && new Date() <= this.expires;
}

Connection.prototype._fetch = function (path) {
    if(!this.isConnected()) {
        return Promise.reject(new Error("No accessToken has been fetched"));
    }
    debug("_fetch %o", baseUrl + path);
    return this._request({
        method: "GET",
        uri: baseUrl + path,
        headers: {
            "Authorization": "Bearer " + this.accessToken,
            "User-Agent": this.options.userAgent
        }
    })
    .then(JSON.parse);
}

Connection.prototype._request = function (options) {
    return rp(options);
}

module.exports = Connection;<commit_msg>Bumped version in user agent<commit_after>var rp      = require("request-promise"),
    Promise = require("bluebird"),
    util    = require("./util"),
    extend  = require("util")._extend,
    debug   = require("debug")("open-shl:connection")

const baseUrl = "https://openapi.shl.se",
    auth = "/oauth2/token";

const DEFAULTS = {
    userAgent: "OpenShl.js/1.1.3",
    autoConnect: true
}

function Connection(options) {
    if(!options || !options.clientId || !options.clientSecret) {
        throw new Error("clientId and clientSecret must be set");
    }
    this.options = extend(DEFAULTS, options);
    debug("options: %o", {autoConnect: this.options.autoConnect, userAgent: this.options.userAgent });
}

Connection.prototype.autoConnect = function (autoConnect) {
    this.options.autoConnect = autoConnect;
}

Connection.prototype.connect = function () {
    debug("connect: %o", baseUrl + auth);
    return this._request({
        method: "POST",
        uri: baseUrl + auth,
        form: {
            client_id: this.options.clientId,
            client_secret: this.options.clientSecret,
            grant_type: "client_credentials"
        },
        headers: {
            "User-Agent": this.options.userAgent
        }
    })
    .then(JSON.parse)
    .then((body) => {
        this.accessToken = body.access_token;
        this.expires = new Date();
        this.expires.setSeconds(this.expires.getSeconds() + body.expires_in);
        debug("token: %o, expires: %o", this.accessToken, this.expires);
    });
}

Connection.prototype.get = function (path, query) {
    if(query && util.isObject(query)) {
        path += "?" + util.query.convert(query);
    }
    if(!this.isConnected() && this.options.autoConnect) {
        debug("get: no valid token. refreshing")
        return this.connect().then(() => this._fetch(path));
    }
    return this._fetch(path);
}

Connection.prototype.isConnected = function () {
    return this.accessToken && new Date() <= this.expires;
}

Connection.prototype._fetch = function (path) {
    if(!this.isConnected()) {
        return Promise.reject(new Error("No accessToken has been fetched"));
    }
    debug("_fetch %o", baseUrl + path);
    return this._request({
        method: "GET",
        uri: baseUrl + path,
        headers: {
            "Authorization": "Bearer " + this.accessToken,
            "User-Agent": this.options.userAgent
        }
    })
    .then(JSON.parse);
}

Connection.prototype._request = function (options) {
    return rp(options);
}

module.exports = Connection;<|endoftext|>


<commit_before>const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
context.scale(20, 20);

let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;

const matrix = [
  [0, 0, 0],
  [1, 1, 1],
  [0, 1, 0]
];

const arena = createMatrix(12, 20);

const player = {
  pos: { x: 5, y: 0 },
  matrix
};

function collides(arena, player) {
  const [m, o] = [player.matrix, player.pos];
  for (let y = 0; y < m.length; ++y) {
    for (let x = 0; x < m[y].length; ++x) {
      if (m[y][x] !== 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) {
        return true;
      }
    }
  }
  return false;
}

function createMatrix(width, height) {
  const newMatrix = [];
  while (--height) {
    newMatrix.push(new Array(width).fill(0));
  }
  return newMatrix;
}

function merge(arena, player) {
  player.matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      if (value !== 0) {
        console.log(y, x);
        arena[y + player.pos.y][x + player.pos.x] = value;
      }
    });
  });
}

function draw() {
  context.fillStyle = '#000';
  context.fillRect(0, 0, canvas.width, canvas.height);

  drawMatrix(arena, { x: 0, y: 0 });
  drawMatrix(player.matrix, player.pos);
}

function drawMatrix(matrix, offset) {
  matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      if (value !== 0) {
        context.fillStyle = 'red';
        context.fillRect(x + offset.x, y + offset.y, 1, 1);
      }
    });
  });
}

function playerDrop() {
  player.pos.y++;
  if (collides(arena, player)) {
    player.pos.y--;
    merge(arena, player);
    player.pos.y = 0;
  }
  dropCounter = 0;
}

function update(time = 0) {
  const deltaTime = time - lastTime;
  lastTime = time;
  dropCounter += deltaTime;

  if (dropCounter > dropInterval) {
    playerDrop();
  }

  draw();
  requestAnimationFrame(update);
}

update();

document.addEventListener('keydown', (event) => {
  if (event.keyCode === 37) {
    player.pos.x--;
  }
  if (event.keyCode === 39) {
    player.pos.x++;
  }
  if (event.keyCode === 40) {
    playerDrop();
  }
});
<commit_msg>Fix problem with the arena length<commit_after>const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
context.scale(20, 20);

let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;

const matrix = [
  [0, 0, 0],
  [1, 1, 1],
  [0, 1, 0]
];

const arena = createMatrix(12, 20);

const player = {
  pos: { x: 5, y: 0 },
  matrix
};

function collides(arena, player) {
  const [m, o] = [player.matrix, player.pos];
  for (let y = 0; y < m.length; ++y) {
    for (let x = 0; x < m[y].length; ++x) {
      if (m[y][x] !== 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) {
        return true;
      }
    }
  }
  return false;
}

function createMatrix(width, height) {
  const newMatrix = [];
  while (height--) {
    newMatrix.push(new Array(width).fill(0));
  }
  return newMatrix;
}

function merge(arena, player) {
  player.matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      if (value !== 0) {
        arena[y + player.pos.y][x + player.pos.x] = value;
      }
    });
  });
}

function draw() {
  context.fillStyle = '#000';
  context.fillRect(0, 0, canvas.width, canvas.height);

  drawMatrix(arena, { x: 0, y: 0 });
  drawMatrix(player.matrix, player.pos);
}

function drawMatrix(matrix, offset) {
  matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      if (value !== 0) {
        context.fillStyle = 'red';
        context.fillRect(x + offset.x, y + offset.y, 1, 1);
      }
    });
  });
}

function playerDrop() {
  player.pos.y++;
  if (collides(arena, player)) {
    player.pos.y--;
    merge(arena, player);
    player.pos.y = 0;
  }
  dropCounter = 0;
}

function update(time = 0) {
  const deltaTime = time - lastTime;
  lastTime = time;
  dropCounter += deltaTime;

  if (dropCounter > dropInterval) {
    playerDrop();
  }

  draw();
  requestAnimationFrame(update);
}

update();

document.addEventListener('keydown', (event) => {
  if (event.keyCode === 37) {
    player.pos.x--;
  }
  if (event.keyCode === 39) {
    player.pos.x++;
  }
  if (event.keyCode === 40) {
    playerDrop();
  }
});
<|endoftext|>


<commit_before>setTimeout(function() {
    $('.selectize-control').removeClass('event-input');
    $('.selectize-dropdown').removeClass('event-input')
}, 1000);

<commit_msg>fix: remove unused script files<commit_after><|endoftext|>


<commit_before>import Spec from './spec'
import { dt, getName } from '../util'
import { invalid } from '../symbols'
/**
 * This is to quickly convert a predicate function into a spec
 */
export class Predicate extends Spec {
  conform(value) {
    const conformed = dt(this.options.fn, value)
    if (conformed === invalid) {
      return invalid
    }
    if (conformed === value) {
      return value
    }
    return conformed
  }

  toString() {
    return this.name || this.options.name
  }

  explain(path, via, value) {
    if (!this.options.fn(value)) {
      return [{
        path,
        via: [...via, getName(this)],
        value,
        predicate: this.options.fn
      }]
    }
    return [null]
  }
}

export default function predicate(predFn) {
  if (!predFn) {
    throw new Error(`Cannot use Predicate spec without predicate function.`)
  }
  return new Predicate({
    fn: predFn,
    name: getName(predFn)
  })
}
<commit_msg>:bug: fix: handle conformed like everywhere<commit_after>import Spec from './spec'
import { dt, getName } from '../util'
import { invalid } from '../symbols'
/**
 * This is to quickly convert a predicate function into a spec
 */
export class Predicate extends Spec {
  conform(value) {
    const conformed = dt(this.options.fn, value)
    if (conformed === invalid) {
      return invalid
    }
    return conformed
  }

  toString() {
    return this.name || this.options.name
  }

  explain(path, via, value) {
    if (!this.options.fn(value)) {
      return [{
        path,
        via: [...via, getName(this)],
        value,
        predicate: this.options.fn
      }]
    }
    return [null]
  }
}

export default function predicate(predFn) {
  if (!predFn) {
    throw new Error(`Cannot use Predicate spec without predicate function.`)
  }
  return new Predicate({
    fn: predFn,
    name: getName(predFn)
  })
}
<|endoftext|>


<commit_before>

var ActionBroker = function()
{
  /*
    static constants
      ActionBroker.GLOBAL_HANDLER_ID = "global";

    static methods
      ActionBroker.get_instance
  */

  if (ActionBroker.instance)
    return ActionBroker.instance;

  ActionBroker.instance = this;

  /* interface */

  /**
    * To register an instance of class which implements
    * the ActionHandler interface.
    */
  this.register_handler = function(action_handler){};

  /**
    * To get a copy of the current shortcut map.
    */
  this.get_shortcuts = function(){};

  /**
    * To set a new shortcut map.
    * @param {Object} shortcuts
    * @param {String} handler_id
    * @param {Boolean} clear_setting. To reste to defaults
    */
  this.set_shortcuts = function(shortcuts, handler_id, clear_setting){};

  /**
    * To get a list of action handler ids.
    */
  this.get_handlers = function(){};

  /**
    * To propagate UI inputs, e.g. click or right click menu, as actions.
    */
  this.dispatch_action = function(view_id, action_id, event, target){};

  /**
    * To propagate key input as a shortcut action.
    */
  this.dispatch_key_input = function(key_id, event){};

  /**
    * To delay an action propagation,
    * e.g if a click could be followed by a double click.
    * @param {String} type, the event type
    * @param {String} handler_id
    * @param {String} action_id
    * @param {Event} event
    * @param {Element} target
    */
  this.delay_action = function(type, handler_id, action_id, event, target){};

  /**
    * To cancel delayed actions.
    * e.g. if a dbl click has followed a click.
    * @param {String} type, the event type
    */
  this.clear_delayed_actions = function(type){};

  /**
    * To get a list of all actions of a given action handler.
    * @param {String} handler_id
    */
  this.get_actions_with_handler_id = function(handler_id){};

  /**
    * To the label for a given mode.
    * @param {String} handler_id
    * @param {String} mode
    */
  this.get_label_with_handler_id_and_mode = function(handler_id, mode){};

  /**
    * To the global action handler.
    * Any action, which was not cancelled by the focused action handler,
    * gets passed to the global action handler.
    */
  this.get_global_handler = function(){};

  /* constants */

  const GLOBAL_HANDLER = ActionBroker.GLOBAL_HANDLER_ID;

  /* private */

  this._handlers = {};
  this._action_context = null;
  this._action_context_id = '';
  this._container = null;
  this._shortcuts = null;
  this._gloabal_shortcuts = null;
  this._current_shortcuts = null;
  this._global_handler = new GlobalActionHandler(GLOBAL_HANDLER);
  this._delays = {};

  this._set_action_context_bound = (function(event)
  {
    if (!(this._action_context && this._action_context.onclick(event) === false))
    {
      var container = event.target;
      while (container && container.nodeType == 1 &&
             container.parentNode.nodeType == 1 &&
             !/^(?:top-)?(?:container|toolbar|tabs)$/i.test(container.nodeName))
        container = container.parentNode;

      switch (container && container.nodeName.toLowerCase() || '')
      {
        case 'container':
        {
          var ui_obj = UIBase.getUIById(container.getAttribute('ui-id'));
          if (ui_obj)
            this._set_current_handler(ui_obj.view_id, event, container);
          break;
        }
        // TODO set according key handler, e.g. toolbar, tab
        default:
        {
          this._set_current_handler(GLOBAL_HANDLER, event, container);
        }
      }
    }
  }).bind(this);

  this._set_current_handler = function(handler_id, event, container)
  {
    if (handler_id != this._action_context_id)
    {
      if (this._action_context)
        this._action_context.blur();
      if (this._container && this._container.hasClass('edit-mode'))
        this._container.removeClass('edit-mode');
      this._action_context = this._handlers[handler_id] || this._global_handler;
      this._action_context_id = this._action_context.id;
      this._current_shortcuts = this._shortcuts[this._action_context_id] || {};
      this._container = container || document.documentElement;
      this._action_context.focus(event, container);
    }
  }

  this._init = function()
  {
    this._key_identifier = new KeyIdentifier(this.dispatch_key_input.bind(this),
                                             window.ini.browser);
    this.register_handler(this._global_handler);
    window.app.addListener('services-created', function()
    {
      this._shortcuts = window.settings.general.get("shortcuts") ||
                        window.ini.default_shortcuts.windows;
      this._gloabal_shortcuts = this._shortcuts.global;
      this._key_identifier.set_shortcuts(this._get_shortcut_keys());
      this._set_current_handler(this._global_handler);
      document.addEventListener('click', this._set_action_context_bound, true);
    }.bind(this));
  };

  /* implementation */

  this.register_handler = function(action_handler)
  {
    if (!(action_handler && action_handler.id))
      throw 'missing id on action_handler in ActionBroker.instance.register_handler';
    this._handlers[action_handler.id] = action_handler;
  }

  this.dispatch_action = function(action_handler_id, action_id, event, target)
  {
    this._handlers[action_handler_id].handle(action_id, event, target);
  }

  this.dispatch_key_input = function(key_id, event)
  {
    var shortcuts = this._current_shortcuts[this._action_context.mode];
    var action = shortcuts && shortcuts[key_id] || '';
    var propagate_event = true;
    if (action)
      propagate_event = this._action_context.handle(action,
                                                    event,
                                                    this._container);
    if (!(propagate_event === false) &&
         this._action_context != this._global_handler)
    {
      shortcuts = this._gloabal_shortcuts[this._gloabal_shortcuts.mode];
      action = shortcuts && shortcuts[key_id] || '';
      if (action)
        propagate_event = this._global_handler.handle(action,
                                                      event,
                                                      this._container);
    }
    if (propagate_event === false)
    {
      event.stopPropagation();
      event.preventDefault();
    }
  }

  this.delay_action = function(type, handler_id, action_id, event, target)
  {
    if (!this._delays.hasOwnProperty(type))
    {
      this._delays[type] = new Timeouts();
    }
    var cb = this.dispatch_action.bind(this, handler_id, action_id, event, target);
    this._delays[type].set(cb, 300);
  }

  this.clear_delayed_actions = function(type)
  {
    if (this._delays[type])
      this._delays[type].clear();
  }

  this.get_shortcuts = function()
  {
    return window.helpers.copy_object(this._shortcuts);
  }

  this.set_shortcuts = function(shortcuts, handler_id, clear_setting)
  {
    shortcuts = window.helpers.copy_object(shortcuts);
    if (handler_id)
      this._shortcuts[handler_id] = shortcuts;
    else
      this._shortcuts = shortcuts;
    this._gloabal_shortcuts = this._shortcuts.global;
    window.settings.general.set("shortcuts",
                                clear_setting == true ? null : this._shortcuts);
    this._key_identifier.set_shortcuts(this._get_shortcut_keys());
  };

  this.get_actions_with_handler_id = function(handler_id)
  {
    return (
    this._handlers[handler_id] && this._handlers[handler_id].get_action_list());
  };

  this._get_shortcut_keys = function()
  {
    var ret = [], name = '', handler = null, mode = '', key = '';
    for (name in this._shortcuts)
    {
      handler = this._shortcuts[name];
      for (mode in handler)
      {
        for (key in handler[mode])
        {
          if (ret.indexOf(key) == -1)
            ret.push(key);
        }
      }
    }
    return ret;
  };

  this.get_label_with_handler_id_and_mode = function(handler_id, mode)
  {
    return this._handlers[handler_id].mode_labels[mode];
  };

  this.get_global_handler = function()
  {
    return this._global_handler;
  };

  if (document.readyState == "complete")
    this._init();
  else
    document.addEventListener('DOMContentLoaded', this._init.bind(this), false);

}

ActionBroker.get_instance = function()
{
  return this.instance || new ActionBroker();
}

ActionBroker.GLOBAL_HANDLER_ID = "global";
<commit_msg>Fix for checking the mode on the wrong object.<commit_after>

var ActionBroker = function()
{
  /*
    static constants
      ActionBroker.GLOBAL_HANDLER_ID = "global";

    static methods
      ActionBroker.get_instance
  */

  if (ActionBroker.instance)
    return ActionBroker.instance;

  ActionBroker.instance = this;

  /* interface */

  /**
    * To register an instance of class which implements
    * the ActionHandler interface.
    */
  this.register_handler = function(action_handler){};

  /**
    * To get a copy of the current shortcut map.
    */
  this.get_shortcuts = function(){};

  /**
    * To set a new shortcut map.
    * @param {Object} shortcuts
    * @param {String} handler_id
    * @param {Boolean} clear_setting. To reste to defaults
    */
  this.set_shortcuts = function(shortcuts, handler_id, clear_setting){};

  /**
    * To get a list of action handler ids.
    */
  this.get_handlers = function(){};

  /**
    * To propagate UI inputs, e.g. click or right click menu, as actions.
    */
  this.dispatch_action = function(view_id, action_id, event, target){};

  /**
    * To propagate key input as a shortcut action.
    */
  this.dispatch_key_input = function(key_id, event){};

  /**
    * To delay an action propagation,
    * e.g if a click could be followed by a double click.
    * @param {String} type, the event type
    * @param {String} handler_id
    * @param {String} action_id
    * @param {Event} event
    * @param {Element} target
    */
  this.delay_action = function(type, handler_id, action_id, event, target){};

  /**
    * To cancel delayed actions.
    * e.g. if a dbl click has followed a click.
    * @param {String} type, the event type
    */
  this.clear_delayed_actions = function(type){};

  /**
    * To get a list of all actions of a given action handler.
    * @param {String} handler_id
    */
  this.get_actions_with_handler_id = function(handler_id){};

  /**
    * To the label for a given mode.
    * @param {String} handler_id
    * @param {String} mode
    */
  this.get_label_with_handler_id_and_mode = function(handler_id, mode){};

  /**
    * To the global action handler.
    * Any action, which was not cancelled by the focused action handler,
    * gets passed to the global action handler.
    */
  this.get_global_handler = function(){};

  /* constants */

  const GLOBAL_HANDLER = ActionBroker.GLOBAL_HANDLER_ID;

  /* private */

  this._handlers = {};
  this._action_context = null;
  this._action_context_id = '';
  this._container = null;
  this._shortcuts = null;
  this._gloabal_shortcuts = null;
  this._current_shortcuts = null;
  this._global_handler = new GlobalActionHandler(GLOBAL_HANDLER);
  this._delays = {};

  this._set_action_context_bound = (function(event)
  {
    if (!(this._action_context && this._action_context.onclick(event) === false))
    {
      var container = event.target;
      while (container && container.nodeType == 1 &&
             container.parentNode.nodeType == 1 &&
             !/^(?:top-)?(?:container|toolbar|tabs)$/i.test(container.nodeName))
        container = container.parentNode;

      switch (container && container.nodeName.toLowerCase() || '')
      {
        case 'container':
        {
          var ui_obj = UIBase.getUIById(container.getAttribute('ui-id'));
          if (ui_obj)
            this._set_current_handler(ui_obj.view_id, event, container);
          break;
        }
        // TODO set according key handler, e.g. toolbar, tab
        default:
        {
          this._set_current_handler(GLOBAL_HANDLER, event, container);
        }
      }
    }
  }).bind(this);

  this._set_current_handler = function(handler_id, event, container)
  {
    if (handler_id != this._action_context_id)
    {
      if (this._action_context)
        this._action_context.blur();
      if (this._container && this._container.hasClass('edit-mode'))
        this._container.removeClass('edit-mode');
      this._action_context = this._handlers[handler_id] || this._global_handler;
      this._action_context_id = this._action_context.id;
      this._current_shortcuts = this._shortcuts[this._action_context_id] || {};
      this._container = container || document.documentElement;
      this._action_context.focus(event, container);
    }
  }

  this._init = function()
  {
    this._key_identifier = new KeyIdentifier(this.dispatch_key_input.bind(this),
                                             window.ini.browser);
    this.register_handler(this._global_handler);
    window.app.addListener('services-created', function()
    {
      this._shortcuts = window.settings.general.get("shortcuts") ||
                        window.ini.default_shortcuts.windows;
      this._gloabal_shortcuts = this._shortcuts.global;
      this._key_identifier.set_shortcuts(this._get_shortcut_keys());
      this._set_current_handler(this._global_handler);
      document.addEventListener('click', this._set_action_context_bound, true);
    }.bind(this));
  };

  /* implementation */

  this.register_handler = function(action_handler)
  {
    if (!(action_handler && action_handler.id))
      throw 'missing id on action_handler in ActionBroker.instance.register_handler';
    this._handlers[action_handler.id] = action_handler;
  }

  this.dispatch_action = function(action_handler_id, action_id, event, target)
  {
    this._handlers[action_handler_id].handle(action_id, event, target);
  }

  this.dispatch_key_input = function(key_id, event)
  {
    var shortcuts = this._current_shortcuts[this._action_context.mode];
    var action = shortcuts && shortcuts[key_id] || '';
    var propagate_event = true;
    if (action)
      propagate_event = this._action_context.handle(action,
                                                    event,
                                                    this._container);
    if (!(propagate_event === false) &&
         this._action_context != this._global_handler)
    {
      shortcuts = this._gloabal_shortcuts[this._global_handler.mode];
      action = shortcuts && shortcuts[key_id] || '';
      if (action)
        propagate_event = this._global_handler.handle(action,
                                                      event,
                                                      this._container);
    }
    if (propagate_event === false)
    {
      event.stopPropagation();
      event.preventDefault();
    }
  }

  this.delay_action = function(type, handler_id, action_id, event, target)
  {
    if (!this._delays.hasOwnProperty(type))
    {
      this._delays[type] = new Timeouts();
    }
    var cb = this.dispatch_action.bind(this, handler_id, action_id, event, target);
    this._delays[type].set(cb, 300);
  }

  this.clear_delayed_actions = function(type)
  {
    if (this._delays[type])
      this._delays[type].clear();
  }

  this.get_shortcuts = function()
  {
    return window.helpers.copy_object(this._shortcuts);
  }

  this.set_shortcuts = function(shortcuts, handler_id, clear_setting)
  {
    shortcuts = window.helpers.copy_object(shortcuts);
    if (handler_id)
      this._shortcuts[handler_id] = shortcuts;
    else
      this._shortcuts = shortcuts;
    this._gloabal_shortcuts = this._shortcuts.global;
    window.settings.general.set("shortcuts",
                                clear_setting == true ? null : this._shortcuts);
    this._key_identifier.set_shortcuts(this._get_shortcut_keys());
  };

  this.get_actions_with_handler_id = function(handler_id)
  {
    return (
    this._handlers[handler_id] && this._handlers[handler_id].get_action_list());
  };

  this._get_shortcut_keys = function()
  {
    var ret = [], name = '', handler = null, mode = '', key = '';
    for (name in this._shortcuts)
    {
      handler = this._shortcuts[name];
      for (mode in handler)
      {
        for (key in handler[mode])
        {
          if (ret.indexOf(key) == -1)
            ret.push(key);
        }
      }
    }
    return ret;
  };

  this.get_label_with_handler_id_and_mode = function(handler_id, mode)
  {
    return this._handlers[handler_id].mode_labels[mode];
  };

  this.get_global_handler = function()
  {
    return this._global_handler;
  };

  if (document.readyState == "complete")
    this._init();
  else
    document.addEventListener('DOMContentLoaded', this._init.bind(this), false);

}

ActionBroker.get_instance = function()
{
  return this.instance || new ActionBroker();
}

ActionBroker.GLOBAL_HANDLER_ID = "global";
<|endoftext|>


<commit_before>/**
 * @param {Function} f
 * @constructor
 * @namespace
 */
function Thread(f) {
    this.alive = true;
    this.schedule(f);
}

/**
 * The currently running thread.
 * @type Thread
 */
Thread.current = null;

/**
 * @param {Function} f
 * @returns {Function} A function that runs f in the thread.
 * @method
 * @private
 */
Thread.prototype.runner = function(f) {
    var _this = this;
    return function() {
        if (_this.alive) {
            try {
                Thread.current = _this;
                f.apply(this, arguments);
            } finally {
                Thread.current = null;
            }
        }
    };
};

/**
 * Schedule a function to run in this thread.
 * @param {Function} f
 * @method
 * @private
 */
Thread.prototype.schedule = function(f) {
    setTimeout(this.runner(f), 0);
};

/**
 * Permanently stop this thread from running.
 * @method
 */
Thread.prototype.destroy = function() {
    this.alive = false;
};

/**
 * @returns {String}
 * @method
 */
Thread.prototype.toString = function() {
    return '[object Thread]';
};

/**
 * Yield so that other threads can run.
 * @param {Function} f
 */
Thread.yield = function(f) {
    Thread.current.schedule(f);
};

/**
 * Return a version of a function that will run in the current thread.
 * @param {Function} f
 * @returns {Function}
 */
Thread.wrap = function(f) {
    return Thread.current.runner(f);
};
<commit_msg>Support stopping and starting.<commit_after>/**
 * @param {Function} f
 * @constructor
 * @namespace
 */
function Thread(f) {
    this.running = true;
    this.queue = [];
    this.schedule(f);
}

/**
 * The currently running thread.
 * @type Thread
 */
Thread.current = null;

/**
 * @param {Function} f
 * @returns {Function} A function that runs f in the thread.
 * @method
 * @private
 */
Thread.prototype.runner = function(f) {
    var thread = this;
    return function() {
        if (thread.running) {
            try {
                Thread.current = thread;
                f.apply(this, arguments);
            } finally {
                Thread.current = null;
            }
        } else {
            thread.queue.push(f);
        }
    };
};

/**
 * Schedule a function to run in this thread.
 * @param {Function} f
 * @method
 * @private
 */
Thread.prototype.schedule = function(f) {
    setTimeout(this.runner(f), 0);
};

/**
 * Pause the thread, waiting to be restarted.
 * @method
 */
Thread.prototype.stop = function() {
    this.running = false;
};

/**
 * Continue running a stopped thread.
 * @method
 */
Thread.prototype.start = function() {
    this.running = true;
    while (this.queue.length > 0) {
        this.schedule(this.queue.shift());
    }
};

/**
 * @returns {String}
 * @method
 */
Thread.prototype.toString = function() {
    return '[object Thread]';
};

/**
 * Yield so that other threads can run.
 * @param {Function} f
 */
Thread.yield = function(f) {
    Thread.current.schedule(f);
};

/**
 * Return a version of a function that will run in the current thread.
 * @param {Function} f
 * @returns {Function}
 */
Thread.wrap = function(f) {
    return Thread.current.runner(f);
};
<|endoftext|>


<commit_before>goog.require('goog.Disposable');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('webglmaps.Program');
goog.require('webglmaps.TileCoord');
goog.require('webglmaps.TileUrl');

goog.provide('webglmaps.Tile');

/**
 * @constructor
 * @param {WebGLRenderingContext} gl WebGL rendering context.
 * @param {webglmaps.TileCoord} tileCoord Tile coord.
 * @param {webglmaps.TileUrl} tileUrl Tile URL.
 * @extends {goog.events.EventTarget}
 */
webglmaps.Tile = function(gl, tileCoord, tileUrl) {

  goog.base(this);

  /**
   * @private
   * @type {WebGLRenderingContext}
   */
  this.gl_ = gl;

  this.tileCoord_ = tileCoord;

  /**
   * @private
   * @type {WebGLBuffer}
   */
  this.vertexAttribBuffer_ = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexAttribBuffer_);
  var n = 1 << tileCoord.z;
  var x = tileCoord.x, y = n - tileCoord.y - 1;
  var positions = [
    x / n, y / n, 0, 1,
    (x + 1) / n, y / n, 1, 1,
    x / n, (y + 1) / n, 0, 0,
    (x + 1) / n, (y + 1) / n, 1, 0
  ];
  gl.bufferData(
      gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

  /**
   * @private
   * @type {WebGLTexture}
   */
  this.texture_ = null;

  var image = new Image();
  image.crossOrigin = '';
  image.src = tileUrl(this.tileCoord_);

  /**
   * @private
   * @type {?number}
   */
  this.imageLoadListener_ = goog.events.listen(image,
      goog.events.EventType.LOAD, goog.bind(this.onImageLoad_, this));

  /**
   * @private
   * @type {?number}
   */
  this.firstRenderTime_ = null;

};
goog.inherits(webglmaps.Tile, goog.events.EventTarget);

/**
 * @protected
 */
webglmaps.Tile.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');
  var gl = this.gl_;
  this.gl_ = null;
  gl.deleteBuffer(this.vertexAttribBuffer_);
  if (!goog.isNull(this.texture_)) {
    gl.deleteTexture(this.texture_);
    this.texture_ = null;
  }
  if (!goog.isNull(this.imageLoadListener_)) {
    goog.events.unlistenByKey(this.imageLoadListener_);
    this.imageLoadListener_ = null;
  }
};

/**
 * @param {number} time Time.
 * @param {webglmaps.Program} program Program.
 * @return {boolean} Request animation frame?
 */
webglmaps.Tile.prototype.render = function(time, program) {
  var gl = this.gl_;
  if (!this.hasTexture()) {
    return false;
  }
  var alpha, requestAnimationFrame;
  if (goog.isNull(this.firstRenderTime_)) {
    this.firstRenderTime_ = time;
    alpha = 0;
    requestAnimationFrame = true;
  } else if (time - this.firstRenderTime_ < webglmaps.Tile.FADE_IN_PERIOD) {
    alpha = (time - this.firstRenderTime_) / webglmaps.Tile.FADE_IN_PERIOD;
    requestAnimationFrame = true;
  } else {
    alpha = 1;
    requestAnimationFrame = false;
  }
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexAttribBuffer_);
  gl.vertexAttribPointer(
      program.aPositionLocation, 2, gl.FLOAT, false, 16, 0);
  gl.vertexAttribPointer(
      program.aTexCoordLocation, 2, gl.FLOAT, false, 16, 8);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, this.texture_);
  gl.uniform1i(program.uTextureLocation, 0);
  gl.uniform1f(program.uAlphaLocation, alpha);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  return requestAnimationFrame;
};

/**
 * @return {boolean} Has texture?
 */
webglmaps.Tile.prototype.hasTexture = function() {
  return !goog.isNull(this.texture_);
};

/**
 * @param {goog.events.BrowserEvent} event Event.
 * @private
 */
webglmaps.Tile.prototype.onImageLoad_ = function(event) {
  var image = /** @type {Image} */ event.currentTarget;
  var gl = this.gl_;
  this.texture_ = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, this.texture_);
  gl.texImage2D(
      gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.bindTexture(gl.TEXTURE_2D, null);
  goog.events.unlistenByKey(this.imageLoadListener_);
  this.imageLoadListener_ = null;
  this.dispatchEvent(new goog.events.Event(goog.events.EventType.LOAD, this));
};

/**
 * @const
 * @type {number}
 */
webglmaps.Tile.FADE_IN_PERIOD = 2000;
<commit_msg>Remove unused member variable<commit_after>goog.require('goog.Disposable');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('webglmaps.Program');
goog.require('webglmaps.TileCoord');
goog.require('webglmaps.TileUrl');

goog.provide('webglmaps.Tile');

/**
 * @constructor
 * @param {WebGLRenderingContext} gl WebGL rendering context.
 * @param {webglmaps.TileCoord} tileCoord Tile coord.
 * @param {webglmaps.TileUrl} tileUrl Tile URL.
 * @extends {goog.events.EventTarget}
 */
webglmaps.Tile = function(gl, tileCoord, tileUrl) {

  goog.base(this);

  /**
   * @private
   * @type {WebGLRenderingContext}
   */
  this.gl_ = gl;

  /**
   * @private
   * @type {WebGLBuffer}
   */
  this.vertexAttribBuffer_ = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexAttribBuffer_);
  var n = 1 << tileCoord.z;
  var x = tileCoord.x, y = n - tileCoord.y - 1;
  var positions = [
    x / n, y / n, 0, 1,
    (x + 1) / n, y / n, 1, 1,
    x / n, (y + 1) / n, 0, 0,
    (x + 1) / n, (y + 1) / n, 1, 0
  ];
  gl.bufferData(
      gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

  /**
   * @private
   * @type {WebGLTexture}
   */
  this.texture_ = null;

  var image = new Image();
  image.crossOrigin = '';
  image.src = tileUrl(tileCoord);

  /**
   * @private
   * @type {?number}
   */
  this.imageLoadListener_ = goog.events.listen(image,
      goog.events.EventType.LOAD, goog.bind(this.onImageLoad_, this));

  /**
   * @private
   * @type {?number}
   */
  this.firstRenderTime_ = null;

};
goog.inherits(webglmaps.Tile, goog.events.EventTarget);

/**
 * @protected
 */
webglmaps.Tile.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');
  var gl = this.gl_;
  this.gl_ = null;
  gl.deleteBuffer(this.vertexAttribBuffer_);
  if (!goog.isNull(this.texture_)) {
    gl.deleteTexture(this.texture_);
    this.texture_ = null;
  }
  if (!goog.isNull(this.imageLoadListener_)) {
    goog.events.unlistenByKey(this.imageLoadListener_);
    this.imageLoadListener_ = null;
  }
};

/**
 * @param {number} time Time.
 * @param {webglmaps.Program} program Program.
 * @return {boolean} Request animation frame?
 */
webglmaps.Tile.prototype.render = function(time, program) {
  var gl = this.gl_;
  if (!this.hasTexture()) {
    return false;
  }
  var alpha, requestAnimationFrame;
  if (goog.isNull(this.firstRenderTime_)) {
    this.firstRenderTime_ = time;
    alpha = 0;
    requestAnimationFrame = true;
  } else if (time - this.firstRenderTime_ < webglmaps.Tile.FADE_IN_PERIOD) {
    alpha = (time - this.firstRenderTime_) / webglmaps.Tile.FADE_IN_PERIOD;
    requestAnimationFrame = true;
  } else {
    alpha = 1;
    requestAnimationFrame = false;
  }
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexAttribBuffer_);
  gl.vertexAttribPointer(
      program.aPositionLocation, 2, gl.FLOAT, false, 16, 0);
  gl.vertexAttribPointer(
      program.aTexCoordLocation, 2, gl.FLOAT, false, 16, 8);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, this.texture_);
  gl.uniform1i(program.uTextureLocation, 0);
  gl.uniform1f(program.uAlphaLocation, alpha);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  return requestAnimationFrame;
};

/**
 * @return {boolean} Has texture?
 */
webglmaps.Tile.prototype.hasTexture = function() {
  return !goog.isNull(this.texture_);
};

/**
 * @param {goog.events.BrowserEvent} event Event.
 * @private
 */
webglmaps.Tile.prototype.onImageLoad_ = function(event) {
  var image = /** @type {Image} */ event.currentTarget;
  var gl = this.gl_;
  this.texture_ = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, this.texture_);
  gl.texImage2D(
      gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.bindTexture(gl.TEXTURE_2D, null);
  goog.events.unlistenByKey(this.imageLoadListener_);
  this.imageLoadListener_ = null;
  this.dispatchEvent(new goog.events.Event(goog.events.EventType.LOAD, this));
};

/**
 * @const
 * @type {number}
 */
webglmaps.Tile.FADE_IN_PERIOD = 2000;
<|endoftext|>


<commit_before>/*!
 * TouchPoint.js 1.0.0 Beta
 * A JavaScript library that visually shows taps/cicks on HTML prototypes.
 * http://jonahvsweb.com
 *
 * Copyright (c) 2015 Jonah Bitautas <jonahvsweb@gmail.com>
 *
 * Released under the MIT license
 * LICENSE.txt
 */
var TouchPoint;

(function () {

  'use strict';

  TouchPoint = {

    el: 'div',
    dom: '',
    styleEl: '',
    color: '#FFF',
    opacity: 0.8,
    size: 20,
    scale: 8,
    tp: '',
    z: 9999,

    init: function(el) {
      TouchPoint.dom = el;

      TouchPoint
        .createCss('.tp-init',
          'position: absolute; width: ' + TouchPoint.size + 'px; height: ' + TouchPoint.size + 'px; background-color: ' + TouchPoint.color + '; opacity: ' + TouchPoint.opacity + '; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: ' + TouchPoint.z + ';')
        .createCss('.tp-anim',
          '-ms-transform: scale(' + TouchPoint.scale + '); -webkit-transform: scale(' + TouchPoint.scale + '); -moz-transform: scale(' + TouchPoint.scale + '); -o-transform: scale(' + TouchPoint.scale + '); transform: scale(' + TouchPoint.scale + '); opacity: 0;'
        );
      TouchPoint.dom.addEventListener("click", TouchPoint.create, false);
    },

    create: function(e) {
      TouchPoint.dom.removeEventListener("click", TouchPoint.create, false);
      TouchPoint.tp = document.createElement(TouchPoint.el);
      TouchPoint.tp.setAttribute('id', 'touchpoint');
      TouchPoint.tp.style.left = (e.clientX - (TouchPoint.size * 0.5)) + 'px';
      TouchPoint.tp.style.top = (e.clientY - (TouchPoint.size * 0.5)) + 'px';
      TouchPoint.tp.className = 'tp-init';

      document.body.appendChild(TouchPoint.tp);

      requestNextAnimationFrame(function () {
        TouchPoint.tp.className += ' tp-anim';
      });
      TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false);

      return TouchPoint;
    },

    gc: function(e) {
      var currTP = document.body.querySelector('#touchpoint');

      if(currTP) {
        e.target.removeEventListener('transitionend', TouchPoint.gc, false);
        document.body.removeChild(currTP);
        TouchPoint.reInit();
      }
    },

    reInit: function() {
      TouchPoint.dom.addEventListener("click", TouchPoint.create, false);
    },

    createCss: function(name, rules) {
      var head = document.getElementsByTagName('head')[0];

      for(var i = 0; i < head.childNodes.length; i = i + 1) {
        if(head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') {
          head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }';
          TouchPoint.styleEl = head.getElementsByTagName('style')[i];
          break;
        } else {
          TouchPoint.styleEl = document.createElement('style');
          TouchPoint.styleEl.type = 'text/css';
          TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }';
          head.appendChild(TouchPoint.styleEl);
          break;
        }
      }
      return TouchPoint;
    }
  };
  // requestNextAnimationFrame shim from: https://gist.github.com/getify/3004342
  var ids = {};

  function requestId() {
      var id;
      do {
          id = Math.floor(Math.random() * 1E9);
      } while (id in ids);
      return id;
  }

  if (!window.requestNextAnimationFrame) {
      window.requestNextAnimationFrame = function (callback, element) {
          var id = requestId();

          ids[id] = requestAnimationFrame(function () {
              ids[id] = requestAnimationFrame(function (ts) {
                  delete ids[id];
                  callback(ts);
              }, element);
          }, element);

          return id;
      };
  }

  if (!window.cancelNextAnimationFrame) {
      window.cancelNextAnimationFrame = function (id) {
          if (ids[id]) {
              cancelAnimationFrame(ids[id]);
              delete ids[id];
          }
      };
  }
})();<commit_msg>Add default DOM<commit_after>/*!
 * TouchPoint.js 1.0.0 Beta
 * A JavaScript library that visually shows taps/cicks on HTML prototypes.
 * http://jonahvsweb.com
 *
 * Copyright (c) 2015 Jonah Bitautas <jonahvsweb@gmail.com>
 *
 * Released under the MIT license
 * LICENSE.txt
 */
var TouchPoint;

(function () {

  'use strict';

  TouchPoint = {

    el: 'div',
    dom: window,
    styleEl: '',
    color: '#FFF',
    opacity: 0.8,
    size: 20,
    scale: 8,
    tp: '',
    z: 9999,

    init: function(el) {
      TouchPoint.dom = el;

      TouchPoint
        .createCss('.tp-init',
          'position: absolute; width: ' + TouchPoint.size + 'px; height: ' + TouchPoint.size + 'px; background-color: ' + TouchPoint.color + '; opacity: ' + TouchPoint.opacity + '; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: ' + TouchPoint.z + ';')
        .createCss('.tp-anim',
          '-ms-transform: scale(' + TouchPoint.scale + '); -webkit-transform: scale(' + TouchPoint.scale + '); -moz-transform: scale(' + TouchPoint.scale + '); -o-transform: scale(' + TouchPoint.scale + '); transform: scale(' + TouchPoint.scale + '); opacity: 0;'
        );
      TouchPoint.dom.addEventListener("click", TouchPoint.create, false);
    },

    create: function(e) {
      TouchPoint.dom.removeEventListener("click", TouchPoint.create, false);
      TouchPoint.tp = document.createElement(TouchPoint.el);
      TouchPoint.tp.setAttribute('id', 'touchpoint');
      TouchPoint.tp.style.left = (e.clientX - (TouchPoint.size * 0.5)) + 'px';
      TouchPoint.tp.style.top = (e.clientY - (TouchPoint.size * 0.5)) + 'px';
      TouchPoint.tp.className = 'tp-init';

      document.body.appendChild(TouchPoint.tp);

      requestNextAnimationFrame(function () {
        TouchPoint.tp.className += ' tp-anim';
      });
      TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false);

      return TouchPoint;
    },

    gc: function(e) {
      var currTP = document.body.querySelector('#touchpoint');

      if(currTP) {
        e.target.removeEventListener('transitionend', TouchPoint.gc, false);
        document.body.removeChild(currTP);
        TouchPoint.reInit();
      }
    },

    reInit: function() {
      TouchPoint.dom.addEventListener("click", TouchPoint.create, false);
    },

    createCss: function(name, rules) {
      var head = document.getElementsByTagName('head')[0];

      for(var i = 0; i < head.childNodes.length; i = i + 1) {
        if(head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') {
          head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }';
          TouchPoint.styleEl = head.getElementsByTagName('style')[i];
          break;
        } else {
          TouchPoint.styleEl = document.createElement('style');
          TouchPoint.styleEl.type = 'text/css';
          TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }';
          head.appendChild(TouchPoint.styleEl);
          break;
        }
      }
      return TouchPoint;
    }
  };
  // requestNextAnimationFrame shim from: https://gist.github.com/getify/3004342
  var ids = {};

  function requestId() {
      var id;
      do {
          id = Math.floor(Math.random() * 1E9);
      } while (id in ids);
      return id;
  }

  if (!window.requestNextAnimationFrame) {
      window.requestNextAnimationFrame = function (callback, element) {
          var id = requestId();

          ids[id] = requestAnimationFrame(function () {
              ids[id] = requestAnimationFrame(function (ts) {
                  delete ids[id];
                  callback(ts);
              }, element);
          }, element);

          return id;
      };
  }

  if (!window.cancelNextAnimationFrame) {
      window.cancelNextAnimationFrame = function (id) {
          if (ids[id]) {
              cancelAnimationFrame(ids[id]);
              delete ids[id];
          }
      };
  }
})();<|endoftext|>


<commit_before>var uuid  = require("uuid/v4");
var empty = require("is-empty")

function Spec02(){
  this.payload = {
    specversion: "0.2",
    id: uuid()
  };
}

/*
 * Check the spec constraints.
 */
Spec02.prototype.check = function(){

  if(empty(this.payload["type"])) {
    throw {message: "'type' is invalid"};
  }

  if(empty(this.payload["specversion"])) {
    throw {message: "'specversion' is invalid"};
  }

  if(this.payload["specversion"] !== "0.2") {
    throw {message: "'specversion' value is invalid: '"
      + this.payload["specversion"] + "'"};
  }

  if(empty(this.payload["id"])) {
    throw {message: "'id' is invalid"};
  }
}

Spec02.prototype.type = function(_type){
  this.payload["type"] = _type;
  return this;
}

Spec02.prototype.source = function(_source){
  this.payload["source"] = _source;
  return this;
}

Spec02.prototype.id = function(_id){
  this.payload["id"] = _id;
  return this;
}

Spec02.prototype.time = function(_time){
  this.payload["time"] = _time.toISOString();
  return this;
}

Spec02.prototype.schemaurl = function(_schemaurl){
  this.payload["schemaurl"] = _schemaurl;
  return this;
}

Spec02.prototype.contenttype = function(_contenttype){
  this.payload["contenttype"] = _contenttype;
  return this;
}

Spec02.prototype.data = function(_data){
  this.payload["data"] = _data;
  return this;
}

Spec02.prototype.addExtension = function(key, value){
  this.payload[key] = value;
  return this;
}

module.exports = Spec02;
<commit_msg>Semi colon<commit_after>var uuid  = require("uuid/v4");
var empty = require("is-empty");

function Spec02(){
  this.payload = {
    specversion: "0.2",
    id: uuid()
  };
}

/*
 * Check the spec constraints.
 */
Spec02.prototype.check = function(){

  if(empty(this.payload["type"])) {
    throw {message: "'type' is invalid"};
  }

  if(empty(this.payload["specversion"])) {
    throw {message: "'specversion' is invalid"};
  }

  if(this.payload["specversion"] !== "0.2") {
    throw {message: "'specversion' value is invalid: '"
      + this.payload["specversion"] + "'"};
  }

  if(empty(this.payload["id"])) {
    throw {message: "'id' is invalid"};
  }
};

Spec02.prototype.type = function(_type){
  this.payload["type"] = _type;
  return this;
};

Spec02.prototype.source = function(_source){
  this.payload["source"] = _source;
  return this;
};

Spec02.prototype.id = function(_id){
  this.payload["id"] = _id;
  return this;
};

Spec02.prototype.time = function(_time){
  this.payload["time"] = _time.toISOString();
  return this;
};

Spec02.prototype.schemaurl = function(_schemaurl){
  this.payload["schemaurl"] = _schemaurl;
  return this;
};

Spec02.prototype.contenttype = function(_contenttype){
  this.payload["contenttype"] = _contenttype;
  return this;
};

Spec02.prototype.data = function(_data){
  this.payload["data"] = _data;
  return this;
};

Spec02.prototype.addExtension = function(key, value){
  this.payload[key] = value;
  return this;
};

module.exports = Spec02;
<|endoftext|>


<commit_before>const {exec} = require('child_process');
const path = require('path');
const url = require('url');
const git = require('simple-git/promise')();
const axios = require('axios');
const log = require('./logger');

if (!process.env.GITHUB_TOKEN) {
  throw new Error('GITHUB_TOKEN must be defined in environment');
}

if (!process.env.NOW_TOKEN) {
  throw new Error('NOW_TOKEN must be defined in environment');
}

const now = (cmd='') => {
  const nowBin = path.resolve('./node_modules/now/build/bin/now');
  return `${nowBin} ${cmd} --token ${process.env.NOW_TOKEN}`;
};

const githubApi = axios.create({
  headers: {
    'Authorization': `token ${process.env.GITHUB_TOKEN}`
  }
});

function stage(cwd, {alias}) {
  return new Promise((resolve, reject) => {
    const nowProc = exec(now(), {cwd});
    nowProc.stderr.on('data', (error) => reject(new Error(error)));
    nowProc.stdout.on('data', (url) => {
      if (!url) return;
      console.log(`> Aliasing ${url}`);
      log.info(`> Aliasing ${url}`);
      const aliasProc = exec(now(`alias set ${url} ${alias}`), {cwd});
      aliasProc.on('data', (error) => reject(new Error(error)));
      aliasProc.on('close', (code) => {
        log.info(`> Alias ready ${alias}`);
        rmdir(cwd);
        resolve(alias);
      });
    });
  });
}

function sync(cloneUrl, localDirectory, {ref, checkout}) {
  // TODO: Avoid multiple request working on the same localDirectory
  // TODO: Silence the noise
  return git.clone(cloneUrl, localDirectory, ['--depth=1',`--branch=${ref}`])
  .then(() => git.cwd(localDirectory))
  .then(() => {
    log.info(`> Fetching origin#${ref}...`);
    return git.fetch('origin', ref);
  })
  .then(() => {
    log.info(`> Checking out ${ref}#${checkout}...`);
    return git.checkout(checkout);
  });
}

function github(data) {
  if (!['opened', 'synchronize'].includes(data.action)) return {success: false};

  const {repository, pull_request} = data;
  const {ref, sha} = pull_request.head;

  return {
    ref,
    sha,
    success: true,
    name: repository.full_name,
    alias: `https://${repository.name.replace(/[^A-Z0-9]/ig, '-')}-${ref}.now.sh`,
    cloneUrl: url.format(Object.assign(
      url.parse(repository.clone_url),
      {auth: process.env.GITHUB_TOKEN}
    )),
    setStatus: (state, description, targetUrl) => {
      log.info(`> Setting GitHub status to "${state}"...`);
      return githubApi.post(pull_request.statuses_url, {
        state: state,
        target_url: targetUrl,
        description: description,
        context: 'ci/stage-ci'
      });
    }
  };
}

function rmdir(dir) {
  return new Promise((resolve) => {
    const rmProc = exec(`rm -r ${dir}`);

    rmProc.on('close', (code) => {
      resolve();
    });
  });
}

module.exports = {
  stage,
  sync,
  github
};
<commit_msg>Remove console.log<commit_after>const {exec} = require('child_process');
const path = require('path');
const url = require('url');
const git = require('simple-git/promise')();
const axios = require('axios');
const log = require('./logger');

if (!process.env.GITHUB_TOKEN) {
  throw new Error('GITHUB_TOKEN must be defined in environment');
}

if (!process.env.NOW_TOKEN) {
  throw new Error('NOW_TOKEN must be defined in environment');
}

const now = (cmd='') => {
  const nowBin = path.resolve('./node_modules/now/build/bin/now');
  return `${nowBin} ${cmd} --token ${process.env.NOW_TOKEN}`;
};

const githubApi = axios.create({
  headers: {
    'Authorization': `token ${process.env.GITHUB_TOKEN}`
  }
});

function stage(cwd, {alias}) {
  return new Promise((resolve, reject) => {
    const nowProc = exec(now(), {cwd});
    nowProc.stderr.on('data', (error) => reject(new Error(error)));
    nowProc.stdout.on('data', (url) => {
      if (!url) return;
      log.info(`> Aliasing ${url}`);
      const aliasProc = exec(now(`alias set ${url} ${alias}`), {cwd});
      aliasProc.on('data', (error) => reject(new Error(error)));
      aliasProc.on('close', (code) => {
        log.info(`> Alias ready ${alias}`);
        rmdir(cwd);
        resolve(alias);
      });
    });
  });
}

function sync(cloneUrl, localDirectory, {ref, checkout}) {
  // TODO: Avoid multiple request working on the same localDirectory
  // TODO: Silence the noise
  return git.clone(cloneUrl, localDirectory, ['--depth=1',`--branch=${ref}`])
  .then(() => git.cwd(localDirectory))
  .then(() => {
    log.info(`> Fetching origin#${ref}...`);
    return git.fetch('origin', ref);
  })
  .then(() => {
    log.info(`> Checking out ${ref}#${checkout}...`);
    return git.checkout(checkout);
  });
}

function github(data) {
  if (!['opened', 'synchronize'].includes(data.action)) return {success: false};

  const {repository, pull_request} = data;
  const {ref, sha} = pull_request.head;

  return {
    ref,
    sha,
    success: true,
    name: repository.full_name,
    alias: `https://${repository.name.replace(/[^A-Z0-9]/ig, '-')}-${ref}.now.sh`,
    cloneUrl: url.format(Object.assign(
      url.parse(repository.clone_url),
      {auth: process.env.GITHUB_TOKEN}
    )),
    setStatus: (state, description, targetUrl) => {
      log.info(`> Setting GitHub status to "${state}"...`);
      return githubApi.post(pull_request.statuses_url, {
        state: state,
        target_url: targetUrl,
        description: description,
        context: 'ci/stage-ci'
      });
    }
  };
}

function rmdir(dir) {
  return new Promise((resolve) => {
    const rmProc = exec(`rm -r ${dir}`);

    rmProc.on('close', (code) => {
      resolve();
    });
  });
}

module.exports = {
  stage,
  sync,
  github
};
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import { Redirect } from 'react-router-dom'
import performLogout from 'frontend/firebase/logout'

class Logout extends React.Component<*> {
  componentDidMount() {
    performLogout()
  }

  render() {
    return <Redirect to="/" />
  }
}

export default Logout
<commit_msg>Clear user login state in Redux on logout.<commit_after>// @flow
import * as React from 'react'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import { USER_LOGGED_OUT } from 'frontend/actions/types'
import performLogout from 'frontend/firebase/logout'

type Props = {
  logout: Function
}
class Logout extends React.Component<Props> {
  componentDidMount() {
    performLogout()
    this.props.logout()
  }

  render() {
    return <Redirect to="/" />
  }
}

const mapDispatchToProps = (dispatch: Function) => ({
  logout: () => dispatch({ type: USER_LOGGED_OUT })
})

export default connect(null, mapDispatchToProps)(Logout)
<|endoftext|>


<commit_before>// --------------------------------------------------
// Helps with the search
// --------------------------------------------------
define(function (require) {

	// Dependencies
	var $ = require('jquery'),
		_ = require('underscore'),
		Backbone = require('backbone'),
		storage = require('decoy/plugins/kizzy')('decoy.search');

	// Make the key used to save the state
	var state_key = 'state-'+location.pathname;

	// Public view module
	var Search = Backbone.View.extend({

		// Properties
		visible: false,

		// Init
		initialize: function () {
			_.bindAll(this);

			// Set the initial state
			this.visible = storage.get('visible') ? true : false;
			if (this.visible) this.$el.show();

			// Cache selectors
			this.schema = this.$el.data('schema');
			this.title = this.$el.data('title');
			this.$conditions = this.$('.conditions');
			this.$submit = this.$conditions.find('button[type="submit"]');
			this.$search_actions = $('h1 .search-toggle').closest('.btn-group');

			// Make the add and substract buttons
			this.$add = $('<button type="button" class="btn add">');
			this.$subtract = $('<button type="button" class="btn subtract">');

			// Listen for the clicks on the open/close and clear buttons
			this.$search_actions.find('.search-toggle').click(this.toggle);
			this.$search_actions.find('.search-clear').click(this.clear);

			// Add an initial row
			if (this.defrost() === false) this.add();

			// Defer animation of the clear button
			_.defer(_.bind(function() {
				this.$search_actions.addClass('initialized');
			}, this));

			// Redirect the page to apply the filter if there is no query in the
			// url but there is at state.
			// Not currently applied cause it was weird UX
			// this.applyState();

		},

		// Events
		events: {
			'change .fields': 'change',
			'click .add': 'add',
			'click .subtract': 'subtract',
			'change .comparisons': 'freeze',
			'change .input-field': 'freeze',
			'input .input-field': 'freeze',
			'submit': 'submit'
		},

		//----------------------------------------------------------------
		// Render the UI
		//----------------------------------------------------------------

		// Toggle the search open and close
		toggle: function(e) {
			e.preventDefault();

			// Remember the state
			this.visible = !this.visible;
			storage.set('visible', this.visible);

			// Animate
			this.$el.slideToggle();
			this.toggleClear();

		},

		// The user has changed a condition to a different field
		change: function(e) {
			if (e) e.preventDefault();

			// Get the condition
			var $condition;
			if (e) $condition = $(e.target).closest('.condition');
			else $condition = this.$conditions.find('.condition').last();

			// Get the type
			var $fields = $condition.find('.fields'),
				field = $fields.val();

			// Add comparison options
			this.removeComparisons($condition);
			$condition.find('.fields').after(this.addComparisons(this.schema[field]));

			// Remember the form
			this.freeze();

		},

		// Return type specific comparison options
		addComparisons: function(meta) {
			switch(meta.type) {

				// Text input
				case 'text':
					return '<select class="comparisons">'+
							'<option value="%*%">contains</option>'+
							'<option value="=">is exactly</option>'+
							'<option value="*%">begins with</option>'+
							'<option value="%*">ends with</option>'+
							'<option value="!%*%">doesn\'t contain</option>'+
						'</select>'+
						'<input type="text" class="input input-field"/>';

				// Date selector
				case 'date':
					return $('<select class="comparisons">'+
							'<option value=">">is after</option>'+
							'<option value="<">is before</option>'+
							'<option value="=">is on</option>'+
						'</select>'+
						'
'+
							'<input class="date input-field" maxlength="10" placeholder="mm/dd/yyyy" type="text">'+
							''+
						'
').datepicker();

				// Number selector
				case 'number':
					return '<select class="comparisons">'+
							'<option value="=">is</option>'+
							'<option value="!=">is not</option>'+
							'<option value="<">is less than</option>'+
							'<option value=">">is greater than</option>'+
						'</select>'+
						'<input type="number" class="input input-field">';

				// Select menu
				case 'select':
					var comparisons = '<select class="comparisons">'+
							'<option value="=">is</option>'+
							'<option value="!=">is not</option>'+
						'</select>';
					var $select = $('<select class="input input-field">');
					_.each(meta.options, function(label, value) {
						$select.append($('<option>').text(label).val(value));
					});
					return comparisons + $select[0].outerHTML;
			}
		},

		// Remove any existing comparisons on a condition
		removeComparisons: function($condition) {
			$condition.find('.comparisons,.input').remove();
		},

		// Add a new condition
		add: function(e) {
			if (e) e.preventDefault();

			// Figure out if this is the initial row
			var is_first = this.$conditions.find('.condition').length === 0;

			// Container
			var $condition = $('
').addClass('condition');

			// Add initial title
			if (is_first) $condition.append('Find '+this.title+' where the');
			else $condition.append('and where the');

			// Add the fields list
			if (_.size(this.schema) > 1) {
				$condition.append('');
				var $fields = $('<select>').addClass('fields');
				_.each(this.schema, function(meta, field) {
					$fields.append($('<option>').text(meta.label.toLowerCase()).val(field));
				});
				$condition.append($fields);

			// There is only a single field
			} else {
				$condition.find('.intro').append(' '+_.values(this.schema)[0].label.toLowerCase()+'');
				$condition.append('<input type="hidden" class="fields" value="'+_.keys(this.schema)[0]+'"/>');
			}

			// Add the add/substract button
			if (is_first) $condition.append(this.$add.clone());
			else $condition.append(this.$subtract.clone());

			// Add the new condition to the dom
			this.$submit.before($condition);

			// Produce input fields for the first field
			this.change();

			// Update the cached version
			this.freeze();

			// Return the new condition
			return $condition;

		},

		// Remove a condition
		subtract: function(e) {
			if (e) e.preventDefault();
			var $condition = $(e.target).closest('.condition');
			$condition.remove();
			this.freeze();
		},

		// Toggle the clear button
		toggleClear: function() {

			// Get the conditions from the frozen state
			var conditions = storage.get(state_key);

			// Anytime we serialize, check if we should show or hide the clear button.
			// The form must be visible and have more than one condition or an input
			// value in the first condition.  This function gets called often but jquery
			// won't add a class more than once, so it won't be triggered too often.
			if (this.visible && (conditions.length > 1 || conditions[0][2])) this.$search_actions.removeClass('closed');
			else this.$search_actions.addClass('closed');

		},

		//----------------------------------------------------------------
		// Store and recall the state of the form
		//----------------------------------------------------------------

		// Freeze the state of the form in storage
		freeze: function() {
			storage.set(state_key, this.serialize());
			this.toggleClear();
		},

		// Restore the form from a frozen state
		defrost: function() {
			var conditions = storage.get(state_key);
			if (!conditions || conditions.length === 0) return false;

			// Loop through the conditions, add new rows, and then set them to what
			// was in the conditions
			_.each(conditions, function(condition) {
				var $condition = this.add();

				// Restore choices
				if ($condition.find('select.fields').length) $condition.find('.fields')[0].selectedIndex = condition[0];
				this.change(); // selectedIndex won't tigger the handler automatically
				$condition.find('.comparisons').val(condition[1]);
				$condition.find('.input-field').val(condition[2]);

			}, this);

			// Update the state after the last change
			this.freeze();
		},

		// Serialize the state of the form.  It's done in a terse form because
		// the serialized form will be converted to JSON and used as the query
		// for the page as well
		serialize: function(ignore_empty) {

			// Loop through the conditions
			var conditions = [];
			this.$conditions.find('.condition').each(function() {
				var $condition = $(this);

				// Lookup vals
				var field = $condition.find('select.fields').length ? $condition.find('.fields')[0].selectedIndex : 0,
					comparison = $condition.find('.comparisons').val(),
					input = $condition.find('.input-field').val();

				// Don't add empty items
				if (ignore_empty && !input) return;

				// Add the field choice, comparison choice, and selected value
				conditions.push([field, comparison, input]);

			});

			// Return object that has the state
			return conditions;
		},

		//----------------------------------------------------------------
		// Act on the contents of the form
		//----------------------------------------------------------------

		// Submit the form by redirecting with the serialized query
		submit: function(e) {
			if (e) e.preventDefault();

			// Remove any existing query from the search
			var search = this.stripQuery(location.search);

			// Add the query
			var query = this.serialize(true);
			if (query.length) {
				if (!search) search = '?';
				else search += '&';
				search += 'query='+encodeURIComponent(JSON.stringify(query));
			}

			// Redirect the page
			location.href = location.pathname+search;

		},

		// Clear the form
		clear: function(e) {
			if (e) e.preventDefault();

			// Clear the state
			storage.set(state_key, null);

			// Redirect with no query
			var search = this.stripQuery(location.search);
			location.href = location.origin+location.pathname+search;
		},

		// Redirect the page to apply the filter if there is no query in the
		// url but there is at state
		applyState: function() {

			// If there is a query in the location, then do nothing
			if (location.search.match(/query=/)) return;

			// If there is no state, do nothing
			if (!storage.get(state_key)) return;

			// Otherwise, redirect the page by doing a submit
			this.submit();

		},

		//----------------------------------------------------------------
		// Utils
		//----------------------------------------------------------------

		// Remove the query from the search query
		stripQuery: function(search) {
			return search.replace(/\??&?query=[^&]+/, '');
		}

	});

	return Search;

});<commit_msg>Fixing search reset in FF<commit_after>// --------------------------------------------------
// Helps with the search
// --------------------------------------------------
define(function (require) {

	// Dependencies
	var $ = require('jquery'),
		_ = require('underscore'),
		Backbone = require('backbone'),
		storage = require('decoy/plugins/kizzy')('decoy.search');

	// Make the key used to save the state
	var state_key = 'state-'+location.pathname;

	// Public view module
	var Search = Backbone.View.extend({

		// Properties
		visible: false,

		// Init
		initialize: function () {
			_.bindAll(this);

			// Set the initial state
			this.visible = storage.get('visible') ? true : false;
			if (this.visible) this.$el.show();

			// Cache selectors
			this.schema = this.$el.data('schema');
			this.title = this.$el.data('title');
			this.$conditions = this.$('.conditions');
			this.$submit = this.$conditions.find('button[type="submit"]');
			this.$search_actions = $('h1 .search-toggle').closest('.btn-group');

			// Make the add and substract buttons
			this.$add = $('<button type="button" class="btn add">');
			this.$subtract = $('<button type="button" class="btn subtract">');

			// Listen for the clicks on the open/close and clear buttons
			this.$search_actions.find('.search-toggle').click(this.toggle);
			this.$search_actions.find('.search-clear').click(this.clear);

			// Add an initial row
			if (this.defrost() === false) this.add();

			// Defer animation of the clear button
			_.defer(_.bind(function() {
				this.$search_actions.addClass('initialized');
			}, this));

			// Redirect the page to apply the filter if there is no query in the
			// url but there is at state.
			// Not currently applied cause it was weird UX
			// this.applyState();

		},

		// Events
		events: {
			'change .fields': 'change',
			'click .add': 'add',
			'click .subtract': 'subtract',
			'change .comparisons': 'freeze',
			'change .input-field': 'freeze',
			'input .input-field': 'freeze',
			'submit': 'submit'
		},

		//----------------------------------------------------------------
		// Render the UI
		//----------------------------------------------------------------

		// Toggle the search open and close
		toggle: function(e) {
			e.preventDefault();

			// Remember the state
			this.visible = !this.visible;
			storage.set('visible', this.visible);

			// Animate
			this.$el.slideToggle();
			this.toggleClear();

		},

		// The user has changed a condition to a different field
		change: function(e) {
			if (e) e.preventDefault();

			// Get the condition
			var $condition;
			if (e) $condition = $(e.target).closest('.condition');
			else $condition = this.$conditions.find('.condition').last();

			// Get the type
			var $fields = $condition.find('.fields'),
				field = $fields.val();

			// Add comparison options
			this.removeComparisons($condition);
			$condition.find('.fields').after(this.addComparisons(this.schema[field]));

			// Remember the form
			this.freeze();

		},

		// Return type specific comparison options
		addComparisons: function(meta) {
			switch(meta.type) {

				// Text input
				case 'text':
					return '<select class="comparisons">'+
							'<option value="%*%">contains</option>'+
							'<option value="=">is exactly</option>'+
							'<option value="*%">begins with</option>'+
							'<option value="%*">ends with</option>'+
							'<option value="!%*%">doesn\'t contain</option>'+
						'</select>'+
						'<input type="text" class="input input-field"/>';

				// Date selector
				case 'date':
					return $('<select class="comparisons">'+
							'<option value=">">is after</option>'+
							'<option value="<">is before</option>'+
							'<option value="=">is on</option>'+
						'</select>'+
						'
'+
							'<input class="date input-field" maxlength="10" placeholder="mm/dd/yyyy" type="text">'+
							''+
						'
').datepicker();

				// Number selector
				case 'number':
					return '<select class="comparisons">'+
							'<option value="=">is</option>'+
							'<option value="!=">is not</option>'+
							'<option value="<">is less than</option>'+
							'<option value=">">is greater than</option>'+
						'</select>'+
						'<input type="number" class="input input-field">';

				// Select menu
				case 'select':
					var comparisons = '<select class="comparisons">'+
							'<option value="=">is</option>'+
							'<option value="!=">is not</option>'+
						'</select>';
					var $select = $('<select class="input input-field">');
					_.each(meta.options, function(label, value) {
						$select.append($('<option>').text(label).val(value));
					});
					return comparisons + $select[0].outerHTML;
			}
		},

		// Remove any existing comparisons on a condition
		removeComparisons: function($condition) {
			$condition.find('.comparisons,.input').remove();
		},

		// Add a new condition
		add: function(e) {
			if (e) e.preventDefault();

			// Figure out if this is the initial row
			var is_first = this.$conditions.find('.condition').length === 0;

			// Container
			var $condition = $('
').addClass('condition');

			// Add initial title
			if (is_first) $condition.append('Find '+this.title+' where the');
			else $condition.append('and where the');

			// Add the fields list
			if (_.size(this.schema) > 1) {
				$condition.append('');
				var $fields = $('<select>').addClass('fields');
				_.each(this.schema, function(meta, field) {
					$fields.append($('<option>').text(meta.label.toLowerCase()).val(field));
				});
				$condition.append($fields);

			// There is only a single field
			} else {
				$condition.find('.intro').append(' '+_.values(this.schema)[0].label.toLowerCase()+'');
				$condition.append('<input type="hidden" class="fields" value="'+_.keys(this.schema)[0]+'"/>');
			}

			// Add the add/substract button
			if (is_first) $condition.append(this.$add.clone());
			else $condition.append(this.$subtract.clone());

			// Add the new condition to the dom
			this.$submit.before($condition);

			// Produce input fields for the first field
			this.change();

			// Update the cached version
			this.freeze();

			// Return the new condition
			return $condition;

		},

		// Remove a condition
		subtract: function(e) {
			if (e) e.preventDefault();
			var $condition = $(e.target).closest('.condition');
			$condition.remove();
			this.freeze();
		},

		// Toggle the clear button
		toggleClear: function() {

			// Get the conditions from the frozen state
			var conditions = storage.get(state_key);

			// Anytime we serialize, check if we should show or hide the clear button.
			// The form must be visible and have more than one condition or an input
			// value in the first condition.  This function gets called often but jquery
			// won't add a class more than once, so it won't be triggered too often.
			if (this.visible && (conditions.length > 1 || conditions[0][2])) this.$search_actions.removeClass('closed');
			else this.$search_actions.addClass('closed');

		},

		//----------------------------------------------------------------
		// Store and recall the state of the form
		//----------------------------------------------------------------

		// Freeze the state of the form in storage
		freeze: function() {
			storage.set(state_key, this.serialize());
			this.toggleClear();
		},

		// Restore the form from a frozen state
		defrost: function() {
			var conditions = storage.get(state_key);
			if (!conditions || conditions.length === 0) return false;

			// Loop through the conditions, add new rows, and then set them to what
			// was in the conditions
			_.each(conditions, function(condition) {
				var $condition = this.add();

				// Restore choices
				if ($condition.find('select.fields').length) $condition.find('.fields')[0].selectedIndex = condition[0];
				this.change(); // selectedIndex won't tigger the handler automatically
				$condition.find('.comparisons').val(condition[1]);
				$condition.find('.input-field').val(condition[2]);

			}, this);

			// Update the state after the last change
			this.freeze();
		},

		// Serialize the state of the form.  It's done in a terse form because
		// the serialized form will be converted to JSON and used as the query
		// for the page as well
		serialize: function(ignore_empty) {

			// Loop through the conditions
			var conditions = [];
			this.$conditions.find('.condition').each(function() {
				var $condition = $(this);

				// Lookup vals
				var field = $condition.find('select.fields').length ? $condition.find('.fields')[0].selectedIndex : 0,
					comparison = $condition.find('.comparisons').val(),
					input = $condition.find('.input-field').val();

				// Don't add empty items
				if (ignore_empty && !input) return;

				// Add the field choice, comparison choice, and selected value
				conditions.push([field, comparison, input]);

			});

			// Return object that has the state
			return conditions;
		},

		//----------------------------------------------------------------
		// Act on the contents of the form
		//----------------------------------------------------------------

		// Submit the form by redirecting with the serialized query
		submit: function(e) {
			if (e) e.preventDefault();

			// Remove any existing query from the search
			var search = this.stripQuery(location.search);

			// Add the query
			var query = this.serialize(true);
			if (query.length) {
				if (!search) search = '?';
				else search += '&';
				search += 'query='+encodeURIComponent(JSON.stringify(query));
			}

			// Redirect the page
			location.href = location.pathname+search;

		},

		// Clear the form
		clear: function(e) {
			if (e) e.preventDefault();

			// Clear the state
			storage.set(state_key, null);

			// Redirect with no query
			var search = this.stripQuery(location.search);
			location.href = location.pathname+search;
		},

		// Redirect the page to apply the filter if there is no query in the
		// url but there is at state
		applyState: function() {

			// If there is a query in the location, then do nothing
			if (location.search.match(/query=/)) return;

			// If there is no state, do nothing
			if (!storage.get(state_key)) return;

			// Otherwise, redirect the page by doing a submit
			this.submit();

		},

		//----------------------------------------------------------------
		// Utils
		//----------------------------------------------------------------

		// Remove the query from the search query
		stripQuery: function(search) {
			return search.replace(/\??&?query=[^&]+/, '');
		}

	});

	return Search;

});<|endoftext|>


<commit_before>var pull = require('pull-stream')
var fs   = require('fs')
var path = require('path')
var split = require('pull-split')

var readdir =
exports.readdir =
pull.Source(function (dir) {
  var ls, ended = false
  return function (abort, cb) {
    if(ended || abort) {
      cb(ended = ended || abort)
    }
    else if(!ls)
      fs.readdir(dir, function (err, _ls) {
        if(err)             cb(ended = err)
        else if(!_ls.length) cb(ended = true)
        else {
          ls = _ls.map(function (f) {
            return path.resolve(dir, f)
          })
          cb(null, ls.shift())
        }
      })
    else if(!ls.length) cb(ended = true)
    else                cb(null, ls.shift())
  }
})

var blocksize = 512

var read =
exports.read =
pull.Source(function (path, options) {

  //C/P from
  // https://github.com/joyent/node/blob/master/lib/fs.js#L172-L202
  /*
    [fs.js](https://github.com/joyent/node/blob/
    2f88272ba298f20f5756415d2a69e6f6617fcbbe/lib/fs.js#L172-L204)
  */
  if (typeof options === 'function' || !options) {
    options = { encoding: null, flag: 'r' };
  } else if (typeof options === 'string') {
    options = { encoding: options, flag: 'r' };
  } else if (!options) {
    options = { encoding: null, flag: 'r' };
  } else if (typeof options !== 'object') {
    throw new TypeError('Bad arguments');
  }

  var encoding = options.encoding;

  var fd, _cb, read, ended

  var flag = options.flag || 'r';
  fs.open(path, flag, 438 /*=0666*/, function(er, fd_) {
    if (er) {
      ended = er
      if(_cb)
        _cb(ended)
    }
    fd = fd_;
    var buffer = new Buffer(blocksize)
    read = function (abort, cb) {
      fs.read(fd, buffer, 0, blocksize, -1, function (err, bytes, buffer) {
        if(err)         return cb(ended = err)
        if(bytes === 0) return cb(true)
        cb(null, buffer.slice(0, bytes))
      })
    }
    read(null, _cb)
  })

  return function (abort, cb) {
    if(!read) _cb = cb
    else read(abort, cb)
  }
})

function testStat(test) {
  return function () {
    return pull.asyncMap(function (e, cb) {
      fs.stat(e, function (err, stat) {
        if(stat && test(stat))
          cb(null, e)
        else
          cb(null, null)
      })
    }).pipe(pull.filter())
  }
}

var isFile =
exports.isFile =
testStat(function (e) { return e.isFile() })

var isDirectory =
exports.isDirectory =
testStat(function (e) { return e.isDirectory() })

var isBlockDevice =
exports.isBlockDevice =
testStat(function (e) { return e.isBlockDevice() })

var isCharacterDevice =
exports.isCharacterDevice =
testStat(function (e) { return e.isCharacterDevice() })

var isSymbolicLink =
exports.isSymbolicLink =
testStat(function (e) { return e.isSymbolicLink() })

var isFIFO =
exports.isFIFO =
testStat(function (e) { return e.isFIFO() })

var isSocket =
exports.isSocket =
testStat(function (e) { return e.isSocket() })

<commit_msg>handle empty dir<commit_after>var pull = require('pull-stream')
var fs   = require('fs')
var path = require('path')
var split = require('pull-split')

var readdir =
exports.readdir =
pull.Source(function (dir, match) {
  var ls, ended = false
  match = (
    !match                           ? null
  : 'function' === typeof match      ? match
  : 'function' === typeof match.test ? match.test.bind(match)
  :                                    null
  )
  return function (abort, cb) {
    if(ended || abort) {
      cb(ended = ended || abort)
    }
    else if(!ls)
      fs.readdir(dir, function (err, _ls) {
        if(err)             cb(ended = err)
        else if(!_ls.length) cb(ended = true)
        else {
          if(match)
            _ls = _ls.filter(match)
          ls = _ls.map(function (f) {
            return path.resolve(dir, f)
          })
          if(ls.length)
            cb(null, ls.shift())
          else
            cb(true)
        }
      })
    else if(!ls.length) cb(ended = true)
    else                cb(null, ls.shift())
  }
})

var blocksize = 512

var read =
exports.read =
pull.Source(function (path, options) {

  //C/P from
  // https://github.com/joyent/node/blob/master/lib/fs.js#L172-L202
  /*
    [fs.js](https://github.com/joyent/node/blob/
    2f88272ba298f20f5756415d2a69e6f6617fcbbe/lib/fs.js#L172-L204)
  */
  if (typeof options === 'function' || !options) {
    options = { encoding: null, flag: 'r' };
  } else if (typeof options === 'string') {
    options = { encoding: options, flag: 'r' };
  } else if (!options) {
    options = { encoding: null, flag: 'r' };
  } else if (typeof options !== 'object') {
    throw new TypeError('Bad arguments');
  }

  var encoding = options.encoding;

  var fd, _cb, read, ended

  var flag = options.flag || 'r';
  fs.open(path, flag, 438 /*=0666*/, function(er, fd_) {
    if (er) {
      ended = er
      if(_cb)
        _cb(ended)
    }
    fd = fd_;
    var buffer = new Buffer(blocksize)
    read = function (abort, cb) {
      fs.read(fd, buffer, 0, blocksize, -1, function (err, bytes, buffer) {
        if(err)         return cb(ended = err)
        if(bytes === 0) return cb(true)
        cb(null, buffer.slice(0, bytes))
      })
    }
    read(null, _cb)
  })

  return function (abort, cb) {
    if(!read) _cb = cb
    else read(abort, cb)
  }
})

var exists =
exports.exists =
function (test) {
  test = test || function (e) {
    return !!e
  }
  return pull.asyncMap(function (e, cb) {
    fs.stat(e, function (err, stat) {
      if(stat && test(stat))
        cb(null, e)
      else
        cb(null, null)
    })
  }).pipe(pull.filter(Boolean))
}

function testStat(test) {
  return function () {
    return exists(test)
  }
}

var isFile =
exports.isFile =
testStat(function (e) { return e.isFile() })

var isDirectory =
exports.isDirectory =
testStat(function (e) { return e.isDirectory() })

var isBlockDevice =
exports.isBlockDevice =
testStat(function (e) { return e.isBlockDevice() })

var isCharacterDevice =
exports.isCharacterDevice =
testStat(function (e) { return e.isCharacterDevice() })

var isSymbolicLink =
exports.isSymbolicLink =
testStat(function (e) { return e.isSymbolicLink() })

var isFIFO =
exports.isFIFO =
testStat(function (e) { return e.isFIFO() })

var isSocket =
exports.isSocket =
testStat(function (e) { return e.isSocket() })
<|endoftext|>