<commit_before>/* -*- Mode: Javascript; js-indent-level: 2 -*- */

var async = require('async');
var naan = require('naan');
var _ = require('underscore');
var util = require('util');

var node = module.exports = {
  /**
   * Save or update an object. If a new object is passed, the callback will
   * return a copy of that object with the <options.id> key set to the id of the
   * created object.
   */
  save: function(obj, key, val, callback) {
    if (typeof key == 'function') {
      callback = key;
      key = val = undefined;
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [key, val];
      async.map(obj, naan.ecurry(txn.save, args, [1, 2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(obj, !key);

    if (!this._isValidId(id)) {
      if (typeof obj !== 'object') {
        return callback(new Error("No data to save"));
      }

      this.node._create(obj, callback);
    } else {
      if (key) this.node._updateProp(obj, key, val, callback);
      else this.node._update(obj, callback);
    }
  },

  /**
   * Create a new object. Maps to /node.
   */
  _create: function(obj, callback) {
    var op = this.operation('node', obj);
    this.call(op, function(err, body, location) {
      if (err) {
        return callback(err);
      }

      var result = _.clone(obj);
      result[this.options.id] = this._extractId(location);

      callback(null, result);
    });
  },

  /**
   * Save the properties of an object. Maps to PUT /node/{id}/properties.
   */
  _update: function(obj, callback) {
    var id = this._getId(obj, true);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var untouchedObj = _.clone(obj);
    if (untouchedObj[this.options.id] != null) {
      delete untouchedObj[this.options.id];
    }

    var endpoint = util.format('%s/properties', this._nodeRoot(id));
    var op = this.operation(endpoint, 'PUT', untouchedObj);
    this.call(op, function(err, body, response) {
      if (err) {
        return callback(err);
      } else {
        return callback(null, obj);
      }
    });
  },

  _updateProp: function(obj, key, val, callback) {
    var id = this._getId(obj);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint = util.format('%s/properties/%s', this._nodeRoot(id), key);
    var op = this.operation(endpoint, 'PUT', val);
    this.call(op, function(err, body) {
      if (err) return callback(err);
      else {
        obj = _.clone(obj);
        obj[key] = val;
        callback(null, obj);
      }
    });
  },

  /**
   * Read an object's properties. Maps to GET node/{id}/properties.
   */
  read: function(id, property, callback) {
    if (typeof property == 'function') {
      callback = property;
      property = null;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.read, property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint;
    if (!property)
      endpoint = util.format('%s/properties', this._nodeRoot(id));
    else
      endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
    var op = this.operation(endpoint);
    this.call(op, function(err, body) {
      if (err) {
        return callback(err);
      } else {
        if (!property) body[this.options.id] = id;
        return callback(null, body);
      }
    });
  },

  /**
   * Delete an object. Maps to DELETE node/{id}
   * If force is truthy, delete node and all its relations.
   */
  delete: function(id, force, callback) {
    var property = null;
    if (typeof force == 'function') {
      callback = force;
      force = false;
    } else if (typeof force == 'string') {
      property = force;
      force = false;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.node.delete, force || property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    var object = id;
    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    if (!force) {
      var endpoint;
      if (!property) endpoint = this._nodeRoot(id);
      else
        endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
      var op = this.operation(endpoint, 'DELETE');
      this.call(op, function(err) {
        if (!property || err || typeof object != 'object') callback(err);
        else {
          delete object[property];
          callback(null, object);
        }
      });
    } else {
      this.query([
        'START node=node({root})',
        'MATCH node-[rel?]-()',
        'DELETE node, rel'
      ].join(' '), {root: id}, function(err) { callback(err) });
    }
  },

  /**
   * Relate objects together. maps to POST node/{first}/relationships
   */
  relate: function(startNode, type, endNode, properties, callback) {
    this.rel.create(startNode, type, endNode, properties, callback);
  },

  /**
   * Retrieve a set of relationships for the given node. Optionally specify the
   * direction and relationship name.
   *
   * `direction` must be one of "all", "in" or "out".
   *
   * db.relationships(obj|id, [direction, [relationshipName]], callback)
   */
  relationships: function(obj, direction, relName, callback) {
    if (typeof direction === 'function') {
      callback = direction;
      direction = 'all';
      relName = '';
    } else {
      if (typeof relName === 'function') {
        callback = relName;
        relName = '';
      }

      if (typeof direction !== 'string') {
        return callback(new Error('Invalid direction - ' + direction));
      } else if (typeof relName !== 'string') {
        return callback(new Error('Invalid relationship name - ' + relName));
      }

      direction = direction.toLowerCase();
      if (['in', 'all', 'out'].indexOf(direction) === -1) {
        return callback(new Error('Invalid direction - ' + direction));
      }
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [ direction, relName ];
      var rels = naan.ecurry(txn.node.relationships, args, [1, 2]);
      async.map(obj, rels, callback);
      return this._safeBatchCommit(txn);
    }

    var objId = this._getId(obj);

    if (!this._isValidId(objId)) {
      callback(new Error("Invalid ID"));
    }

    var nodeRoot = this._nodeRoot(objId);
    var endpoint = util.format('%s/relationships/%s', nodeRoot, direction);
    if (relName) {
      endpoint += "/" + relName;
    }
    var op = this.operation(endpoint);
    var self = this;
    this.call(op, function(err, rels) {
      if (err) {
        callback(err);
      } else {
        rels = rels.map(function(rel) {
          return self._createRelationshipObject(rel);
        });
        callback(null, rels);
      }
    });
  },

  /**
   * Perform a query based on a predicate. The predicate is translated to a
   * cypher query.
   */
  find: function(predicate, any, start, callback) {
    if (typeof any === 'function') {
      callback = any;
      any = false;
      start = 'node(*)';
    } else if (typeof start === 'function') {
      callback = start;
      start = 'node(*)';
    }

    if (Array.isArray(predicate)) {
      var txn = this._safeBatch();
      var finder = naan.ecurry(txn.node.find, [any, start], [1, 2]);
      async.map(predicate, finder, callback);
      return this._safeBatchCommit(txn);
    }

    if (typeof predicate !== 'object') {
      callback(new Error('Invalid Predicate'));
    }

    var matchers = Object.keys(predicate).reduce(function(matchers, key) {
      return matchers.push(util.format(" (n.%s = {%s}) ", key, key)), matchers;
    }, []);

    var cypher = [
      "START n = " + start + " WHERE",
      matchers.join(any ? "or" : "and"),
      "RETURN n"
    ].join(" ");

    var self = this;
    this.query(cypher, predicate, callback);
  },

  saveUnique: function(node, index, key, value, returnExistingOnConflict, cb) {
    if (typeof returnExistingOnConflict == 'function') {
      cb = returnExistingOnConflict;
      returnExistingOnConflict = false;
    }

    if (returnExistingOnConflict) {
      this.node.index.getOrSaveUnique(node, index, key, value, cb);
    } else {
      this.node.index.saveUniqueOrFail(node, index, key, value, cb);
    }
  },

  label: function(node, label, replace, callback) {
    if (typeof replace == 'function') {
      callback = replace;
      replace = false;
    }

    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, naan.ecurry(txn.label, [label, replace], [1,2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));

    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, replace ? 'PUT' : 'POST', label);
    this.call(op, function(err) {
      if (err) callback(err);
      else callback();
    });
  },

  nodesWithLabel: function(label, callback) {
    var endpoint = util.format('label/%s/nodes', label);
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, nodes) {
      if (err || !nodes) return callback(err, nodes);
      nodes = nodes.map(this._createNodeObject);
      callback(null, nodes);
    });
  },

  readLabels: function(node, callback) {
    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));
    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, labels) {
      if (err) callback(err);
      else callback(null, labels);
    });
  }
}
<commit_msg>labels: read labels for more than one node<commit_after>/* -*- Mode: Javascript; js-indent-level: 2 -*- */

var async = require('async');
var naan = require('naan');
var _ = require('underscore');
var util = require('util');

var node = module.exports = {
  /**
   * Save or update an object. If a new object is passed, the callback will
   * return a copy of that object with the <options.id> key set to the id of the
   * created object.
   */
  save: function(obj, key, val, callback) {
    if (typeof key == 'function') {
      callback = key;
      key = val = undefined;
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [key, val];
      async.map(obj, naan.ecurry(txn.save, args, [1, 2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(obj, !key);

    if (!this._isValidId(id)) {
      if (typeof obj !== 'object') {
        return callback(new Error("No data to save"));
      }

      this.node._create(obj, callback);
    } else {
      if (key) this.node._updateProp(obj, key, val, callback);
      else this.node._update(obj, callback);
    }
  },

  /**
   * Create a new object. Maps to /node.
   */
  _create: function(obj, callback) {
    var op = this.operation('node', obj);
    this.call(op, function(err, body, location) {
      if (err) {
        return callback(err);
      }

      var result = _.clone(obj);
      result[this.options.id] = this._extractId(location);

      callback(null, result);
    });
  },

  /**
   * Save the properties of an object. Maps to PUT /node/{id}/properties.
   */
  _update: function(obj, callback) {
    var id = this._getId(obj, true);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var untouchedObj = _.clone(obj);
    if (untouchedObj[this.options.id] != null) {
      delete untouchedObj[this.options.id];
    }

    var endpoint = util.format('%s/properties', this._nodeRoot(id));
    var op = this.operation(endpoint, 'PUT', untouchedObj);
    this.call(op, function(err, body, response) {
      if (err) {
        return callback(err);
      } else {
        return callback(null, obj);
      }
    });
  },

  _updateProp: function(obj, key, val, callback) {
    var id = this._getId(obj);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint = util.format('%s/properties/%s', this._nodeRoot(id), key);
    var op = this.operation(endpoint, 'PUT', val);
    this.call(op, function(err, body) {
      if (err) return callback(err);
      else {
        obj = _.clone(obj);
        obj[key] = val;
        callback(null, obj);
      }
    });
  },

  /**
   * Read an object's properties. Maps to GET node/{id}/properties.
   */
  read: function(id, property, callback) {
    if (typeof property == 'function') {
      callback = property;
      property = null;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.read, property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint;
    if (!property)
      endpoint = util.format('%s/properties', this._nodeRoot(id));
    else
      endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
    var op = this.operation(endpoint);
    this.call(op, function(err, body) {
      if (err) {
        return callback(err);
      } else {
        if (!property) body[this.options.id] = id;
        return callback(null, body);
      }
    });
  },

  /**
   * Delete an object. Maps to DELETE node/{id}
   * If force is truthy, delete node and all its relations.
   */
  delete: function(id, force, callback) {
    var property = null;
    if (typeof force == 'function') {
      callback = force;
      force = false;
    } else if (typeof force == 'string') {
      property = force;
      force = false;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.node.delete, force || property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    var object = id;
    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    if (!force) {
      var endpoint;
      if (!property) endpoint = this._nodeRoot(id);
      else
        endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
      var op = this.operation(endpoint, 'DELETE');
      this.call(op, function(err) {
        if (!property || err || typeof object != 'object') callback(err);
        else {
          delete object[property];
          callback(null, object);
        }
      });
    } else {
      this.query([
        'START node=node({root})',
        'MATCH node-[rel?]-()',
        'DELETE node, rel'
      ].join(' '), {root: id}, function(err) { callback(err) });
    }
  },

  /**
   * Relate objects together. maps to POST node/{first}/relationships
   */
  relate: function(startNode, type, endNode, properties, callback) {
    this.rel.create(startNode, type, endNode, properties, callback);
  },

  /**
   * Retrieve a set of relationships for the given node. Optionally specify the
   * direction and relationship name.
   *
   * `direction` must be one of "all", "in" or "out".
   *
   * db.relationships(obj|id, [direction, [relationshipName]], callback)
   */
  relationships: function(obj, direction, relName, callback) {
    if (typeof direction === 'function') {
      callback = direction;
      direction = 'all';
      relName = '';
    } else {
      if (typeof relName === 'function') {
        callback = relName;
        relName = '';
      }

      if (typeof direction !== 'string') {
        return callback(new Error('Invalid direction - ' + direction));
      } else if (typeof relName !== 'string') {
        return callback(new Error('Invalid relationship name - ' + relName));
      }

      direction = direction.toLowerCase();
      if (['in', 'all', 'out'].indexOf(direction) === -1) {
        return callback(new Error('Invalid direction - ' + direction));
      }
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [ direction, relName ];
      var rels = naan.ecurry(txn.node.relationships, args, [1, 2]);
      async.map(obj, rels, callback);
      return this._safeBatchCommit(txn);
    }

    var objId = this._getId(obj);

    if (!this._isValidId(objId)) {
      callback(new Error("Invalid ID"));
    }

    var nodeRoot = this._nodeRoot(objId);
    var endpoint = util.format('%s/relationships/%s', nodeRoot, direction);
    if (relName) {
      endpoint += "/" + relName;
    }
    var op = this.operation(endpoint);
    var self = this;
    this.call(op, function(err, rels) {
      if (err) {
        callback(err);
      } else {
        rels = rels.map(function(rel) {
          return self._createRelationshipObject(rel);
        });
        callback(null, rels);
      }
    });
  },

  /**
   * Perform a query based on a predicate. The predicate is translated to a
   * cypher query.
   */
  find: function(predicate, any, start, callback) {
    if (typeof any === 'function') {
      callback = any;
      any = false;
      start = 'node(*)';
    } else if (typeof start === 'function') {
      callback = start;
      start = 'node(*)';
    }

    if (Array.isArray(predicate)) {
      var txn = this._safeBatch();
      var finder = naan.ecurry(txn.node.find, [any, start], [1, 2]);
      async.map(predicate, finder, callback);
      return this._safeBatchCommit(txn);
    }

    if (typeof predicate !== 'object') {
      callback(new Error('Invalid Predicate'));
    }

    var matchers = Object.keys(predicate).reduce(function(matchers, key) {
      return matchers.push(util.format(" (n.%s = {%s}) ", key, key)), matchers;
    }, []);

    var cypher = [
      "START n = " + start + " WHERE",
      matchers.join(any ? "or" : "and"),
      "RETURN n"
    ].join(" ");

    var self = this;
    this.query(cypher, predicate, callback);
  },

  saveUnique: function(node, index, key, value, returnExistingOnConflict, cb) {
    if (typeof returnExistingOnConflict == 'function') {
      cb = returnExistingOnConflict;
      returnExistingOnConflict = false;
    }

    if (returnExistingOnConflict) {
      this.node.index.getOrSaveUnique(node, index, key, value, cb);
    } else {
      this.node.index.saveUniqueOrFail(node, index, key, value, cb);
    }
  },

  label: function(node, label, replace, callback) {
    if (typeof replace == 'function') {
      callback = replace;
      replace = false;
    }

    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, naan.ecurry(txn.label, [label, replace], [1,2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));

    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, replace ? 'PUT' : 'POST', label);
    this.call(op, function(err) {
      if (err) callback(err);
      else callback();
    });
  },

  nodesWithLabel: function(label, callback) {
    var endpoint = util.format('label/%s/nodes', label);
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, nodes) {
      if (err || !nodes) return callback(err, nodes);
      nodes = nodes.map(this._createNodeObject);
      callback(null, nodes);
    });
  },

  readLabels: function(node, callback) {
    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, txn.readLabels, function(err, allLabels) {
        if (err) return callback(err);
        callback(null, _.uniq(_.flatten(allLabels)));
      });
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));
    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, labels) {
      if (err) callback(err);
      else callback(null, labels);
    });
  }
}
<|endoftext|>


<commit_before>'use strict';

const whitespace = /\s+/g;

/**
 * Transform a hyphenated property such as `align-self`
 * to camelcase style, `alignSelf` for use with CSS `style` hashes
 *
 * @param {string} prop Property to transform
 * @return {string} Camelcase-style property
 */
const dehyphenate = prop => {
    const length = prop.length;
    let result = '';

    for (let i = 0; i < length; i += 1) {
        if (prop[i] === '-') {
            result += prop[i + 1].toUpperCase();
            i += 1;
        } else {
            result += prop[i];
        }
    }

    return prop;
};

/**
 * Ensure a node is not wrapped before using it in native methods
 *
 * @param {Node|Object} node A node, wrapped or not
 * @return {Node} Unwrapped node
 */
const unwrap = node => node.node === null ? node : node.node;

/**
 * Create an object to manipulate given node's CSS classes
 *
 * @param {HTMLElement} node Input element
 * @see `Set` documentation for behaviour information
 * @return {Object} Set-like object
 */
const wrapClass = (node) => ({
    add: function (el) {
        if (!this.has(el)) {
            const split = node.className.split(whitespace);

            split.push(el);
            node.className = split.join(' ');
        }
    },

    delete: el => {
        const split = node.className.split(whitespace),
            pos = split.indexOf(el);

        if (pos > -1) {
            split.splice(pos, 1);
            node.className = split.join(' ');
        }
    },

    has: el => node.className.split(whitespace).indexOf(el) !== -1,
    clear: () => node.className = '',

    get size() {
        if (node.className.trim() === '') {
            return 0;
        }

        return node.className.split(whitespace).length;
    }

    // TODO: implement forEach, entries, keys, values, @@iterator
});

/**
 * Create an object to manipulate given node's CSS styles
 *
 * @param {HTMLElement} node Input element
 * @see `Map` documentation for behaviour information
 * @return {Object} Map-like object
 */
const wrapStyle = (node) => ({
    set: (prop, value) => node.style[dehyphenate(prop)] = value,
    delete: prop => node.style[dehyphenate(prop)] = '',
    has: prop => node.style[dehyphenate(prop)].trim() !== '',

    get: prop => {
        const result = node.style[dehyphenate(prop)];

        if (result.trim() === '') {
            return null;
        }

        return result;
    },

    clear: () => {
        const length = node.style.length;

        for (let i = 0; i < length; i += 1) {
            node.style[node.style[i]] = '';
        }
    },

    get size() {
        return node.style.length;
    }

    // TODO: implement forEach, entries, keys, values, @@iterator
});

/**
 * Create an object of shortcuts to manipulate
 * given node more easily
 *
 * @param {Node} Input node
 * @return {Object} DOM shortcuts
 */
const wrapNode = (node) => {
    if (node === null || typeof node !== 'object') {
        return null;
    }

    return {
        node,

        // search among children
        find: query => wrapNode(node.querySelector(query)),
        // findAll

        // access node's relative tree (parent, children, siblings)
        parent: () => wrapNode(node.parentNode),
        // children

        // get and set element attributes
        getType: () => node.tagName.toLowerCase().trim(),
        getKind: () => {
            switch (node.nodeType) {
            case 1: return 'element';
            case 3: return 'text';
            case 7: return 'processing-instruction';
            case 8: return 'comment';
            case 9: return 'document';
            case 10: return 'document-type';
            case 11: return 'document-fragment';
            default: return null;
            }
        },

        getAttr: attr => node.getAttribute(attr),
        setAttr: (attr, value) => node.setAttribute(attr, value),

        // place an element in the DOM tree
        append: subnode => node.appendChild(unwrap(subnode)),
        attach: parent => unwrap(parent).appendChild(node),

        // manipulate element's CSS (see wrapClass, wrapStyle)
        class: wrapClass(node),
        style: wrapStyle(node),

        // change an element's content
        getContent: () => node.textContent,
        setContent: val => node.textContent = val,
        getHTML: () => node.innerHTML,
        setHTML: val => node.innerHTML = val,

        // listen to events
        on: (evts, handler) => {
            evts.split(whitespace).forEach(evt => {
                node.addEventListener(evt, handler);
            });
        },

        off: (evts, handler) => {
            evts.split(whitespace).forEach(evt => {
                node.removeEventListener(evt, handler);
            });
        }
    };
};

module.exports = wrapNode;
<commit_msg>:bug: Unwrap null should not throw errors<commit_after>'use strict';

const whitespace = /\s+/g;

/**
 * Transform a hyphenated property such as `align-self`
 * to camelcase style, `alignSelf` for use with CSS `style` hashes
 *
 * @param {string} prop Property to transform
 * @return {string} Camelcase-style property
 */
const dehyphenate = prop => {
    const length = prop.length;
    let result = '';

    for (let i = 0; i < length; i += 1) {
        if (prop[i] === '-') {
            result += prop[i + 1].toUpperCase();
            i += 1;
        } else {
            result += prop[i];
        }
    }

    return prop;
};

/**
 * Ensure a node is not wrapped before using it in native methods
 *
 * @param {Node|Object} node A node, wrapped or not
 * @return {Node} Unwrapped node
 */
const unwrap = node => node === null || node.node === null ? node : node.node;

/**
 * Create an object to manipulate given node's CSS classes
 *
 * @param {HTMLElement} node Input element
 * @see `Set` documentation for behaviour information
 * @return {Object} Set-like object
 */
const wrapClass = (node) => ({
    add: function (el) {
        if (!this.has(el)) {
            const split = node.className.split(whitespace);

            split.push(el);
            node.className = split.join(' ');
        }
    },

    delete: el => {
        const split = node.className.split(whitespace),
            pos = split.indexOf(el);

        if (pos > -1) {
            split.splice(pos, 1);
            node.className = split.join(' ');
        }
    },

    has: el => node.className.split(whitespace).indexOf(el) !== -1,
    clear: () => node.className = '',

    get size() {
        if (node.className.trim() === '') {
            return 0;
        }

        return node.className.split(whitespace).length;
    }

    // TODO: implement forEach, entries, keys, values, @@iterator
});

/**
 * Create an object to manipulate given node's CSS styles
 *
 * @param {HTMLElement} node Input element
 * @see `Map` documentation for behaviour information
 * @return {Object} Map-like object
 */
const wrapStyle = (node) => ({
    set: (prop, value) => node.style[dehyphenate(prop)] = value,
    delete: prop => node.style[dehyphenate(prop)] = '',
    has: prop => node.style[dehyphenate(prop)].trim() !== '',

    get: prop => {
        const result = node.style[dehyphenate(prop)];

        if (result.trim() === '') {
            return null;
        }

        return result;
    },

    clear: () => {
        const length = node.style.length;

        for (let i = 0; i < length; i += 1) {
            node.style[node.style[i]] = '';
        }
    },

    get size() {
        return node.style.length;
    }

    // TODO: implement forEach, entries, keys, values, @@iterator
});

/**
 * Create an object of shortcuts to manipulate
 * given node more easily
 *
 * @param {Node} Input node
 * @return {Object} DOM shortcuts
 */
const wrapNode = (node) => {
    if (node === null || typeof node !== 'object') {
        return null;
    }

    return {
        node,

        // search among children
        find: query => wrapNode(node.querySelector(query)),
        // findAll

        // access node's relative tree (parent, children, siblings)
        parent: () => wrapNode(node.parentNode),
        // children

        // get and set element attributes
        getType: () => node.tagName.toLowerCase().trim(),
        getKind: () => {
            switch (node.nodeType) {
            case 1: return 'element';
            case 3: return 'text';
            case 7: return 'processing-instruction';
            case 8: return 'comment';
            case 9: return 'document';
            case 10: return 'document-type';
            case 11: return 'document-fragment';
            default: return null;
            }
        },

        getAttr: attr => node.getAttribute(attr),
        setAttr: (attr, value) => node.setAttribute(attr, value),

        // place an element in the DOM tree
        append: subnode => node.appendChild(unwrap(subnode)),
        attach: parent => unwrap(parent).appendChild(node),

        // manipulate element's CSS (see wrapClass, wrapStyle)
        class: wrapClass(node),
        style: wrapStyle(node),

        // change an element's content
        getContent: () => node.textContent,
        setContent: val => node.textContent = val,
        getHTML: () => node.innerHTML,
        setHTML: val => node.innerHTML = val,

        // listen to events
        on: (evts, handler) => {
            evts.split(whitespace).forEach(evt => {
                node.addEventListener(evt, handler);
            });
        },

        off: (evts, handler) => {
            evts.split(whitespace).forEach(evt => {
                node.removeEventListener(evt, handler);
            });
        }
    };
};

module.exports = wrapNode;
<|endoftext|>


<commit_before>const DEFAULT_ENCODING = 'UTF8';
const DEFAULT_RESOURCE_TIMEOUT = 3500;
const DEFAULT_PAGE_TIMEOUT = 5000;
const MAX_RESOURCES_CACHE = 50;
const MAX_RESOURCE_AGE = 1000 * 60; // 60 seconds
const MODIFIED_ROUND_TIME = 1000 * 60 * 5; // 5 minutes
const EXPIRY_TIME = 1000 * 60 * 5; // 5 minutes
const SUCCESSFUL_RESPONSE_CODES = [ 200 ]; // 200 OK

var url = require('url');
var fs = require('fs');
var path = require('path');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var _ = require('underscore');
var async = require('async');
var hyperquest = require('hyperquest');
var lru = require('lru-cache');
var zlib = require('zlib');
var cache = lru({
	max: MAX_RESOURCES_CACHE,
	length: function( n ){ return 1 },
	maxAge: MAX_RESOURCE_AGE
});

// escape backslashes in path for use in regex
var escapePathForRegex = function( file_path ){
	return file_path.replace( /(\\)/g, '\\\\' );
};

// rounds datetime to nearest 5 minutes (in the past)
var getRoundedTime = function( datetime, round_by ){
	var remainder = datetime % round_by;
	var rounded_time = new Date( datetime - remainder );
	return rounded_time;
};

// checks if a string is a resource url
var isURL = function( string ){
	return /https?:\/\//.test( string );
};

var Page = function( page_path, options ){

	// properly inherit from EventEmitter part 1
	EventEmitter.call( this );

	var page = this;

	options = options || {};
	this.options = options;
	var server = this.options.server;
	var router = server.router;
	this.path = page_path;
	this.relative_path = path.relative( server.paths.views, page_path );

	// adds a route based on the page's path
	this.createRoute = function(){

		page.is_index = /index\.hbs$/i.test( this.relative_path );
		var route = this.relative_path.replace( /\.[a-z0-9]+$/i, '' ).replace( /\\/g, '/' );
		var route = '/'+ route;
		route = route.replace( '/index', '' ); // replace indexes with base routes
		route = route.replace( /{([a-z_-]*)}/ig, ':$1' ); // replace dynamic bits
		if( route === '' ) route = '/';
		page.route = route;

		// only overwrite existing routes if we're an index page
		var existing_route = _( router.routes.get ).find( function( route_data ){
			return route_data.path === route;
		});
		if( existing_route ){
			server.logger.log( 'Warning. You have a conflicting route at "'+ existing_route.path +'"', 0 );
			if( !page.is_index ) return route; // return out if this isn't an index
			router.routes.get = _( router.routes.get ).without( existing_route ); // ensure the old route is removed if this is an index
		}

		router.get( route +'.json', function( req, res ){
			page.render( req, res, {
				json: true
			});
		});

		router.get( route, function( req, res ){
			page.render( req, res );
		});

		return route;

	};

	// reads the json configuration inside the view
	this.parseConfig = function( callback ){

		var auth_options = server.auth;

		fs.readFile( this.path, DEFAULT_ENCODING, function( err, data ){

			var params = {};
			var params_exec = /^{{!\s([\S\s]+?)\s}}/.exec( data );
			try {
				params = ( params_exec )? JSON.parse( params_exec[1] ): {};
			}
			catch( err ){
				server.logger.log( 'Error preprocessing "'+ page.path +'" '+ err, 0 );
			}
			finally {
				// insert global resource data by name
				if( params.resources ){
					for( var name in params.resources ){
						// reassign string contents to url property
						if( _.isString( params.resources[name] ) ){
							params.resources[name] = {
								url: params.resources[name]
							}
						}
						// mixin global resource options
						if( auth_options ){
							_.each( auth_options, function( options, match ){
								var matcher = new RegExp( match, 'ig' );
								if( matcher.test( params.resources[name].url ) ){
									params.resources[name] = _.extend( params.resources[name], options );
								}
							});
						}
					}
				}
				page.params = params;
				_( page ).extend({
					title: params.title,
					description: params.description,
					name: params.name,
					layout: params.layout
				});
				if( callback ) callback( params );
			}

		});

	};

	// fetches remote resources
	this.fetchResources = function( resources, params, callback ){

		var page = this;
		var resources_data = {};

		if( resources ){
			// convert resources object into array
			var resources_array = _( resources ).map( function( data, name ){
				var resource = {
					name: name,
					data: data
				};
				return resource;
			});
			// loop through array to create new resources object
			async.each( resources_array, function( resource, cb ){
				page.fetchResource( resource.data, params, function( err, response ){
					if( err ){
						server.logger.log( 'Error retrieving resource "'+ resource.name +'": '+ err, 3 );
						return cb( err, null );
					}
					resources_data[resource.name] = response;
					return cb( null, response );
				});
			}, function( err ){
				if( callback ) callback( err, resources_data );
			});
		}
		else {
			if( callback ) callback( null, {} );
		}

	};

	// fetches a single resource and returns its contents
	this.fetchResource = function( resource_data, params, callback ){
		var resource_url = resource_data.url;
		if( !isURL( resource_url ) ) return callback( null, {} );
		// replace variable strings like {this} in the resource url with url or query parameters
		resource_url = resource_url.replace( /\{([^\}]*)\}/ig, function( match, capture, index, str ){
			var replacement = params[capture] || '';
			return replacement;
		});
		// mixin query params defined in resource config
		var url_bits = url.parse( resource_url, true );
		// loop through query params looking for dynamic bits
		var key;
		for( key in resource_data.query ){
			if( typeof resource_data.query[key] === 'string' ){
				resource_data.query[key] = resource_data.query[key].replace( /\{([^\}]*)\}/ig, function( match, capture, index, str ){
					var replacement = params[capture] || '';
					return replacement;
				});
			}
		}
		url_bits.query = _.defaults( url_bits.query, resource_data.query );
		delete url_bits.search; // this will confuse url.format if we leave it in
		var resource_url = url.format( url_bits );
		// attempt to get resource from cache
		var cached_resource = cache.get( resource_url );
		if( cached_resource ){
			server.logger.log( 'Resource recovered from cache: '+ resource_url, 3 );
			return callback( null, cached_resource );
		}

		// retrieve compressed resources if possible
		if ( !resource_data.headers ) resource_data.headers = {};
		if ( !resource_data.headers['Accept-Encoding'] ) resource_data.headers['Accept-Encoding'] = 'gzip,deflate';

		// fetch resource remotely
		hyperquest( resource_url, resource_data, function( err, res ){
			if( err ) return callback( err, null );

			var data = '';
			var response_stream = res;
			// log all resource requests
			server.logger.log( 'Requested resource: ['+ res.statusCode +'] '+ resource_url, 3 );

			if( res.headers['content-encoding'] == 'gzip' || res.headers['content-encoding'] == 'deflate' ) {
				response_stream = res.pipe( new zlib.Unzip() );
			}

			response_stream.on( 'data', function onData( chunk ){
				data += chunk;
			});
			response_stream.on( 'end', function onEnd(){
				var response;
				try {
					response = data.toString( DEFAULT_ENCODING );
					response = JSON.parse( response );
				} catch( err ){
					return callback( err, null );
				}
				// cache resource if it came back successful
				if( SUCCESSFUL_RESPONSE_CODES.indexOf( res.statusCode ) > -1 ){
					cache.set( resource_url, response );
				}
				return callback( null, response );
			});
		});
	};

	// preprocesses the page's context
	this.preprocess = function( context, callback ){

		var preprocessor = server.preprocessors[page.params.preprocessor];

		if( preprocessor ){
			preprocessor.process( context, function( processed_context ){
				if( callback ) callback( processed_context );
			});
		}
		else {
			if( callback ) callback( context );
		}

	};

	// generates the page's markup
	this.render = function( req, res, options ){

		if( server.options.log_level >= 2 ) var start_serve = new Date;
		options = options || {};
		// generate url data to be served in context
		var href = req.protocol +'://'+ req.get('host') + req.url;
		var url_data = url.parse( href, true );
		url_data = _.pick( url_data, 'host', 'port', 'hostname', 'hash', 'search', 'query', 'pathname', 'path', 'href' );
		url_data.origin = req.protocol +'//'+ url_data.host;

		var context = {
			url: url_data,
			page: {
				path: this.path,
				title: this.title,
				description: this.description,
				name: this.name
			},
			parameters: {},
			query: req.query,
			resources: {},
			assets: {
				scripts: '<script src="/compiled/scripts.js"></script>',
				styles: '<link rel="stylesheet" href="/compiled/styles.css" />'
			},
			layout: this.getLayout()
		};
		context = _( context ).defaults( router.locals );

		// req.params is actually an array with crap stuck to it
		// so we have to parse that stuff out into a real object
		var parameters = {};
		for( var key in req.params ) parameters[key] = req.params[key];
		context.parameters = _( parameters ).extend( req.query );

		// actually render the page
		// uses once so we can have a custom timeout for .render
		var renderPage = _.once( function( context ){
			context = context || {};
			if( !server.options.dev ){
				res.set({
					'Cache-Control': 'public, max-age='+ ( 60 * 5 ),
					'Expires': new Date( Date.now() + EXPIRY_TIME ).toUTCString(),
					'Last-Modified': getRoundedTime( Date.now(), MODIFIED_ROUND_TIME ).toUTCString()
				});
			}
			if( options.json ) return res.json( context );
			res.expose( context, 'solidus.context', 'context' );
			server.logger.log( page.route +' served in '+ ( new Date - start_serve ) +'ms', 3 );
			res.render( page.relative_path, context );
		});

		// render the page manually if our context isn't fast enough
		setTimeout( function(){
			renderPage( context )
		}, DEFAULT_PAGE_TIMEOUT );

		if( server.options.log_level >= 2 ) var start_resources = new Date;
		this.fetchResources( page.params.resources, context.parameters, function( err, resources ){
			server.logger.log( page.route +' resources fetched in '+ ( new Date - start_resources ) +'ms', 3 );
			context.resources = resources;
			if( server.options.log_level >= 2 ) var start_preprocess = new Date;
			page.preprocess( context, function( context ){
				server.logger.log( page.route +' preprocessed in '+ ( new Date - start_preprocess ) +'ms', 3 );
				renderPage( context );
			});
		});

	};

	// get the view's layout
	this.getLayout = function(){

		if( this.layout || this.layout === false ) return this.layout;
		var layouts = _( server.layouts ).sortBy( function( layout_path ){
			return -layout_path.length;
		});
		var local_layout = _( layouts ).find( function( layout_path ){
			var layout_dir = layout_path.replace( /layout\..+$/i, '' );
			return page.path.indexOf( layout_dir ) > -1;
		});
		if( !local_layout ) return null;
		local_layout = path.relative( server.paths.views, local_layout );
		return local_layout;

	};

	// removes the page's route
	this.destroy = function(){

		router.routes.get = _( router.routes.get ).reject( function( current_route ){
			return current_route.path === page.route;
		});

	};

	this.createRoute();
	this.parseConfig( function(){
		page.emit( 'ready' );
	});

};

// properly inherit from EventEmitter part 2
util.inherits( Page, EventEmitter );

Page.layouts = {};

module.exports = Page;<commit_msg>add colon to origin string in url object<commit_after>const DEFAULT_ENCODING = 'UTF8';
const DEFAULT_RESOURCE_TIMEOUT = 3500;
const DEFAULT_PAGE_TIMEOUT = 5000;
const MAX_RESOURCES_CACHE = 50;
const MAX_RESOURCE_AGE = 1000 * 60; // 60 seconds
const MODIFIED_ROUND_TIME = 1000 * 60 * 5; // 5 minutes
const EXPIRY_TIME = 1000 * 60 * 5; // 5 minutes
const SUCCESSFUL_RESPONSE_CODES = [ 200 ]; // 200 OK

var url = require('url');
var fs = require('fs');
var path = require('path');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var _ = require('underscore');
var async = require('async');
var hyperquest = require('hyperquest');
var lru = require('lru-cache');
var zlib = require('zlib');
var cache = lru({
	max: MAX_RESOURCES_CACHE,
	length: function( n ){ return 1 },
	maxAge: MAX_RESOURCE_AGE
});

// escape backslashes in path for use in regex
var escapePathForRegex = function( file_path ){
	return file_path.replace( /(\\)/g, '\\\\' );
};

// rounds datetime to nearest 5 minutes (in the past)
var getRoundedTime = function( datetime, round_by ){
	var remainder = datetime % round_by;
	var rounded_time = new Date( datetime - remainder );
	return rounded_time;
};

// checks if a string is a resource url
var isURL = function( string ){
	return /https?:\/\//.test( string );
};

var Page = function( page_path, options ){

	// properly inherit from EventEmitter part 1
	EventEmitter.call( this );

	var page = this;

	options = options || {};
	this.options = options;
	var server = this.options.server;
	var router = server.router;
	this.path = page_path;
	this.relative_path = path.relative( server.paths.views, page_path );

	// adds a route based on the page's path
	this.createRoute = function(){

		page.is_index = /index\.hbs$/i.test( this.relative_path );
		var route = this.relative_path.replace( /\.[a-z0-9]+$/i, '' ).replace( /\\/g, '/' );
		var route = '/'+ route;
		route = route.replace( '/index', '' ); // replace indexes with base routes
		route = route.replace( /{([a-z_-]*)}/ig, ':$1' ); // replace dynamic bits
		if( route === '' ) route = '/';
		page.route = route;

		// only overwrite existing routes if we're an index page
		var existing_route = _( router.routes.get ).find( function( route_data ){
			return route_data.path === route;
		});
		if( existing_route ){
			server.logger.log( 'Warning. You have a conflicting route at "'+ existing_route.path +'"', 0 );
			if( !page.is_index ) return route; // return out if this isn't an index
			router.routes.get = _( router.routes.get ).without( existing_route ); // ensure the old route is removed if this is an index
		}

		router.get( route +'.json', function( req, res ){
			page.render( req, res, {
				json: true
			});
		});

		router.get( route, function( req, res ){
			page.render( req, res );
		});

		return route;

	};

	// reads the json configuration inside the view
	this.parseConfig = function( callback ){

		var auth_options = server.auth;

		fs.readFile( this.path, DEFAULT_ENCODING, function( err, data ){

			var params = {};
			var params_exec = /^{{!\s([\S\s]+?)\s}}/.exec( data );
			try {
				params = ( params_exec )? JSON.parse( params_exec[1] ): {};
			}
			catch( err ){
				server.logger.log( 'Error preprocessing "'+ page.path +'" '+ err, 0 );
			}
			finally {
				// insert global resource data by name
				if( params.resources ){
					for( var name in params.resources ){
						// reassign string contents to url property
						if( _.isString( params.resources[name] ) ){
							params.resources[name] = {
								url: params.resources[name]
							}
						}
						// mixin global resource options
						if( auth_options ){
							_.each( auth_options, function( options, match ){
								var matcher = new RegExp( match, 'ig' );
								if( matcher.test( params.resources[name].url ) ){
									params.resources[name] = _.extend( params.resources[name], options );
								}
							});
						}
					}
				}
				page.params = params;
				_( page ).extend({
					title: params.title,
					description: params.description,
					name: params.name,
					layout: params.layout
				});
				if( callback ) callback( params );
			}

		});

	};

	// fetches remote resources
	this.fetchResources = function( resources, params, callback ){

		var page = this;
		var resources_data = {};

		if( resources ){
			// convert resources object into array
			var resources_array = _( resources ).map( function( data, name ){
				var resource = {
					name: name,
					data: data
				};
				return resource;
			});
			// loop through array to create new resources object
			async.each( resources_array, function( resource, cb ){
				page.fetchResource( resource.data, params, function( err, response ){
					if( err ){
						server.logger.log( 'Error retrieving resource "'+ resource.name +'": '+ err, 3 );
						return cb( err, null );
					}
					resources_data[resource.name] = response;
					return cb( null, response );
				});
			}, function( err ){
				if( callback ) callback( err, resources_data );
			});
		}
		else {
			if( callback ) callback( null, {} );
		}

	};

	// fetches a single resource and returns its contents
	this.fetchResource = function( resource_data, params, callback ){
		var resource_url = resource_data.url;
		if( !isURL( resource_url ) ) return callback( null, {} );
		// replace variable strings like {this} in the resource url with url or query parameters
		resource_url = resource_url.replace( /\{([^\}]*)\}/ig, function( match, capture, index, str ){
			var replacement = params[capture] || '';
			return replacement;
		});
		// mixin query params defined in resource config
		var url_bits = url.parse( resource_url, true );
		// loop through query params looking for dynamic bits
		var key;
		for( key in resource_data.query ){
			if( typeof resource_data.query[key] === 'string' ){
				resource_data.query[key] = resource_data.query[key].replace( /\{([^\}]*)\}/ig, function( match, capture, index, str ){
					var replacement = params[capture] || '';
					return replacement;
				});
			}
		}
		url_bits.query = _.defaults( url_bits.query, resource_data.query );
		delete url_bits.search; // this will confuse url.format if we leave it in
		var resource_url = url.format( url_bits );
		// attempt to get resource from cache
		var cached_resource = cache.get( resource_url );
		if( cached_resource ){
			server.logger.log( 'Resource recovered from cache: '+ resource_url, 3 );
			return callback( null, cached_resource );
		}

		// retrieve compressed resources if possible
		if ( !resource_data.headers ) resource_data.headers = {};
		if ( !resource_data.headers['Accept-Encoding'] ) resource_data.headers['Accept-Encoding'] = 'gzip,deflate';

		// fetch resource remotely
		hyperquest( resource_url, resource_data, function( err, res ){
			if( err ) return callback( err, null );

			var data = '';
			var response_stream = res;
			// log all resource requests
			server.logger.log( 'Requested resource: ['+ res.statusCode +'] '+ resource_url, 3 );

			if( res.headers['content-encoding'] == 'gzip' || res.headers['content-encoding'] == 'deflate' ) {
				response_stream = res.pipe( new zlib.Unzip() );
			}

			response_stream.on( 'data', function onData( chunk ){
				data += chunk;
			});
			response_stream.on( 'end', function onEnd(){
				var response;
				try {
					response = data.toString( DEFAULT_ENCODING );
					response = JSON.parse( response );
				} catch( err ){
					return callback( err, null );
				}
				// cache resource if it came back successful
				if( SUCCESSFUL_RESPONSE_CODES.indexOf( res.statusCode ) > -1 ){
					cache.set( resource_url, response );
				}
				return callback( null, response );
			});
		});
	};

	// preprocesses the page's context
	this.preprocess = function( context, callback ){

		var preprocessor = server.preprocessors[page.params.preprocessor];

		if( preprocessor ){
			preprocessor.process( context, function( processed_context ){
				if( callback ) callback( processed_context );
			});
		}
		else {
			if( callback ) callback( context );
		}

	};

	// generates the page's markup
	this.render = function( req, res, options ){

		if( server.options.log_level >= 2 ) var start_serve = new Date;
		options = options || {};
		// generate url data to be served in context
		var href = req.protocol +'://'+ req.get('host') + req.url;
		var url_data = url.parse( href, true );
		url_data = _.pick( url_data, 'host', 'port', 'hostname', 'hash', 'search', 'query', 'pathname', 'path', 'href', 'protocol' );
		url_data.origin = url_data.protocol +'//'+ url_data.host;

		var context = {
			url: url_data,
			page: {
				path: this.path,
				title: this.title,
				description: this.description,
				name: this.name
			},
			parameters: {},
			query: req.query,
			resources: {},
			assets: {
				scripts: '<script src="/compiled/scripts.js"></script>',
				styles: '<link rel="stylesheet" href="/compiled/styles.css" />'
			},
			layout: this.getLayout()
		};
		context = _( context ).defaults( router.locals );

		// req.params is actually an array with crap stuck to it
		// so we have to parse that stuff out into a real object
		var parameters = {};
		for( var key in req.params ) parameters[key] = req.params[key];
		context.parameters = _( parameters ).extend( req.query );

		// actually render the page
		// uses once so we can have a custom timeout for .render
		var renderPage = _.once( function( context ){
			context = context || {};
			if( !server.options.dev ){
				res.set({
					'Cache-Control': 'public, max-age='+ ( 60 * 5 ),
					'Expires': new Date( Date.now() + EXPIRY_TIME ).toUTCString(),
					'Last-Modified': getRoundedTime( Date.now(), MODIFIED_ROUND_TIME ).toUTCString()
				});
			}
			if( options.json ) return res.json( context );
			res.expose( context, 'solidus.context', 'context' );
			server.logger.log( page.route +' served in '+ ( new Date - start_serve ) +'ms', 3 );
			res.render( page.relative_path, context );
		});

		// render the page manually if our context isn't fast enough
		setTimeout( function(){
			renderPage( context )
		}, DEFAULT_PAGE_TIMEOUT );

		if( server.options.log_level >= 2 ) var start_resources = new Date;
		this.fetchResources( page.params.resources, context.parameters, function( err, resources ){
			server.logger.log( page.route +' resources fetched in '+ ( new Date - start_resources ) +'ms', 3 );
			context.resources = resources;
			if( server.options.log_level >= 2 ) var start_preprocess = new Date;
			page.preprocess( context, function( context ){
				server.logger.log( page.route +' preprocessed in '+ ( new Date - start_preprocess ) +'ms', 3 );
				renderPage( context );
			});
		});

	};

	// get the view's layout
	this.getLayout = function(){

		if( this.layout || this.layout === false ) return this.layout;
		var layouts = _( server.layouts ).sortBy( function( layout_path ){
			return -layout_path.length;
		});
		var local_layout = _( layouts ).find( function( layout_path ){
			var layout_dir = layout_path.replace( /layout\..+$/i, '' );
			return page.path.indexOf( layout_dir ) > -1;
		});
		if( !local_layout ) return null;
		local_layout = path.relative( server.paths.views, local_layout );
		return local_layout;

	};

	// removes the page's route
	this.destroy = function(){

		router.routes.get = _( router.routes.get ).reject( function( current_route ){
			return current_route.path === page.route;
		});

	};

	this.createRoute();
	this.parseConfig( function(){
		page.emit( 'ready' );
	});

};

// properly inherit from EventEmitter part 2
util.inherits( Page, EventEmitter );

Page.layouts = {};

module.exports = Page;
<|endoftext|>


<commit_before>var os = require('os')
var path = require('path')

var p = {}

for (var key in path) {
  if (typeof path[key] === 'function') {
    p[key] = path[key]
  }
}

p.sep = path.sep
path = p

function Darwin () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/Library/Application Support/' + appname
  }
}

function Linux () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function FreeBSD () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function SunOS () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function Windows () {
  this.tempdir = function () {
    return process.env['TEMP']
  }

  this.homedir = function () {
    return process.env['USERPROFILE']
  }

  this.datadir = function (appname) {
    var appData = process.env['LOCALAPPDATA'] || process.env['APPDATA']
    return appData + '\\' + appname
  }
}

var ost = os.type().toLowerCase()

var OSObj = null
if (ost.indexOf('lin') === 0) {
  OSObj = new Linux()
} else if (ost.indexOf('darwin') === 0) {
  OSObj = new Darwin()
} else if (ost.indexOf('freebsd') === 0) {
  OSObj = new FreeBSD()
} else if (ost.indexOf('win') === 0) {
  OSObj = new Windows()
} else if (ost.indexOf('sunos') === 0) {
  OSObj = new SunOS()
} else {
  throw new Error('Unsupported OS: ' + ost)
}

if (path.tempdir == null) {
  path.tempdir = OSObj.tempdir
}

if (path.homedir == null) {
  path.homedir = OSObj.homedir
}

if (path.datadir == null) {
  path.datadir = OSObj.datadir
}

module.exports = path
<commit_msg>lib/path: bugfix for Windows datadir()<commit_after>var os = require('os')
var path = require('path')

var p = {}

for (var key in path) {
  if (typeof path[key] === 'function') {
    p[key] = path[key]
  }
}

p.sep = path.sep
path = p

function Darwin () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/Library/Application Support/' + appname
  }
}

function Linux () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function FreeBSD () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function SunOS () {
  this.tempdir = function () {
    return '/tmp'
  }

  this.homedir = function () {
    return process.env['HOME']
  }

  this.datadir = function (appname) {
    return this.homedir() + '/.config/' + appname
  }
}

function Windows () {
  this.tempdir = function () {
    return process.env['TEMP']
  }

  this.homedir = function () {
    return process.env['USERPROFILE']
  }

  this.datadir = function (appname) {
    var appData = process.env['APPDATA']
    return appData + '\\' + appname
  }
}

var ost = os.type().toLowerCase()

var OSObj = null
if (ost.indexOf('lin') === 0) {
  OSObj = new Linux()
} else if (ost.indexOf('darwin') === 0) {
  OSObj = new Darwin()
} else if (ost.indexOf('freebsd') === 0) {
  OSObj = new FreeBSD()
} else if (ost.indexOf('win') === 0) {
  OSObj = new Windows()
} else if (ost.indexOf('sunos') === 0) {
  OSObj = new SunOS()
} else {
  throw new Error('Unsupported OS: ' + ost)
}

if (path.tempdir == null) {
  path.tempdir = OSObj.tempdir
}

if (path.homedir == null) {
  path.homedir = OSObj.homedir
}

if (path.datadir == null) {
  path.datadir = OSObj.datadir
}

module.exports = path
<|endoftext|>


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

    angular.module('app')
        .directive('pivot', ['$parse', '$compile', 'Lang', 'Connector', function($parse, $compile, Lang, Connector) {
            return {
                link: function(scope, element, attrs) {
                    var desc = scope.getDesc(scope.item.idx);

                    var setup = {
                        container: element[0],
                        dataSource: {
                            pivot: desc.dataSource,
                            MDX2JSONSource: Connector.url.substring(0, Connector.url.length - 1),
                            basicMDX: scope.item.pivotMdx,
                            namespace: Connector.getNamespace(),
                            sendCookies: true
                        },
                        columnResizeAnimation: true,
                        showSummary: true,
                        //loadingMessageHTML: '

',
                        locale: Lang.current,
                        formatNumbers: "#,###.##"
                    };
                    delete scope.model.pivotMdx;

                    var lpt = new LightPivotTable(setup);
                    if (scope.onInit) scope.onInit(lpt);
                }
            };
        }]);

})();<commit_msg>events for linked widgets<commit_after>(function() {
    'use strict';

    angular.module('app')
        .directive('pivot', ['$parse', '$compile', 'Lang', 'Connector', function($parse, $compile, Lang, Connector) {
            return {
                link: function(scope, element, attrs) {
                    var desc = scope.getDesc(scope.item.idx);

                    var setup = {
                        container: element[0],
                        dataSource: {
                            pivot: desc.dataSource,
                            MDX2JSONSource: Connector.url.substring(0, Connector.url.length - 1),
                            basicMDX: scope.item.pivotMdx,
                            namespace: Connector.getNamespace(),
                            sendCookies: true
                        },
                        triggers: {
                            drillDown: scope.item.onDrillDown,
                            back: scope.item.onDrillDown
                            //drillThrough: scope.item.onDrillDown
                        },
                        columnResizeAnimation: true,
                        showSummary: true,
                        //loadingMessageHTML: '

',
                        locale: Lang.current,
                        formatNumbers: "#,###.##"
                    };
                    delete scope.model.pivotMdx;

                    var lpt = new LightPivotTable(setup);
                    if (scope.onInit) scope.onInit(lpt);
                }
            };
        }]);

})();<|endoftext|>


<commit_before>/**
 * Alloy
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * See LICENSE for more information on licensing.
 */

exports.cliVersion = '>=3.X';

exports.init = function (logger, config, cli, appc) {

	var path = require('path')
		fs = require('fs'),
		afs = appc.fs,
		i18n = appc.i18n(__dirname),
		__ = i18n.__,
		__n = i18n.__n,
		pkginfo = appc.pkginfo.package(module),
		exec = require('child_process').exec,
		spawn = require('child_process').spawn,
		parallel = appc.async.parallel;

	cli.addHook('build.pre.compile', function (build, finished) {
		var appDir = path.join(cli.argv['project-dir'], 'app');
		if (!afs.exists(appDir)) {
			logger.info(__('Project not an Alloy app, continuing'));
			finished();
			return;
		}
		logger.info(__('Found Alloy app in %s', appDir.cyan));

		// TODO: Make this check specific to a TiSDK version
		// create a .alloynewcli file to tell old plugins not to run
		var buildDir = path.join(cli.argv['project-dir'], 'build');
		if (!afs.exists(buildDir)) {
			fs.mkdirSync(buildDir);
		}
		fs.writeFileSync(path.join(buildDir, '.alloynewcli'), '');

		var compilerCommand = afs.resolvePath(__dirname, '..', 'Alloy', 'commands', 'compile', 'index.js'),
			config = {
				platform: /(?:iphone|ipad)/.test(cli.argv.platform) ? 'ios' : cli.argv.platform,
				version: '0',
				simtype: 'none',
				devicefamily: /(?:iphone|ios)/.test(cli.argv.platform) ? build.deviceFamily : 'none',
				deploytype: build.deployType || cli.argv['deploy-type'] || 'development'
			};

		config = Object.keys(config).map(function (c) {
			return c + '=' + config[c];
		}).join(',');

		if (afs.exists(compilerCommand)) {
			// we're being invoked from the actual alloy directory!
			// no need to subprocess, just require() and run
			var origLimit = Error.stackTraceLimit;
			Error.stackTraceLimit = Infinity;
			try {
				require(compilerCommand)({}, {
					config: config,
					outputPath: cli.argv['project-dir'],
					_version: pkginfo.version,
				});
			} catch (e) {
				logger.error(__('Alloy compiler failed'));
				e.toString().split('\n').forEach(function (line) {
					line && logger.error(line);
				});
				process.exit(1);
			}
			Error.stackTraceLimit = origLimit;
			finished();
		} else {
			// we have no clue where alloy is installed, so we're going to subprocess
			// alloy and hope it's in the system path or a well known place
			var paths = {};
			parallel(this, ['alloy', 'node'].map(function (bin) {
				return function (done) {
					var envName = 'ALLOY_' + (bin == 'node' ? 'NODE_' : '') + 'PATH';
					if (paths[bin] = process.env[envName]) {
						done();
					} else if (process.platform == 'win32') {
						paths['alloy'] = 'alloy.cmd';
						done();
					} else {
						exec('which ' + bin, function (err, stdout, strerr) {
							if (!err) {
								paths[bin] = stdout.trim();
								done();
							} else {
								parallel(this, [
									'/usr/local/bin/' + bin,
									'/opt/local/bin/' + bin,
									path.join(process.env['HOME'], 'local/bin', bin),
									'/opt/bin/' + bin,
									'/usr/bin/' + bin
								].map(function (p) {
									return function (cb) {
										afs.exists(p) && (paths[bin] = p);
										cb();
									};
								}), done);
							}
						});
					}
				};
			}), function () {
				var cmd = [paths.node, paths.alloy, 'compile', appDir, '--config', config];
				cli.argv['no-colors'] && cmd.push('--no-colors');
				process.platform == 'win32' && cmd.shift();
				logger.info(__('Executing Alloy compile: %s', cmd.join(' ').cyan));

				var child = spawn(cmd.shift(), cmd);

				function checkLine(line) {
					var re = new RegExp('(?:\u001b\\[\\d+m)?\\[?(' + logger.getLevels().join('|') + ')\\]?\s*(?:\u001b\\[\\d+m)?(.*)', 'i');
					if (line) {
						var m = line.match(re);
						if (m) {
							logger[m[1].toLowerCase()](m[2].trim());
						} else {
							logger.debug(line);
						}
					}
				}

				child.stdout.on('data', function (data) {
					data.toString().split('\n').forEach(function (line) {
						checkLine(line);
					});
				});
				child.stderr.on('data', function (data) {
					data.toString().split('\n').forEach(function (line) {
						checkLine(line);
					});
				});
				child.on('exit', function (code) {
					if (code) {
						logger.error(__('Alloy compiler failed'));
						process.exit(1);
					} else {
						logger.info(__('Alloy compiler completed successfully'));
					}
					finished();
				});
			});
		}
	});

};
<commit_msg>[TIMOB-13827] Added code processor hook<commit_after>/**
 * Alloy
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * See LICENSE for more information on licensing.
 */

exports.cliVersion = '>=3.X';

exports.init = function (logger, config, cli, appc) {

	var path = require('path'),
		fs = require('fs'),
		afs = appc.fs,
		i18n = appc.i18n(__dirname),
		__ = i18n.__,
		__n = i18n.__n,
		pkginfo = appc.pkginfo.package(module),
		exec = require('child_process').exec,
		spawn = require('child_process').spawn,
		parallel = appc.async.parallel;

	function run(deviceFamily, deployType, finished) {
		var appDir = path.join(cli.argv['project-dir'], 'app');
		if (!afs.exists(appDir)) {
			logger.info(__('Project not an Alloy app, continuing'));
			finished();
			return;
		}
		logger.info(__('Found Alloy app in %s', appDir.cyan));

		// TODO: Make this check specific to a TiSDK version
		// create a .alloynewcli file to tell old plugins not to run
		var buildDir = path.join(cli.argv['project-dir'], 'build');
		if (!afs.exists(buildDir)) {
			fs.mkdirSync(buildDir);
		}
		fs.writeFileSync(path.join(buildDir, '.alloynewcli'), '');

		var compilerCommand = afs.resolvePath(__dirname, '..', 'Alloy', 'commands', 'compile', 'index.js'),
			config = {
				platform: /(?:iphone|ipad)/.test(cli.argv.platform) ? 'ios' : cli.argv.platform,
				version: '0',
				simtype: 'none',
				devicefamily: /(?:iphone|ios)/.test(cli.argv.platform) ? deviceFamily : 'none',
				deploytype: deployType || cli.argv['deploy-type'] || 'development'
			};

		config = Object.keys(config).map(function (c) {
			return c + '=' + config[c];
		}).join(',');

		if (afs.exists(compilerCommand)) {
			// we're being invoked from the actual alloy directory!
			// no need to subprocess, just require() and run
			var origLimit = Error.stackTraceLimit;
			Error.stackTraceLimit = Infinity;
			try {
				require(compilerCommand)({}, {
					config: config,
					outputPath: cli.argv['project-dir'],
					_version: pkginfo.version,
				});
			} catch (e) {
				logger.error(__('Alloy compiler failed'));
				e.toString().split('\n').forEach(function (line) {
					line && logger.error(line);
				});
				process.exit(1);
			}
			Error.stackTraceLimit = origLimit;
			finished();
		} else {
			// we have no clue where alloy is installed, so we're going to subprocess
			// alloy and hope it's in the system path or a well known place
			var paths = {};
			parallel(this, ['alloy', 'node'].map(function (bin) {
				return function (done) {
					var envName = 'ALLOY_' + (bin == 'node' ? 'NODE_' : '') + 'PATH';
					if (paths[bin] = process.env[envName]) {
						done();
					} else if (process.platform == 'win32') {
						paths['alloy'] = 'alloy.cmd';
						done();
					} else {
						exec('which ' + bin, function (err, stdout, strerr) {
							if (!err) {
								paths[bin] = stdout.trim();
								done();
							} else {
								parallel(this, [
									'/usr/local/bin/' + bin,
									'/opt/local/bin/' + bin,
									path.join(process.env['HOME'], 'local/bin', bin),
									'/opt/bin/' + bin,
									'/usr/bin/' + bin
								].map(function (p) {
									return function (cb) {
										afs.exists(p) && (paths[bin] = p);
										cb();
									};
								}), done);
							}
						});
					}
				};
			}), function () {
				var cmd = [paths.node, paths.alloy, 'compile', appDir, '--config', config];
				cli.argv['no-colors'] && cmd.push('--no-colors');
				process.platform == 'win32' && cmd.shift();
				logger.info(__('Executing Alloy compile: %s', cmd.join(' ').cyan));

				var child = spawn(cmd.shift(), cmd);

				function checkLine(line) {
					var re = new RegExp('(?:\u001b\\[\\d+m)?\\[?(' + logger.getLevels().join('|') + ')\\]?\s*(?:\u001b\\[\\d+m)?(.*)', 'i');
					if (line) {
						var m = line.match(re);
						if (m) {
							logger[m[1].toLowerCase()](m[2].trim());
						} else {
							logger.debug(line);
						}
					}
				}

				child.stdout.on('data', function (data) {
					data.toString().split('\n').forEach(function (line) {
						checkLine(line);
					});
				});
				child.stderr.on('data', function (data) {
					data.toString().split('\n').forEach(function (line) {
						checkLine(line);
					});
				});
				child.on('exit', function (code) {
					if (code) {
						logger.error(__('Alloy compiler failed'));
						process.exit(1);
					} else {
						logger.info(__('Alloy compiler completed successfully'));
					}
					finished();
				});
			});
		}
	}

	cli.addHook('build.pre.compile', function (build, finished) {
		run(build.deployType, build.deviceFamily, finished);
	});

	cli.addHook('codeprocessor.pre.run', function (build, finished) {
		run('none', 'development', finished);
	});
};
<|endoftext|>


<commit_before>const Telegraf = require('../')
const { Extra, Markup } = require('../')

const bot = new Telegraf(process.env.BOT_TOKEN)

bot.use(Telegraf.log())

bot.command('onetime', (ctx) => {
  return ctx.reply('One time keyboard', Markup
    .keyboard([
      '/simple',
      '/inline',
      '/pyramid'
    ])
    .oneTime()
    .resize()
    .extra()
  )
})

bot.command('special', (ctx) => {
  return ctx.reply('Special buttons keyboard', Extra.markup((markup) => {
    return markup.resize()
      .keyboard([
        markup.contactRequestButton('Send contact'),
        markup.locationRequestButton('Send location')
      ])
  }))
})

bot.command('pyramid', (ctx) => {
  return ctx.reply('Keyboard wrap', Extra.markup(
    Markup.keyboard(['one', 'two', 'three', 'four', 'five', 'six'], {
      wrap: (btn, index, currentRow) => currentRow.length >= (index + 1) / 2
    })
  ))
})

bot.command('simple', (ctx) => {
  return ctx.replyWithHTML('Coke or Pepsi?', Extra.markup(
    Markup.keyboard(['Coke', 'Pepsi'])
  ))
})

bot.command('inline', (ctx) => {
  return ctx.reply('Coke or Pepsi?', Extra.HTML().markup(
    Markup.inlineKeyboard([
      Markup.callbackButton('Coke', 'Coke'),
      Markup.callbackButton('Pepsi', 'Pepsi')
    ])))
})

bot.command('random', (ctx) => {
  return ctx.reply('random example',
    Markup.inlineKeyboard([
      Markup.callbackButton('Coke', 'Coke'),
      Markup.callbackButton('Dr Pepper', 'Dr Pepper', Math.random() > 0.5),
      Markup.callbackButton('Pepsi', 'Pepsi')
    ]).extra()
  )
})

bot.hears(/\/wrap (\d+)/, (ctx) => {
  return ctx.reply('Keyboard wrap', Extra.markup(
    Markup.keyboard(['one', 'two', 'three', 'four', 'five', 'six'], {
      columns: parseInt(ctx.match[1])
    })
  ))
})

bot.action('Dr Pepper', (ctx, next) => {
  return ctx.reply('👍').then(next)
})

bot.action(/.+/, (ctx) => {
  return ctx.answerCallbackQuery(`Oh, ${ctx.match[0]}! Great choise`)
})

bot.startPolling(0)
<commit_msg>Update keyboard-bot.js<commit_after>const Telegraf = require('../')
const { Extra, Markup } = require('../')

const bot = new Telegraf(process.env.BOT_TOKEN)

bot.use(Telegraf.log())

bot.command('onetime', (ctx) => {
  return ctx.reply('One time keyboard', Markup
    .keyboard([
      '/simple',
      '/inline',
      '/pyramid'
    ])
    .oneTime()
    .resize()
    .extra()
  )
})

bot.command('custom', (ctx) => {
  return ctx.reply('Custom buttons keyboard', Markup
    .keyboard(
      [
        [
          ['🔍 Search', '😎 Popular'], // Row1 with 2 button
          ['☸ Setting', '📞 Feedback'], // Row2 with 2 button
          ['📢 Ads', '⭐ Rate us', '👥 Share'] // Row3 with 3 button
        ]
      ]
    )
    .oneTime()
    .resize()
    .extra()
  )
})

bot.command('special', (ctx) => {
  return ctx.reply('Special buttons keyboard', Extra.markup((markup) => {
    return markup.resize()
      .keyboard([
        markup.contactRequestButton('Send contact'),
        markup.locationRequestButton('Send location')
      ])
  }))
})

bot.command('pyramid', (ctx) => {
  return ctx.reply('Keyboard wrap', Extra.markup(
    Markup.keyboard(['one', 'two', 'three', 'four', 'five', 'six'], {
      wrap: (btn, index, currentRow) => currentRow.length >= (index + 1) / 2
    })
  ))
})

bot.command('simple', (ctx) => {
  return ctx.replyWithHTML('Coke or Pepsi?', Extra.markup(
    Markup.keyboard(['Coke', 'Pepsi'])
  ))
})

bot.command('inline', (ctx) => {
  return ctx.reply('Coke or Pepsi?', Extra.HTML().markup(
    Markup.inlineKeyboard([
      Markup.callbackButton('Coke', 'Coke'),
      Markup.callbackButton('Pepsi', 'Pepsi')
    ])))
})

bot.command('random', (ctx) => {
  return ctx.reply('random example',
    Markup.inlineKeyboard([
      Markup.callbackButton('Coke', 'Coke'),
      Markup.callbackButton('Dr Pepper', 'Dr Pepper', Math.random() > 0.5),
      Markup.callbackButton('Pepsi', 'Pepsi')
    ]).extra()
  )
})

bot.hears(/\/wrap (\d+)/, (ctx) => {
  return ctx.reply('Keyboard wrap', Extra.markup(
    Markup.keyboard(['one', 'two', 'three', 'four', 'five', 'six'], {
      columns: parseInt(ctx.match[1])
    })
  ))
})

bot.action('Dr Pepper', (ctx, next) => {
  return ctx.reply('👍').then(next)
})

bot.action(/.+/, (ctx) => {
  return ctx.answerCallbackQuery(`Oh, ${ctx.match[0]}! Great choise`)
})

bot.startPolling(0)
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

module.exports = function(cb) {
    var mycro = this;

    // make sure that `mycro.server` exists and resembles an express or restify server
    if (!mycro.server || !mycro.server.listen || !_.isFunction(mycro.server.listen)) {
        mycro.log('silly', 'There doesn\'t appear to be a server to start');
        return cb();
    }

    var port = parseInt(mycro._config.server.port || process.env.PORT);
    if (isNaN(port)) {
        mycro.log('info', 'port is not a number', port);
    }

    mycro.log('silly', 'starting server on port', port);
    mycro.server.listen(port, function(err) {
        if (err) {
            mycro.log('error', 'there was an erro starting the server', err);
            return cb(err);
        }
        mycro.log('info', 'server listening on port', port);
        cb();
    });
};
<commit_msg>fix for issue #26<commit_after>'use strict';

var _ = require('lodash');

module.exports = function(cb) {
    var mycro = this;

    // make sure that `mycro.server` exists and resembles an express or restify server
    if (!mycro.server || !mycro.server.listen || !_.isFunction(mycro.server.listen)) {
        mycro.log('silly', 'There doesn\'t appear to be a server to start');
        return cb();
    }

    var port = mycro._config.server.port || process.env.PORT;
    if (isNaN(port)) {
        mycro.log('info', 'port is not a number', port);
    }

    mycro.log('silly', 'starting server on port', port);
    mycro.server.listen(port, function(err) {
        if (err) {
            mycro.log('error', 'there was an erro starting the server', err);
            return cb(err);
        }
        mycro.log('info', 'server listening on port', port);
        cb();
    });
};
<|endoftext|>


<commit_before>var Train = Backbone.Model.extend({
    idAttribute: 'train_name',

    url: function() {
        return 'api/trains/' + this.get('train_name');
    },

    matchFilter: function(filter) {
        filter = filter.toLowerCase();
        return this.get("train_name").toLowerCase().indexOf(filter) >= 0 ||
            _.some(this.get("stations"), function(s) {
                return s.toLowerCase().indexOf(filter) >= 0
            });
    },

    parse: function(train) {
        train.activeStation = 1;
        train.firstIntermediateStation = 1;
        train.lastIntermediateStation = train.stations.length - 2;
        return train;
    },

    nextStation: function() {
        var activeStation = this.get("activeStation");
        if (activeStation < this.get("lastIntermediateStation")) {
            this.set("activeStation", activeStation + 1);
        }
    },

    prevStation: function() {
        var activeStation = this.get("activeStation");
        if (activeStation > this.get("firstIntermediateStation")) {
            this.set("activeStation", activeStation - 1);
        }
    }
});

var Trains = Backbone.Collection.extend({
    model: Train,
    url: 'api/trains',

    parse: function(resp) {
        return resp.trains;
    }
});

var MainItemView = Backbone.View.extend({
    tagName: 'tr',
    template: _.template($('#main-item-template').html()),

    events: {
        'click .prev-station': 'prevStation',
        'click .next-station': 'nextStation'
    },

    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
    },

    prevStation: function() {
        this.model.prevStation();
    },

    nextStation: function() {
        this.model.nextStation();
    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var MainView = Backbone.View.extend({
    el: '#page',
    template: _.template($('#main-template').html()),
    filterTimeout: 300,
    nameFilter: '',
    lastFilterValue: '',

    events: {
        'keyup': function(e) {
            if (e.target.value != this.nameFilter) {
                var that = this;
                this.nameFilter = e.target.value;
                clearTimeout(this.timer);
                this.timer = setTimeout(function() {
                    that.render();
                }, this.filterTimeout);
            }
        }
    },

    activate: function() {
        if (!this.trains) {
            var that = this;
            this.trains = new Trains();
            this.trains.fetch({success: function() { that.render(); }});
        }
        this.render();
    },

    render: function() {
        if (this.nameFilter) {
            var that = this
            var trains = new Trains(this.trains.filter(function (train) {
                return train.matchFilter(that.nameFilter);
            }));

            if (trains.length > 0) {
                this.$el.html(this.template({state: 'ok'}));
                trains.each(function(train) {
                    var view = new MainItemView({model: train});
                    $('#main').append(view.render().el);
                });
            } else {
                this.$el.html(this.template({state: 'empty'}));
            }

        } else if (this.trains.length > 0) {
            this.$el.html(this.template({state: 'ok'}));
            this.trains.each(function(train) {
                var view = new MainItemView({model: train});
                $('#main').append(view.render().el);
            });
        } else {
            this.$el.html(this.template({state: 'waiting'}));
        }

        $('#filter').focus();
        $('#filter').val(this.nameFilter);
        document.title = 'InfoPasażer Archiver - archiwum opóźnień pociągów';
        return this;
    }
});

var TrainView = Backbone.View.extend({
    el: '#page',
    template: _.template($('#train-template').html()),

    activate: function(name) {
        var that = this;
        var train = new Train({train_name: name});
        train.fetch({success: function(train) { that.render(train); }});
    },

    render: function(train) {
        this.$el.html(this.template(train.toJSON()));
        document.title = train.get('train_name');
        return this;
    }
});

var Router = Backbone.Router.extend({
    routes: {
        '': 'index',
        'train/*name': 'train'
    },

    initialize: function() {
        this.mainView = new MainView();
        this.trainView = new TrainView();
    },

    index: function() {
        this.mainView.activate();
    },

    train: function(name) {
        this.trainView.activate(name);
    }
});

var router = new Router();
Backbone.history.start();
<commit_msg>with bug when missing stations<commit_after>var Train = Backbone.Model.extend({
    idAttribute: 'train_name',

    url: function() {
        return 'api/trains/' + this.get('train_name');
    },

    matchFilter: function(filter) {
        filter = filter.toLowerCase();
        return this.get("train_name").toLowerCase().indexOf(filter) >= 0 ||
            _.some(this.get("stations"), function(s) {
                return s.toLowerCase().indexOf(filter) >= 0
            });
    },

    parse: function(train) {
        if (train.stations) {
            train.activeStation = 1;
            train.firstIntermediateStation = 1;
            train.lastIntermediateStation = train.stations.length - 2;
        }
        return train;
    },

    nextStation: function() {
        var activeStation = this.get("activeStation");
        if (activeStation < this.get("lastIntermediateStation")) {
            this.set("activeStation", activeStation + 1);
        }
    },

    prevStation: function() {
        var activeStation = this.get("activeStation");
        if (activeStation > this.get("firstIntermediateStation")) {
            this.set("activeStation", activeStation - 1);
        }
    }
});

var Trains = Backbone.Collection.extend({
    model: Train,
    url: 'api/trains',

    parse: function(resp) {
        return resp.trains;
    }
});

var MainItemView = Backbone.View.extend({
    tagName: 'tr',
    template: _.template($('#main-item-template').html()),

    events: {
        'click .prev-station': 'prevStation',
        'click .next-station': 'nextStation'
    },

    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
    },

    prevStation: function() {
        this.model.prevStation();
    },

    nextStation: function() {
        this.model.nextStation();
    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var MainView = Backbone.View.extend({
    el: '#page',
    template: _.template($('#main-template').html()),
    filterTimeout: 300,
    nameFilter: '',
    lastFilterValue: '',

    events: {
        'keyup': function(e) {
            if (e.target.value != this.nameFilter) {
                var that = this;
                this.nameFilter = e.target.value;
                clearTimeout(this.timer);
                this.timer = setTimeout(function() {
                    that.render();
                }, this.filterTimeout);
            }
        }
    },

    activate: function() {
        if (!this.trains) {
            var that = this;
            this.trains = new Trains();
            this.trains.fetch({success: function() { that.render(); }});
        }
        this.render();
    },

    render: function() {
        if (this.nameFilter) {
            var that = this
            var trains = new Trains(this.trains.filter(function (train) {
                return train.matchFilter(that.nameFilter);
            }));

            if (trains.length > 0) {
                this.$el.html(this.template({state: 'ok'}));
                trains.each(function(train) {
                    var view = new MainItemView({model: train});
                    $('#main').append(view.render().el);
                });
            } else {
                this.$el.html(this.template({state: 'empty'}));
            }

        } else if (this.trains.length > 0) {
            this.$el.html(this.template({state: 'ok'}));
            this.trains.each(function(train) {
                var view = new MainItemView({model: train});
                $('#main').append(view.render().el);
            });
        } else {
            this.$el.html(this.template({state: 'waiting'}));
        }

        $('#filter').focus();
        $('#filter').val(this.nameFilter);
        document.title = 'InfoPasażer Archiver - archiwum opóźnień pociągów';
        return this;
    }
});

var TrainView = Backbone.View.extend({
    el: '#page',
    template: _.template($('#train-template').html()),

    activate: function(name) {
        var that = this;
        var train = new Train({train_name: name});
        train.fetch({success: function(train) { that.render(train); }});
    },

    render: function(train) {
        this.$el.html(this.template(train.toJSON()));
        document.title = train.get('train_name');
        return this;
    }
});

var Router = Backbone.Router.extend({
    routes: {
        '': 'index',
        'train/*name': 'train'
    },

    initialize: function() {
        this.mainView = new MainView();
        this.trainView = new TrainView();
    },

    index: function() {
        this.mainView.activate();
    },

    train: function(name) {
        this.trainView.activate(name);
    }
});

var router = new Router();
Backbone.history.start();
<|endoftext|>


<commit_before>var app = require("express")();
var pug = require("pug");

var mails = [];
var config;

app.set("views", __dirname + "/views");
app.set("view engine", "pug");

app.get('/', function (req, res) {
  res.type("html");
  res.render('index', { mails: mails });
});

app.get("/emails", function (req, res) {
  res.type("json");
  res.send(JSON.stringify(mails));
});

app.get("/emails/:index(\\d+)", function (req, res, next) {
  res.type("html");
  var index = +req.params["index"];
  if (index < 0 || index >= mails.length) {
    return next();
  }
  res.send(mails[index].html || mails[index].text);
});

app.all("*", function (req, res) {
  res.type("text");
  res.status(404);
  res.send("Not found");
});

function start() {
  app.listen(config.httpPort);
}

module.exports = {
  start: function () {
    start();
  },
  setMails: function (mailsArg) {
    mails = mailsArg;
  },
  setConfig: function (configArg) {
    config = configArg;
  }
};
<commit_msg>Display plain text emails and html emails correctly<commit_after>var app = require("express")();
var pug = require("pug");

var mails = [];
var config;

app.set("views", __dirname + "/views");
app.set("view engine", "pug");

app.get('/', function (req, res) {
  res.type("html");
  res.render('index', { mails: mails });
});

app.get("/emails", function (req, res) {
  res.type("json");
  res.send(JSON.stringify(mails));
});

app.get("/emails/:index(\\d+)", function (req, res, next) {

  var index = +req.params["index"];
  if (index < 0 || index >= mails.length) {
    return next();
  }
  if (mails[index].html === undefined || mails[index].html === null) {
    // plain text mail
    res.type("text");
    // disable mime type sniffing
    res.set('X-Content-Type-Options', 'nosniff');
  } else {
    // html mail
    res.type("html");
  }
  res.send(mails[index].html || mails[index].text);
});

app.all("*", function (req, res) {
  res.type("text");
  res.status(404);
  res.send("Not found");
});

function start() {
  app.listen(config.httpPort);
}

module.exports = {
  start: function () {
    start();
  },
  setMails: function (mailsArg) {
    mails = mailsArg;
  },
  setConfig: function (configArg) {
    config = configArg;
  }
};
<|endoftext|>


<commit_before>$(document).ready(function(){
  // Add scrollspy to <body>
  $('body').scrollspy({target: ".chevron-up || .chevron-up-last", offset: 50}); //chevron-up

  // Add smooth scrolling on all links inside the navbar //chevron-up a
  $(".chevron-up  a || .chevron-up-last a").on('click', function(event) {
    // Make sure this.hash has a value before overriding default behavior
    if (this.hash !== "") {
      // Prevent default anchor click behavior
      event.preventDefault();

      // Store hash
      var hash = this.hash;

      // Using jQuery's animate() method to add smooth page scroll
      // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
      $('html, body').animate({
        scrollTop: $(hash).offset().top
      }, 800, function(){

        // Add hash (#) to URL when done scrolling (default click behavior)
        window.location.hash = hash;
      });
    }  // End if
  });
});
<commit_msg>rmv chevron last class<commit_after>$(document).ready(function(){
  // Add scrollspy to <body>
  $('body').scrollspy({target: ".chevron-up", offset: 50}); //chevron-up

  // Add smooth scrolling on all links inside the navbar //chevron-up a
  $(".chevron-up  a").on('click', function(event) {
    // Make sure this.hash has a value before overriding default behavior
    if (this.hash !== "") {
      // Prevent default anchor click behavior
      event.preventDefault();

      // Store hash
      var hash = this.hash;

      // Using jQuery's animate() method to add smooth page scroll
      // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
      $('html, body').animate({
        scrollTop: $(hash).offset().top
      }, 800, function(){

        // Add hash (#) to URL when done scrolling (default click behavior)
        window.location.hash = hash;
      });
    }  // End if
  });
});
<|endoftext|>


<commit_before>module.exports = {
  auto: false,
  debug: true,
  CRON: '0 * */1 * *',
  authtoken: '',
  guildID: ''
}
// Cron help http://crontab.guru/
<commit_msg>Change default settings Default settings will archive your guild messages automatically every midnight.<commit_after>module.exports = {
  auto: true,
  debug: false,
  CRON: '0 * */1 * *',
  authtoken: '',
  guildID: ''
}
// Cron help http://crontab.guru/
<|endoftext|>