<commit_before>/**
 * Handlebars Collections Helpers
 * Copyright (c) 2013 Jon Schlinkert, Brian Woodward, contributors
 * Licensed under the MIT License (MIT).
 */
'use strict';

// node_modules
var Handlebars = require('../helpers/helpers').Handlebars;
var _          = require('lodash');

// Local utils
var Utils      = require('../utils/utils');

// The module to be exported
var helpers = {

  /**
   * {{any}}
   * @param  {Array}  array
   * @param  {Object} options
   */
  any: function (array, options) {
    if (array.length > 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * Returns all of the items in the collection after the specified count.
   * @param  {Array}  array Collection
   * @param  {Number} count Number of items to exclude
   * @return {Array}        Array excluding the number of items specified
   */
  after: function (array, count) {
    return array.slice(count);
  },

  /**
   * Use all of the items in the collection after the specified count
   * inside a block.
   * @param  {Array}  array
   * @param  {Number} count
   * @param  {Ojbect} options
   * @return {Array}
   */
  withAfter: function (array, count, options) {
    array = array.slice(count);
    var result = '';
    for (var item in array) {
      result += options.fn(array[item]);
    }
    return result;
  },

  /**
   * {{arrayify}}
   * Converts a string such as "foo, bar, baz" to an ES Array of strings.
   * @credit: http://bit.ly/1840DsB
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  arrayify: function (str) {
    return str.split(",").map(function (tag) {
      return "\"" + tag + "\"";
    });
  },

  /**
   * Returns all of the items in the collection before the specified
   * count. Opposite of {{after}}.
   * @param  {Array}  array [description]
   * @param  {[type]} count [description]
   * @return {[type]}       [description]
   */
  before: function (array, count) {
    return array.slice(0, -count);
  },

  /**
   * Use all of the items in the collection before the specified count
   * inside a block. Opposite of {{withAfter}}
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withBefore: function (array, count, options) {
    array = array.slice(0, -count);
    var result = '';
    for (var item in array) {
      result += options.fn(array[item]);
    }
    return result;
  },

  /**
   * {{first}}
   * Returns the first item in a collection.
   *
   * @param  {Array}  array
   * @param  {[type]} count
   * @return {[type]}
   */
  first: function (array, count) {
    if (Utils.isUndefined(count)) {
      return array[0];
    } else {
      return array.slice(0, count);
    }
  },

  /**
   * {{withFirst}}
   * Use the first item in a collection inside a block.
   *
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withFirst: function (array, count, options) {
    var item, result;
    if (Utils.isUndefined(count)) {
      options = count;
      return options.fn(array[0]);
    } else {
      array = array.slice(0, count);
      result = '';
      for (item in array) {
        result += options.fn(array[item]);
      }
      return result;
    }
  },

  /**
   * Returns the last item in a collection. Opposite of `first`.
   * @param  {Array}  array [description]
   * @param  {[type]} count [description]
   * @return {[type]}       [description]
   */
  last: function (array, count) {
    if (Utils.isUndefined(count)) {
      return array[array.length - 1];
    } else {
      return array.slice(-count);
    }
  },

  /**
   * Use the last item in a collection inside a block.
   * Opposite of {{withFirst}}.
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withLast: function (array, count, options) {
    var item, result;
    if (Utils.isUndefined(count)) {
      options = count;
      return options.fn(array[array.length - 1]);
    } else {
      array = array.slice(-count);
      result = '';
      for (item in array) {
        result += options.fn(array[item]);
      }
      return result;
    }
  },

  /**
   * Joins all elements of a collection into a string
   * using a separator if specified.
   * @param  {Array}  array     [description]
   * @param  {[type]} separator [description]
   * @return {[type]}           [description]
   */
  join: function (array, separator) {
    return array.join(Utils.isUndefined(separator) ? ' ' : separator);
  },

  /**
   * Handlebars "joinAny" block helper that supports
   * arrays of objects or strings. implementation found here:
   * https://github.com/wycats/handlebars.js/issues/133
   *
   * @param  {[type]} items [description]
   * @param  {[type]} block [description]
   * @return {[type]}       [description]
   *
   * If "delimiter" is not speficified, then it defaults to ",".
   * You can use "start", and "end" to do a "slice" of the array.
   * @example:
   *   Use with objects:
   *   {{#join people delimiter=" and "}}{{name}}, {{age}}{{/join}}
   * @example:
   *   Use with arrays:
   *   {{join jobs delimiter=", " start="1" end="2"}}
   *
   */
  joinAny: function (items, block) {
    var delimiter, end, i, len, out, start;
    delimiter = block.hash.delimiter || ",";
    start = start = block.hash.start || 0;
    len = (items ? items.length : 0);
    end = block.hash.end || len;
    out = '';
    if (end > len) {
      end = len;
    }
    if ('function' === typeof block) {
      i = start;
      while (i < end) {
        if (i > start) {
          out += delimiter;
        }
        if ('string' === typeof items[i]) {
          out += items[i];
        } else {
          out += block(items[i]);
        }
        i++;
      }
      return out;
    } else {
      return [].concat(items).slice(start, end).join(delimiter);
    }
  },

  sort: function (array, field) {
    if (Utils.isUndefined(field)) {
      return array.sort();
    } else {
      return array.sort(function (a, b) {
        return a[field] > b[field];
      });
    }
  },

  withSort: function (array, field, options) {
    array = _.cloneDeep(array);
    var getDescendantProp = function (obj, desc) {
      var arr = desc.split('.');
      while (arr.length && (obj = obj[arr.shift()])) {
        continue;
      }
      return obj;
    };
    var result = '', item, i, len;
    if (Utils.isUndefined(field)) {
      options = field;
      array = array.sort();
      for (i = 0, len = array.length; i < len; i++) {
        item = array[i];
        result += options.fn(item);
      }
    } else {
      array = array.sort(function (a, b) {
        var aProp = getDescendantProp(a, field);
        var bProp = getDescendantProp(b, field);
        if (aProp > bProp) {
          return 1;
        } else {
          if (aProp < bProp) {
            return -1;
          }
        }
        return 0;
      });
      for (item in array) {
        result += options.fn(array[item]);
      }
    }
    return result;
  },

  length: function (array) {
    return array.length;
  },

  lengthEqual: function (array, length, options) {
    if (array.length === length) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  empty: function (array, options) {
    if (array.length <= 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * {{inArray}}
   *
   * @param  {Array}  array   [description]
   * @param  {[type]} value   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  inArray: function (array, value, options) {
    var _indexOf = require('../utils/lib/indexOf');
    if (_indexOf.call(array, value) >= 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * {{iterate}}
   *
   * Similar to {{#each}} helper, but treats array-like objects
   * as arrays (e.g. objects with a `.length` property that
   * is a number) rather than objects. This lets us iterate
   * over our collections items.
   *
   * @param  {[type]} context [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  iterate: function (context, options) {
    var fn = options.fn;
    var inverse = options.inverse;
    var i = 0;
    var ret = "";
    var data = void 0;
    if (options.data) {
      data = Handlebars.createFrame(options.data);
    }
    if (context && typeof context === 'object') {
      if (typeof context.length === 'number') {
        var j = context.length;
        while (i < j) {
          if (data) {data.index = i;}
          ret = ret + fn(context[i], {data: data});
          i++;
        }
      } else {
        for (var key in context) {
          if (context.hasOwnProperty(key)) {
            if (data) {data.key = key;}
            ret = ret + fn(context[key], {data: data});
            i++;
          }
        }
      }
    }
    if (i === 0) {ret = inverse(this);}
    return ret;
  },

  /**
   * {{forEach}}
   * Credit: http://bit.ly/14HLaDR
   *
   * @param  {[type]}   array [description]
   * @param  {Function} fn    [description]
   * @return {[type]}         [description]
   *
   * @example:
   *   var accounts = [
   *     {'name': 'John', 'email': 'john@example.com'},
   *     {'name': 'Malcolm', 'email': 'malcolm@example.com'},
   *     {'name': 'David', 'email': 'david@example.com'}
   *   ];
   *
   *   {{#forEach accounts}}
   *     <a href="mailto:{{ email }}" title="Send an email to {{ name }}">
   *       {{ name }}
   *     </a>{{#unless isLast}}, {{/unless}}
   *   {{/forEach}}
   */
  forEach: function (array, fn) {
    var buffer, i, item, j, total;
    total = array.length;
    buffer = "";
    // Better performance: http://jsperf.com/for-vs-forEach/2
    i = 0;
    j = total;
    while (i < j) {
      // stick an index property onto the item, starting
      // with 1, may make configurable later
      item = array[i];
      item['index'] = i + 1;
      item['_total'] = total;
      item['isFirst'] = i === 0;
      item['isLast'] = i === (total - 1);
      // show the inside of the block
      buffer += fn.fn(item);
      i++;
    }
    // return the finished buffer
    return buffer;
  },

  /**
   * {{each}}
   * @param  {Object} context
   * @param  {Object} options
   */
  each: function(context, options) {
    var fn = options.fn, inverse = options.inverse;
    var i = 0, ret = '', data;
    var current;

    if (options.data) {
      data = Handlebars.createFrame(options.data);
    }

    if (context && typeof context === 'object') {
      if (context instanceof Array) {
        for (var j = context.length; i<j; i++) {
          if (data) { data.index = i; }
          current = context.at ? context.at(i) : context[i];
          ret = ret + fn(current, { data: data });
        }
      } else {
        for (var key in context) {
          if (context.hasOwnProperty(key)) {
            if (data) { data.key = key; }
            ret = ret + fn(context[key], {data: data});
            i++;
          }
        }
      }
    }

    if (i === 0) {
      ret = inverse(this);
    }

    return ret;
  },

  /**
   * {{eachProperty}}
   * Handlebars block helper to enumerate
   * the properties in an object
   *
   * @param  {[type]} context [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  eachProperty: function (context, options) {
    var content = (function () {
      var results = [];
      for (var key in context) {
        var value = context[key];
        results.push(options.fn({
          key: key,
          value: value
        }));
      }
      return results;
    })();
    return content.join('');
  },

  /**
   * {{eachIndex}}
   *
   * @param  {Array}  array   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   * @example:
   *   {{#eachIndex collection}}
   *     {{item}} is {{index}}
   *   {{/eachIndex}}
   */
  eachIndex: function (array, options) {
    var i;
    var len;
    var result = '';
    var index;
    for (index = i = 0, len = array.length; i < len; index = ++i) {
      var value = array[index];
      result += options.fn({
        item: value,
        index: index
      });
    }
    return result;
  },

  /**
   * {{eachIndexPlusOne}}
   *
   * @param  {Array}  array   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   * @example:
   *   {{#eachIndexPlusOne collection}}
   *     {{item}} is {{index}}
   *   {{/eachIndexPlusOne}}
   */
  eachIndexPlusOne: function (array, options) {
    var result = '';
    var len;
    var i;
    var index;
    for (index = i = 0, len = array.length; i < len; index = ++i) {
      var value = array[index];
      result += options.fn({
        item: value,
        index: index + 1
      });
    }
    return result;
  }

};

module.exports.register = function (Handlebars, options) {
  options = options || {};

  for (var helper in helpers) {
    Handlebars.registerHelper(helper, helpers[helper]);
  }
  return this;
};
<commit_msg>add `dir` "desc" option hash to `withSort` helper<commit_after>/**
 * Handlebars Collections Helpers
 * Copyright (c) 2013 Jon Schlinkert, Brian Woodward, contributors
 * Licensed under the MIT License (MIT).
 */
'use strict';

// node_modules
var Handlebars = require('../helpers/helpers').Handlebars;
var _          = require('lodash');

// Local utils
var Utils      = require('../utils/utils');

// The module to be exported
var helpers = {

  /**
   * {{any}}
   * @param  {Array}  array
   * @param  {Object} options
   */
  any: function (array, options) {
    if (array.length > 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * Returns all of the items in the collection after the specified count.
   * @param  {Array}  array Collection
   * @param  {Number} count Number of items to exclude
   * @return {Array}        Array excluding the number of items specified
   */
  after: function (array, count) {
    return array.slice(count);
  },

  /**
   * Use all of the items in the collection after the specified count
   * inside a block.
   * @param  {Array}  array
   * @param  {Number} count
   * @param  {Ojbect} options
   * @return {Array}
   */
  withAfter: function (array, count, options) {
    array = array.slice(count);
    var result = '';
    for (var item in array) {
      result += options.fn(array[item]);
    }
    return result;
  },

  /**
   * {{arrayify}}
   * Converts a string such as "foo, bar, baz" to an ES Array of strings.
   * @credit: http://bit.ly/1840DsB
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  arrayify: function (str) {
    return str.split(",").map(function (tag) {
      return "\"" + tag + "\"";
    });
  },

  /**
   * Returns all of the items in the collection before the specified
   * count. Opposite of {{after}}.
   * @param  {Array}  array [description]
   * @param  {[type]} count [description]
   * @return {[type]}       [description]
   */
  before: function (array, count) {
    return array.slice(0, -count);
  },

  /**
   * Use all of the items in the collection before the specified count
   * inside a block. Opposite of {{withAfter}}
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withBefore: function (array, count, options) {
    array = array.slice(0, -count);
    var result = '';
    for (var item in array) {
      result += options.fn(array[item]);
    }
    return result;
  },

  /**
   * {{first}}
   * Returns the first item in a collection.
   *
   * @param  {Array}  array
   * @param  {[type]} count
   * @return {[type]}
   */
  first: function (array, count) {
    if (Utils.isUndefined(count)) {
      return array[0];
    } else {
      return array.slice(0, count);
    }
  },

  /**
   * {{withFirst}}
   * Use the first item in a collection inside a block.
   *
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withFirst: function (array, count, options) {
    var item, result;
    if (Utils.isUndefined(count)) {
      options = count;
      return options.fn(array[0]);
    } else {
      array = array.slice(0, count);
      result = '';
      for (item in array) {
        result += options.fn(array[item]);
      }
      return result;
    }
  },

  /**
   * Returns the last item in a collection. Opposite of `first`.
   * @param  {Array}  array [description]
   * @param  {[type]} count [description]
   * @return {[type]}       [description]
   */
  last: function (array, count) {
    if (Utils.isUndefined(count)) {
      return array[array.length - 1];
    } else {
      return array.slice(-count);
    }
  },

  /**
   * Use the last item in a collection inside a block.
   * Opposite of {{withFirst}}.
   * @param  {Array}  array   [description]
   * @param  {[type]} count   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  withLast: function (array, count, options) {
    var item, result;
    if (Utils.isUndefined(count)) {
      options = count;
      return options.fn(array[array.length - 1]);
    } else {
      array = array.slice(-count);
      result = '';
      for (item in array) {
        result += options.fn(array[item]);
      }
      return result;
    }
  },

  /**
   * Joins all elements of a collection into a string
   * using a separator if specified.
   * @param  {Array}  array     [description]
   * @param  {[type]} separator [description]
   * @return {[type]}           [description]
   */
  join: function (array, separator) {
    return array.join(Utils.isUndefined(separator) ? ' ' : separator);
  },

  /**
   * Handlebars "joinAny" block helper that supports
   * arrays of objects or strings. implementation found here:
   * https://github.com/wycats/handlebars.js/issues/133
   *
   * @param  {[type]} items [description]
   * @param  {[type]} block [description]
   * @return {[type]}       [description]
   *
   * If "delimiter" is not speficified, then it defaults to ",".
   * You can use "start", and "end" to do a "slice" of the array.
   * @example:
   *   Use with objects:
   *   {{#join people delimiter=" and "}}{{name}}, {{age}}{{/join}}
   * @example:
   *   Use with arrays:
   *   {{join jobs delimiter=", " start="1" end="2"}}
   *
   */
  joinAny: function (items, block) {
    var delimiter, end, i, len, out, start;
    delimiter = block.hash.delimiter || ",";
    start = start = block.hash.start || 0;
    len = (items ? items.length : 0);
    end = block.hash.end || len;
    out = '';
    if (end > len) {
      end = len;
    }
    if ('function' === typeof block) {
      i = start;
      while (i < end) {
        if (i > start) {
          out += delimiter;
        }
        if ('string' === typeof items[i]) {
          out += items[i];
        } else {
          out += block(items[i]);
        }
        i++;
      }
      return out;
    } else {
      return [].concat(items).slice(start, end).join(delimiter);
    }
  },

  sort: function (array, field) {
    if (Utils.isUndefined(field)) {
      return array.sort();
    } else {
      return array.sort(function (a, b) {
        return a[field] > b[field];
      });
    }
  },

  withSort: function (array, field, options) {
    array = _.cloneDeep(array);
    var getDescendantProp = function (obj, desc) {
      var arr = desc.split('.');
      while (arr.length && (obj = obj[arr.shift()])) {
        continue;
      }
      return obj;
    };
    var result = '', item, i, len;
    if (Utils.isUndefined(field)) {
      options = field;
      array = array.sort();
      if (options.hash && options.hash.dir === 'desc') {
        array = array.reverse();
      }
      for (i = 0, len = array.length; i < len; i++) {
        item = array[i];
        result += options.fn(item);
      }
    } else {
      array = array.sort(function (a, b) {
        var aProp = getDescendantProp(a, field);
        var bProp = getDescendantProp(b, field);
        if (aProp > bProp) {
          return 1;
        } else {
          if (aProp < bProp) {
            return -1;
          }
        }
        return 0;
      });
      if (options.hash && options.hash.dir === 'desc') {
        array = array.reverse();
      }
      for (item in array) {
        result += options.fn(array[item]);
      }
    }
    return result;
  },

  length: function (array) {
    return array.length;
  },

  lengthEqual: function (array, length, options) {
    if (array.length === length) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  empty: function (array, options) {
    if (array.length <= 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * {{inArray}}
   *
   * @param  {Array}  array   [description]
   * @param  {[type]} value   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  inArray: function (array, value, options) {
    var _indexOf = require('../utils/lib/indexOf');
    if (_indexOf.call(array, value) >= 0) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }
  },

  /**
   * {{iterate}}
   *
   * Similar to {{#each}} helper, but treats array-like objects
   * as arrays (e.g. objects with a `.length` property that
   * is a number) rather than objects. This lets us iterate
   * over our collections items.
   *
   * @param  {[type]} context [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  iterate: function (context, options) {
    var fn = options.fn;
    var inverse = options.inverse;
    var i = 0;
    var ret = "";
    var data = void 0;
    if (options.data) {
      data = Handlebars.createFrame(options.data);
    }
    if (context && typeof context === 'object') {
      if (typeof context.length === 'number') {
        var j = context.length;
        while (i < j) {
          if (data) {data.index = i;}
          ret = ret + fn(context[i], {data: data});
          i++;
        }
      } else {
        for (var key in context) {
          if (context.hasOwnProperty(key)) {
            if (data) {data.key = key;}
            ret = ret + fn(context[key], {data: data});
            i++;
          }
        }
      }
    }
    if (i === 0) {ret = inverse(this);}
    return ret;
  },

  /**
   * {{forEach}}
   * Credit: http://bit.ly/14HLaDR
   *
   * @param  {[type]}   array [description]
   * @param  {Function} fn    [description]
   * @return {[type]}         [description]
   *
   * @example:
   *   var accounts = [
   *     {'name': 'John', 'email': 'john@example.com'},
   *     {'name': 'Malcolm', 'email': 'malcolm@example.com'},
   *     {'name': 'David', 'email': 'david@example.com'}
   *   ];
   *
   *   {{#forEach accounts}}
   *     <a href="mailto:{{ email }}" title="Send an email to {{ name }}">
   *       {{ name }}
   *     </a>{{#unless isLast}}, {{/unless}}
   *   {{/forEach}}
   */
  forEach: function (array, fn) {
    var buffer, i, item, j, total;
    total = array.length;
    buffer = "";
    // Better performance: http://jsperf.com/for-vs-forEach/2
    i = 0;
    j = total;
    while (i < j) {
      // stick an index property onto the item, starting
      // with 1, may make configurable later
      item = array[i];
      item['index'] = i + 1;
      item['_total'] = total;
      item['isFirst'] = i === 0;
      item['isLast'] = i === (total - 1);
      // show the inside of the block
      buffer += fn.fn(item);
      i++;
    }
    // return the finished buffer
    return buffer;
  },

  /**
   * {{each}}
   * @param  {Object} context
   * @param  {Object} options
   */
  each: function(context, options) {
    var fn = options.fn, inverse = options.inverse;
    var i = 0, ret = '', data;
    var current;

    if (options.data) {
      data = Handlebars.createFrame(options.data);
    }

    if (context && typeof context === 'object') {
      if (context instanceof Array) {
        for (var j = context.length; i<j; i++) {
          if (data) { data.index = i; }
          current = context.at ? context.at(i) : context[i];
          ret = ret + fn(current, { data: data });
        }
      } else {
        for (var key in context) {
          if (context.hasOwnProperty(key)) {
            if (data) { data.key = key; }
            ret = ret + fn(context[key], {data: data});
            i++;
          }
        }
      }
    }

    if (i === 0) {
      ret = inverse(this);
    }

    return ret;
  },

  /**
   * {{eachProperty}}
   * Handlebars block helper to enumerate
   * the properties in an object
   *
   * @param  {[type]} context [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   */
  eachProperty: function (context, options) {
    var content = (function () {
      var results = [];
      for (var key in context) {
        var value = context[key];
        results.push(options.fn({
          key: key,
          value: value
        }));
      }
      return results;
    })();
    return content.join('');
  },

  /**
   * {{eachIndex}}
   *
   * @param  {Array}  array   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   * @example:
   *   {{#eachIndex collection}}
   *     {{item}} is {{index}}
   *   {{/eachIndex}}
   */
  eachIndex: function (array, options) {
    var i;
    var len;
    var result = '';
    var index;
    for (index = i = 0, len = array.length; i < len; index = ++i) {
      var value = array[index];
      result += options.fn({
        item: value,
        index: index
      });
    }
    return result;
  },

  /**
   * {{eachIndexPlusOne}}
   *
   * @param  {Array}  array   [description]
   * @param  {Object} options [description]
   * @return {[type]}         [description]
   * @example:
   *   {{#eachIndexPlusOne collection}}
   *     {{item}} is {{index}}
   *   {{/eachIndexPlusOne}}
   */
  eachIndexPlusOne: function (array, options) {
    var result = '';
    var len;
    var i;
    var index;
    for (index = i = 0, len = array.length; i < len; index = ++i) {
      var value = array[index];
      result += options.fn({
        item: value,
        index: index + 1
      });
    }
    return result;
  }

};

module.exports.register = function (Handlebars, options) {
  options = options || {};

  for (var helper in helpers) {
    Handlebars.registerHelper(helper, helpers[helper]);
  }
  return this;
};
<|endoftext|>


<commit_before>var $, io, skipfirst;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Spawn,
            jqconsole,
            ShortCuts = {};

        if (!(this instanceof ConsoleProto))
            return new ConsoleProto();

        function Console(element, prefix, socketPath, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string';

            if (!callback) {
                  if (!socketPath) {
                      callback      = prefix;
                      prefix        = '/console';
                      socketPath    = '';
                  } else {
                      callback      = socketPath;
                      socketPath    = '';
                  }
            }

            if (isString)
                el  = document.querySelector(element);
            else
                el  = element;

            load(prefix, function() {
                jqconsole   = $(el).jqconsole('', '> ');

                Spawn = SpawnProto(jqconsole, prefix, socketPath);
                addShortCuts(jqconsole);
                addKeyWhenNoPrompt(jqconsole);
                addOnMouseUp(jqconsole);

                if (typeof callback === 'function')
                    callback(Spawn);
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = '';

            if (jqconsole.GetState() !== 'output')
                jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            if (jqconsole)
                jqconsole.Focus();
        };

        function addOnMouseUp(jqconsole) {
            var console     = jqconsole.$console;

            console.mouseup(function() {
                var top,
                    isSelection = '' + window.getSelection();

                if (!isSelection) {
                    top        = console.scrollTop();

                    Console.focus();
                    console.scrollTop(top);
                }
            });
        }

        function load(prefix, callback) {
            var scripts = [];

            if (!window.load)
                scripts.push('/modules/load/load.js');

            if (!window.join)
                scripts.push('/join/join.js');

            if (!scripts.length)
                after();
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), after);

            function after() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/console.css',
                        '/css/ansi.css',
                    ]);

                load.json(prefix + '/modules.json', function(error, remote) {
                    var reg,
                        name    = 'jQuery';

                    if (error) {
                        console.error(error);
                    } else {
                        /* do not load jquery if it is loaded */
                        if (window[name]) {
                            reg     = RegExp(name, 'i');
                            remote  = remote.filter(function(item) {
                                return !reg.test(item);
                            });
                        }

                        load.series(remote.concat(css), callback);
                    }
                });
            }
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function SpawnProto(jqconsole, room, socketPath) {
            if (!(this instanceof SpawnProto))
                return new SpawnProto(jqconsole, room, socketPath);

            var Buffer          = {
                    'log-msg': '',
                    'error-msg': ''
                },
                self            = this,
                socket,
                prompt          = function() {
                    var is = isPrompt();

                    if (!is)
                        jqconsole.Prompt(true, self.handler);
                };

            init(room);

            function init(room) {
                var cwd             = '',
                    commands        = [],
                    promptText      = [],
                    href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS,
                    path: socketPath + '/socket.io'
                });

                socket.on('err', function(data) {
                    error(data);
                });

                socket.on('data', function(data) {
                    log(data);
                });

                socket.on('prompt', function() {
                    forceWrite();
                    prompt();

                    if (promptText.length)
                        Console.setPromptText(promptText.pop());
                });

                socket.on('path', function(path) {
                    if (commands.length)
                        exec(commands.pop());
                    else
                        setPromptLabel(path + '> ');

                    cwd = path;
                });

                socket.on('connect', function() {
                    log('console: connected\n');
                });

                socket.on('disconnect', function() {
                    error('console: disconnected\n');

                    commands.push('cd ' + cwd);
                    promptText.push(Console.getPromptText());

                    abortPrompt();
                });

                function exec(cmd) {
                    cmd && socket.emit('command', cmd);
                }
            }

            this.on     = function() {
                socket.on.apply(socket, arguments);
                return self;
            };

            this.emit   = function() {
                socket.emit.apply(socket, arguments);
                return self;
            };

            this.handler = function getHandler(command) {
                if (command)
                    socket.emit('command', command);
                else
                    jqconsole.Prompt(true, self.handler);
            };

            this.kill       = function() {
                socket.emit('kill');
            };

            this.write      = function(data) {
                socket.emit('write', data);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }

            function write(data, className) {
                var isContain;

                if (data) {
                    Buffer[className]   += data;
                    isContain           = ~Buffer[className].indexOf('\n');

                    if (isContain) {
                        jqconsole.Write(Buffer[className], className);
                        Buffer[className] = '';
                    }
                }
            }

            function forceWrite() {
                Object.keys(Buffer).forEach(function(name) {
                    if (Buffer[name]) {
                        jqconsole.Write(Buffer[name], name);
                        Buffer[name] = '';
                    }
                });
            }

            function log(data) {
                write(data, 'log-msg');
            }

            function error(data) {
                write(data, 'error-msg');
            }
        }

        function abortPrompt() {
             var is = isPrompt();

             if (is)
                jqconsole.AbortPrompt();
        }

        function setPromptLabel(prompt) {
            jqconsole.SetPromptLabel(prompt);
            jqconsole.UpdatePromptLabel();
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function addKeyWhenNoPrompt(jqconsole) {
            var skip = skipfirst(readNoPrompt);

            jqconsole
                .$input_source[0]
                .addEventListener('keydown', function(event) {
                    var ENTER   = 13,
                        is      = isPrompt();

                    if (is)
                        skip.clear();
                    else
                        skip(event.keyCode === ENTER, event);
                });
        }

        function readNoPrompt(event) {
            var char,
                KEY_C   = 67,
                isCtrl  = event.ctrlKey || event.metaKey,
                isC     = event.keyCode === KEY_C;

            if (isC && isCtrl) {
                Spawn.kill();
            } else {
                char = fromCharCode(event);

                if (char) {
                    Spawn.write(char);
                    jqconsole.Write(char);
                }
            }
        }

        function fromCharCode(event) {
            var code, hex,
                char        = '',
                identifier  = event.keyIdentifier,
                shift       = event.shiftKey;

            if (identifier === 'Enter') {
                char = '\n';
            } else {
                code        = identifier.substring(2);
                hex         = parseInt(code, 16);

                if (!isNaN(hex))
                    char        = String.fromCharCode(hex);

                if (!shift && /[a-z]/i.test(char))
                    char = char.toLowerCase();
            }

            return char;
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, Spawn.handler);
        }

        return Console;
    }

})(this);
<commit_msg>fix(console) use event.key instead of deprecated event.keyIdentifier<commit_after>var $, io, skipfirst;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Spawn,
            jqconsole,
            ShortCuts = {};

        if (!(this instanceof ConsoleProto))
            return new ConsoleProto();

        function Console(element, prefix, socketPath, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string';

            if (!callback) {
                  if (!socketPath) {
                      callback      = prefix;
                      prefix        = '/console';
                      socketPath    = '';
                  } else {
                      callback      = socketPath;
                      socketPath    = '';
                  }
            }

            if (isString)
                el  = document.querySelector(element);
            else
                el  = element;

            load(prefix, function() {
                jqconsole   = $(el).jqconsole('', '> ');

                Spawn = SpawnProto(jqconsole, prefix, socketPath);
                addShortCuts(jqconsole);
                addKeyWhenNoPrompt(jqconsole);
                addOnMouseUp(jqconsole);

                if (typeof callback === 'function')
                    callback(Spawn);
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = '';

            if (jqconsole.GetState() !== 'output')
                jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            if (jqconsole)
                jqconsole.Focus();
        };

        function addOnMouseUp(jqconsole) {
            var console     = jqconsole.$console;

            console.mouseup(function() {
                var top,
                    isSelection = '' + window.getSelection();

                if (!isSelection) {
                    top        = console.scrollTop();

                    Console.focus();
                    console.scrollTop(top);
                }
            });
        }

        function load(prefix, callback) {
            var scripts = [];

            if (!window.load)
                scripts.push('/modules/load/load.js');

            if (!window.join)
                scripts.push('/join/join.js');

            if (!scripts.length)
                after();
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), after);

            function after() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/console.css',
                        '/css/ansi.css',
                    ]);

                load.json(prefix + '/modules.json', function(error, remote) {
                    var reg,
                        name    = 'jQuery';

                    if (error) {
                        console.error(error);
                    } else {
                        /* do not load jquery if it is loaded */
                        if (window[name]) {
                            reg     = RegExp(name, 'i');
                            remote  = remote.filter(function(item) {
                                return !reg.test(item);
                            });
                        }

                        load.series(remote.concat(css), callback);
                    }
                });
            }
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function SpawnProto(jqconsole, room, socketPath) {
            if (!(this instanceof SpawnProto))
                return new SpawnProto(jqconsole, room, socketPath);

            var Buffer          = {
                    'log-msg': '',
                    'error-msg': ''
                },
                self            = this,
                socket,
                prompt          = function() {
                    var is = isPrompt();

                    if (!is)
                        jqconsole.Prompt(true, self.handler);
                };

            init(room);

            function init(room) {
                var cwd             = '',
                    commands        = [],
                    promptText      = [],
                    href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS,
                    path: socketPath + '/socket.io'
                });

                socket.on('err', function(data) {
                    error(data);
                });

                socket.on('data', function(data) {
                    log(data);
                });

                socket.on('prompt', function() {
                    forceWrite();
                    prompt();

                    if (promptText.length)
                        Console.setPromptText(promptText.pop());
                });

                socket.on('path', function(path) {
                    if (commands.length)
                        exec(commands.pop());
                    else
                        setPromptLabel(path + '> ');

                    cwd = path;
                });

                socket.on('connect', function() {
                    log('console: connected\n');
                });

                socket.on('disconnect', function() {
                    error('console: disconnected\n');

                    commands.push('cd ' + cwd);
                    promptText.push(Console.getPromptText());

                    abortPrompt();
                });

                function exec(cmd) {
                    cmd && socket.emit('command', cmd);
                }
            }

            this.on     = function() {
                socket.on.apply(socket, arguments);
                return self;
            };

            this.emit   = function() {
                socket.emit.apply(socket, arguments);
                return self;
            };

            this.handler = function getHandler(command) {
                if (command)
                    socket.emit('command', command);
                else
                    jqconsole.Prompt(true, self.handler);
            };

            this.kill       = function() {
                socket.emit('kill');
            };

            this.write      = function(data) {
                socket.emit('write', data);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }

            function write(data, className) {
                var isContain;

                if (data) {
                    Buffer[className]   += data;
                    isContain           = ~Buffer[className].indexOf('\n');

                    if (isContain) {
                        jqconsole.Write(Buffer[className], className);
                        Buffer[className] = '';
                    }
                }
            }

            function forceWrite() {
                Object.keys(Buffer).forEach(function(name) {
                    if (Buffer[name]) {
                        jqconsole.Write(Buffer[name], name);
                        Buffer[name] = '';
                    }
                });
            }

            function log(data) {
                write(data, 'log-msg');
            }

            function error(data) {
                write(data, 'error-msg');
            }
        }

        function abortPrompt() {
             var is = isPrompt();

             if (is)
                jqconsole.AbortPrompt();
        }

        function setPromptLabel(prompt) {
            jqconsole.SetPromptLabel(prompt);
            jqconsole.UpdatePromptLabel();
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function addKeyWhenNoPrompt(jqconsole) {
            var skip = skipfirst(readNoPrompt);

            jqconsole
                .$input_source[0]
                .addEventListener('keydown', function(event) {
                    var ENTER   = 13,
                        is      = isPrompt();

                    if (is)
                        skip.clear();
                    else
                        skip(event.keyCode === ENTER, event);
                });
        }

        function readNoPrompt(event) {
            var char,
                KEY_C   = 67,
                isCtrl  = event.ctrlKey || event.metaKey,
                isC     = event.keyCode === KEY_C;

            if (isC && isCtrl) {
                Spawn.kill();
            } else {
                char = fromCharCode(event);

                if (char) {
                    Spawn.write(char);
                    jqconsole.Write(char);
                }
            }
        }

        function fromCharCode(event) {
            var code, hex,
                char        = '',
                identifier  = event.key,
                shift       = event.shiftKey;

            if (!identifier) {
                identifier = event.keyIdentifier;
            }

            if (identifier === 'Enter') {
                char = '\n';
            } else if (!event.key) {
                code = identifier.substring(2);
                hex  = parseInt(code, 16);

                if (!isNaN(hex))
                    char        = String.fromCharCode(hex);

                if (!shift && /[a-z]/i.test(char))
                    char = char.toLowerCase();
            }

            return char;
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, Spawn.handler);
        }

        return Console;
    }

})(this);
<|endoftext|>


<commit_before>(function() {
    var withoutAsync = {};

    ["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) {
        withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction];
        return jasmine.Env.prototype[jasmineFunction] = function() {
            var args = Array.prototype.slice.call(arguments, 0);
            var timeout = null;
            if (isLastArgumentATimeout(args)) {
                timeout = args.pop();
                // The changes to the jasmine test runner causes undef to be passed when
                // calling all it()'s now. If the last argument isn't a timeout and the
                // last argument IS undefined, let's just pop it off. Since out of bounds
                // items are undefined anyways, *hopefully* removing an undef item won't
                // hurt.
            } else if (args[args.length-1] == undefined) {
                args.pop();
            }
            if (isLastArgumentAnAsyncSpecFunction(args))
            {
                var specFunction = args.pop();
                args.push(function() {
                    return asyncSpec(specFunction, this, timeout);
                });
            }
            return withoutAsync[jasmineFunction].apply(this, args);
        };
    });

    function isLastArgumentATimeout(args)
    {
        return args.length > 0 && (typeof args[args.length-1]) === "number";
    }

    function isLastArgumentAnAsyncSpecFunction(args)
    {
        return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0;
    }

    function asyncSpec(specFunction, spec, timeout) {
        if (timeout == null) timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL || 1000;
        var done = false;
        spec.runs(function() {
            try {
                return specFunction.call(spec, function(error) {
                    done = true;
                    if (error != null) return spec.fail(error);
                });
            } catch (e) {
                done = true;
                throw e;
            }
        });
        return spec.waitsFor(function() {
            if (done === true) {
                return true;
            }
        }, "spec to complete", timeout);
    };

}).call(this);
<commit_msg>Changed the default timeout behavior for the async callbacks to use getEnv().defaultTimeoutInterval instead of DEFAULT_TIMEOUT_INTERVAL.  This simplifies configuration changes for the timeout by only requiring a change to the env timout and not both.<commit_after>(function() {
    var withoutAsync = {};

    ["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) {
        withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction];
        return jasmine.Env.prototype[jasmineFunction] = function() {
            var args = Array.prototype.slice.call(arguments, 0);
            var timeout = null;
            if (isLastArgumentATimeout(args)) {
                timeout = args.pop();
                // The changes to the jasmine test runner causes undef to be passed when
                // calling all it()'s now. If the last argument isn't a timeout and the
                // last argument IS undefined, let's just pop it off. Since out of bounds
                // items are undefined anyways, *hopefully* removing an undef item won't
                // hurt.
            } else if (args[args.length-1] == undefined) {
                args.pop();
            }
            if (isLastArgumentAnAsyncSpecFunction(args))
            {
                var specFunction = args.pop();
                args.push(function() {
                    return asyncSpec(specFunction, this, timeout);
                });
            }
            return withoutAsync[jasmineFunction].apply(this, args);
        };
    });

    function isLastArgumentATimeout(args)
    {
        return args.length > 0 && (typeof args[args.length-1]) === "number";
    }

    function isLastArgumentAnAsyncSpecFunction(args)
    {
        return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0;
    }

    function asyncSpec(specFunction, spec, timeout) {
        if (timeout == null) timeout = jasmine.getEnv().defaultTimeoutInterval || 1000;
        var done = false;
        spec.runs(function() {
            try {
                return specFunction.call(spec, function(error) {
                    done = true;
                    if (error != null) return spec.fail(error);
                });
            } catch (e) {
                done = true;
                throw e;
            }
        });
        return spec.waitsFor(function() {
            if (done === true) {
                return true;
            }
        }, "spec to complete", timeout);
    };

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


<commit_before>'use latest';

const P = require('bluebird@2.9.26');
const request = require('superagent-bluebird-promise@1.2.0');
const twilio = require('twilio@2.2.1');

const twilioClient = new twilio.RestClient(ctx.data.TWILIO_ACCOUNT_SID, ctx.data.TWILIO_AUTH_TOKEN);

module.exports = function(ctx, cb) {
  const reqs = meetsReqs();
  if (!reqs.success) {
    return cb(new Error(reqs.message));
  }

  request.get(ctx.data.URL)
    .catch(() => {
      notifyUser();
    }).finally(() => {
      cb();
    });
}

function meetsReqs() {
  const keys = ['TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN', 'TWILIO_PHONE_NUMBER', 'MY_PHONE_NUMBER', 'URL'];
  for (let key of keys) {
    if (!ctx.data[key]) {
      return {
        success: false,
        message: `${key} secret is missing!`
      };
    }
  }
}

function notifyUser() {
  return twilioClient.sms.messages.post({
    to: MY_PHONE_NUMBER,
    from: ctx.data.TWILIO_PHONE_NUMBER,
    body: 'Site down!'
  });
}
<commit_msg>make ctx data available in fn<commit_after>'use latest';

const P = require('bluebird@2.9.26');
const request = require('superagent@1.2.0');
const twilio = require('twilio@2.2.1');

module.exports = function(ctx, cb) {
  const twilioClient = new twilio.RestClient(ctx.data.TWILIO_ACCOUNT_SID, ctx.data.TWILIO_AUTH_TOKEN);
  const reqs = meetsReqs();

  if (!reqs.success) {
    return cb(new Error(reqs.message));
  }

  request.get(ctx.data.URL)
    .end((err, res) => {
      if (err) {
        notifyUser();
      }
      cb();
    })

  function meetsReqs() {
    const keys = ['TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN', 'TWILIO_PHONE_NUMBER', 'MY_PHONE_NUMBER', 'URL'];
    for (let key of keys) {
      if (!ctx.data[key]) {
        return {
          success: false,
          message: `${key} secret is missing!`
        };
      }
    }
  }

  function notifyUser() {
    return twilioClient.sms.messages.post({
      to: MY_PHONE_NUMBER,
      from: ctx.data.TWILIO_PHONE_NUMBER,
      body: 'Site down!'
    });
  }
}
<|endoftext|>


<commit_before>/* exported kalamata */
var bodyParser = require('body-parser');
var app, options;

var kalamata = module.exports = function(_app_, _options_) {
    app = _app_;
    options = _options_;

    app.use(bodyParser.json());

    if(!options) options = {};
    if(!options.apiRoot) options.apiRoot = '';
    options.apiRoot = '/' + options.apiRoot.replace(/^\/|\/$/g, '') + '/';

    return kalamata;
};

kalamata.expose = function(model, _opts_) {

    var validOpts = {
        identifier: true,
        endpointName: true
    };

    var hooks = {
        before: hookArrays(),
        after: hookArrays()
    };

    var opts = {};
    for(var p in _opts_) {
        if(validOpts[p]) {
            opts[p] = _opts_[p];
        } else {
            throw new Error(
                'Invalid option for endpoint ' +
                opts.endpointName + ': ' + p
            );
        }
    }
    if(!opts.identifier) opts.identifier = 'id';
    if(!opts.endpointName) opts.endpointName = model.forge().tableName;

    var chainer = {};
    addHookChainers('before', chainer);
    addHookChainers('after', chainer);

    configureEndpoints();

    function configureEndpoints() {
        app.get(options.apiRoot + opts.endpointName, function(req, res) {
            var err = runHooks(hooks.before.getCollection, [req, res], res);
            if(err) return;
            new model().fetchAll().then(function(collection) {
                var err = runHooks(
                            hooks.after.getCollection,
                            [collection, req, res],
                            res
                        );
                if(err) return;
                res.send(collection);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.get(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.get,
                        [req.params.identifier, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error getting ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                var err = runHooks(hooks.after.get, [m, req, res], res);
                if(err) return;
                res.send(m);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.post(options.apiRoot + opts.endpointName, function(req, res) {
            var err = runHooks(hooks.before.create, [req.body, req, res], res);
            if(err) return;
            new model(req.body).save().then(function(m) {
                var err = runHooks(hooks.after.create, [m, req, res], res);
                if(err) return;
                var resData = {};
                resData[opts.identifier] = m.get(opts.identifier);
                res.send(resData);
            }).catch(function(error) {
                console.log(error.stack);
                res.send(
                    'Error saving ' + opts.endpointName + ' ' +
                    JSON.stringify(req.body)
                );
            });
        });

        app.put(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.update,
                        [req.params.identifier, req.body, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error updating ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                return m.save(req.body, {patch: true});
            })
            .then(function(m) {
                var err = runHooks(hooks.after.update, [m, req, res], res);
                if(err) return;
                res.send(true);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.delete(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.del,
                        [req.params.identifier, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error deleting ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                return m.destroy();
            })
            .then(function() {
                var err = runHooks(hooks.after.del, [req, res], res);
                if(err) return;
                res.send(true);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });
    }

    function runHooks(fnArray, params, res) {
        try {
            for(var i in fnArray) {
                fnArray[i].apply(null, params);
            }
        } catch(err) {
            console.log(err.stack);
            res.send('Error getting ' + opts.endpointName);
            return true;
        }
    }

    function hookArrays() {
        return {
            get: [],
            getCollection: [],
            create: [],
            update: [],
            del: []
        };
    }

    function addHookChainers(prefix, chainerObj) {
        chainerObj[prefix] = hookFn(prefix);
        chainerObj[prefix + 'Get'] = hookFn(prefix, 'get');
        chainerObj[prefix + 'GetCollection'] = hookFn(prefix, 'getCollection');
        chainerObj[prefix + 'Create'] = hookFn(prefix, 'create');
        chainerObj[prefix + 'Update'] = hookFn(prefix, 'update');
        chainerObj[prefix + 'Delete'] = hookFn(prefix, 'del');
    }

    function hookFn(prefix, type) {
        if(type) {
            return function(fn) {
                hooks[prefix][type].push(fn);
                return chainer;
            };
        } else {
            return function(fn) {
                for(var i in hooks[prefix]) {
                    hooks[prefix][i].push(fn);
                }
                return chainer;
            };
        }
    }

    return chainer;

};<commit_msg>Removed the jshint exported comment<commit_after>var bodyParser = require('body-parser');
var app, options;

var kalamata = module.exports = function(_app_, _options_) {
    app = _app_;
    options = _options_;

    app.use(bodyParser.json());

    if(!options) options = {};
    if(!options.apiRoot) options.apiRoot = '';
    options.apiRoot = '/' + options.apiRoot.replace(/^\/|\/$/g, '') + '/';

    return kalamata;
};

kalamata.expose = function(model, _opts_) {

    var validOpts = {
        identifier: true,
        endpointName: true
    };

    var hooks = {
        before: hookArrays(),
        after: hookArrays()
    };

    var opts = {};
    for(var p in _opts_) {
        if(validOpts[p]) {
            opts[p] = _opts_[p];
        } else {
            throw new Error(
                'Invalid option for endpoint ' +
                opts.endpointName + ': ' + p
            );
        }
    }
    if(!opts.identifier) opts.identifier = 'id';
    if(!opts.endpointName) opts.endpointName = model.forge().tableName;

    var chainer = {};
    addHookChainers('before', chainer);
    addHookChainers('after', chainer);

    configureEndpoints();

    function configureEndpoints() {
        app.get(options.apiRoot + opts.endpointName, function(req, res) {
            var err = runHooks(hooks.before.getCollection, [req, res], res);
            if(err) return;
            new model().fetchAll().then(function(collection) {
                var err = runHooks(
                            hooks.after.getCollection,
                            [collection, req, res],
                            res
                        );
                if(err) return;
                res.send(collection);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.get(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.get,
                        [req.params.identifier, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error getting ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                var err = runHooks(hooks.after.get, [m, req, res], res);
                if(err) return;
                res.send(m);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.post(options.apiRoot + opts.endpointName, function(req, res) {
            var err = runHooks(hooks.before.create, [req.body, req, res], res);
            if(err) return;
            new model(req.body).save().then(function(m) {
                var err = runHooks(hooks.after.create, [m, req, res], res);
                if(err) return;
                var resData = {};
                resData[opts.identifier] = m.get(opts.identifier);
                res.send(resData);
            }).catch(function(error) {
                console.log(error.stack);
                res.send(
                    'Error saving ' + opts.endpointName + ' ' +
                    JSON.stringify(req.body)
                );
            });
        });

        app.put(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.update,
                        [req.params.identifier, req.body, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error updating ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                return m.save(req.body, {patch: true});
            })
            .then(function(m) {
                var err = runHooks(hooks.after.update, [m, req, res], res);
                if(err) return;
                res.send(true);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });

        app.delete(options.apiRoot + opts.endpointName + '/:identifier',
        function(req, res) {
            var err = runHooks(
                        hooks.before.del,
                        [req.params.identifier, req, res],
                        res
                    );
            if(err) return;
            var modelAttrs = {};
            modelAttrs[opts.identifier] = req.params.identifier;
            new model(modelAttrs).fetch().then(function(m) {
                if(!m) {
                    throw new Error(
                        'Error deleting ' + opts.endpointName + '. ' +
                        options.identifier + ' = ' + req.params.identifier +
                        ' not found'
                    );
                }
                return m.destroy();
            })
            .then(function() {
                var err = runHooks(hooks.after.del, [req, res], res);
                if(err) return;
                res.send(true);
            }).catch(function(error) {
                console.log(error.stack);
                res.send('Error getting ' + opts.endpointName);
            });
        });
    }

    function runHooks(fnArray, params, res) {
        try {
            for(var i in fnArray) {
                fnArray[i].apply(null, params);
            }
        } catch(err) {
            console.log(err.stack);
            res.send('Error getting ' + opts.endpointName);
            return true;
        }
    }

    function hookArrays() {
        return {
            get: [],
            getCollection: [],
            create: [],
            update: [],
            del: []
        };
    }

    function addHookChainers(prefix, chainerObj) {
        chainerObj[prefix] = hookFn(prefix);
        chainerObj[prefix + 'Get'] = hookFn(prefix, 'get');
        chainerObj[prefix + 'GetCollection'] = hookFn(prefix, 'getCollection');
        chainerObj[prefix + 'Create'] = hookFn(prefix, 'create');
        chainerObj[prefix + 'Update'] = hookFn(prefix, 'update');
        chainerObj[prefix + 'Delete'] = hookFn(prefix, 'del');
    }

    function hookFn(prefix, type) {
        if(type) {
            return function(fn) {
                hooks[prefix][type].push(fn);
                return chainer;
            };
        } else {
            return function(fn) {
                for(var i in hooks[prefix]) {
                    hooks[prefix][i].push(fn);
                }
                return chainer;
            };
        }
    }

    return chainer;

};<|endoftext|>


<commit_before><commit_msg>added globals file for all script tasks<commit_after><|endoftext|>


<commit_before>/**
 * Created by axetroy on 17-2-15.
 */
const path = require('path');
const process = require('process');
const _ = require('lodash');
const prettyjson = require('prettyjson');
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs-extra'));
const log4js = require('log4js');
const gitUrlParse = require("git-url-parse");
const inquirer = require('inquirer');
const co = require('co');

const prompt = inquirer.createPromptModule();
const logger = log4js.getLogger('IMPORT');
const config = require('../config');
const {isGitRepoDir, parseGitConfigAsync, isExistPath, isLink} = require('../utils');

function *importHandlerOneDir(targetPath, options) {
  targetPath = path.resolve(process.cwd(), targetPath);
  const isGitDir = yield isGitRepoDir(targetPath);

  if (!isGitDir) {
    return yield Promise.reject(`Invalid path: ${targetPath.green}, please make sure that is a git repository`);
  }

  let gitConfig = yield parseGitConfigAsync({
    cwd: targetPath,
    path: path.join('.git', 'config')
  });

  const origin = gitConfig[`remote "origin"`] || {};

  if (!origin.url) {
    logger.error(`Invalid repository, please make sure that you have set remote repository url`);
    return yield Promise.reject();
  }

  const gitInfo = gitUrlParse(origin.url);

  const configJSON = yield fs.readJsonAsync(config.paths.config);

  const distPath = path.join(process.env.HOME, configJSON.base, gitInfo.source, gitInfo.owner, gitInfo.name);

  if (yield isExistPath(distPath)) {
    let isConfirmReplace = {result: false};
    if (options.force) {
      isConfirmReplace = {result: true};
    } else {
      isConfirmReplace = yield prompt({
        type: 'confirm',
        name: 'result',
        message: `repository has exist, Are you sure to replace ${distPath.yellow}`.white,
        "default": false,
      });
    }

    if (!isConfirmReplace.result) {
      logger.info('ok! Good bye.');
      return Promise.reject();
    } else {
      // if it's a link, then unlink first
      if (yield isLink(distPath)) yield fs.unlinkAsync(distPath);
      yield fs.removeAsync(distPath);
    }
  }

  let action = '';

  if (options.hard) {
    action = 'move';
    yield fs.moveAsync(targetPath, distPath);
  } else {
    action = 'link';
    // make sure his parent dir has exist
    yield fs.ensureDirAsync(path.resolve(distPath, '../'));
    // specify dir only for window, other platform will ignore
    yield fs.symlinkAsync(targetPath, distPath, 'dir');
  }

  const lockJSON = yield fs.readJsonAsync(config.paths.lock);
  lockJSON.repos = lockJSON.repos || [];

  lockJSON.repos.push(_.extend({}, gitInfo, {path: distPath}));

  yield fs.writeJsonAsync(config.paths.lock, lockJSON);

  logger.info(`${targetPath.green} has been ${action} to ${distPath.yellow}`);

  return yield Promise.resolve();
}

function *importHandler(targetPath, options) {
  if (options.all) {
    const files = yield fs.readdirAsync(targetPath);
    while (files.length) {
      let file = files.shift();
      yield co(function *() {
        return yield importHandlerOneDir(path.join(targetPath, file), options);
      }).catch(function (err) {
        if (err) {
          console.error(err);
        }
        return Promise.resolve();
      });
    }
  } else {
    yield importHandlerOneDir(targetPath, options);
  }
}

module.exports = function (path, options) {
  return importHandler(path, options);
};<commit_msg>refactor: refactor error output<commit_after>/**
 * Created by axetroy on 17-2-15.
 */
const path = require('path');
const process = require('process');
const _ = require('lodash');
const prettyjson = require('prettyjson');
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs-extra'));
const log4js = require('log4js');
const gitUrlParse = require("git-url-parse");
const inquirer = require('inquirer');
const co = require('co');

const prompt = inquirer.createPromptModule();
const logger = log4js.getLogger('IMPORT');
const config = require('../config');
const {isGitRepoDir, parseGitConfigAsync, isExistPath, isLink} = require('../utils');

function *importHandlerOneDir(targetPath, options) {
  targetPath = path.resolve(process.cwd(), targetPath);
  const isGitDir = yield isGitRepoDir(targetPath);

  if (!isGitDir) throw new Error(`Invalid path: ${targetPath.green}, please make sure that is a git repository`);

  let gitConfig = yield parseGitConfigAsync({
    cwd: targetPath,
    path: path.join('.git', 'config')
  });

  const origin = gitConfig[`remote "origin"`] || {};

  if (!origin.url) throw new Error(`Invalid repository, please make sure that you have set remote repository url`);

  const gitInfo = gitUrlParse(origin.url);

  const configJSON = yield fs.readJsonAsync(config.paths.config);

  const distPath = path.join(process.env.HOME, configJSON.base, gitInfo.source, gitInfo.owner, gitInfo.name);

  if (yield isExistPath(distPath)) {
    let isConfirmReplace = {result: false};
    if (options.force) {
      isConfirmReplace = {result: true};
    } else {
      isConfirmReplace = yield prompt({
        type: 'confirm',
        name: 'result',
        message: `repository has exist, Are you sure to replace ${distPath.yellow}`.white,
        "default": false,
      });
    }

    if (!isConfirmReplace.result) {
      logger.info('ok! Good bye.');
      return Promise.reject();
    } else {
      // if it's a link, then unlink first
      if (yield isLink(distPath)) yield fs.unlinkAsync(distPath);
      yield fs.removeAsync(distPath);
    }
  }

  let action = '';

  if (options.hard) {
    action = 'move';
    yield fs.moveAsync(targetPath, distPath);
  } else {
    action = 'link';
    // make sure his parent dir has exist
    yield fs.ensureDirAsync(path.resolve(distPath, '../'));
    // specify dir only for window, other platform will ignore
    yield fs.symlinkAsync(targetPath, distPath, 'dir');
  }

  const lockJSON = yield fs.readJsonAsync(config.paths.lock);
  lockJSON.repos = lockJSON.repos || [];

  lockJSON.repos.push(_.extend({}, gitInfo, {path: distPath}));

  yield fs.writeJsonAsync(config.paths.lock, lockJSON);

  logger.info(`${targetPath.green} has been ${action} to ${distPath.yellow}`);

  return yield Promise.resolve();
}

function *importHandler(targetPath, options) {
  if (options.all) {
    const files = yield fs.readdirAsync(targetPath);
    while (files.length) {
      let file = files.shift();
      yield co(function *() {
        return yield importHandlerOneDir(path.join(targetPath, file), options);
      }).catch(function (err) {
        if (err) {
          console.error(err);
        }
        return Promise.resolve();
      });
    }
  } else {
    yield importHandlerOneDir(targetPath, options);
  }
}

module.exports = function (path, options) {
  return importHandler(path, options);
};<|endoftext|>


<commit_before>'use strict';

var Command         = require('./-command');

var CdvBuildTask    = require('../tasks/cordova-build');
var HookTask        = require('../tasks/run-hook');
var BuildTask       = require('../tasks/ember-build');
var chalk           = require('chalk');

var ValidateLocationType    = require('../tasks/validate/location-type');
var ValidatePlatformTask    = require('../tasks/validate/platform');
var ValidateAllowNavigation = require('../tasks/validate/allow-navigation');

module.exports = Command.extend({
  name: 'cordova:build',
  aliases: ['cdv:build', 'cdv:b'],
  description: 'Build the ember application for cordova',
  works: 'insideProject',

  /* eslint-disable max-len */
  availableOptions: [
    { name: 'platform',                        type: String,  default: 'ios' },
    { name: 'verbose',                         type: Boolean, default: false,                       aliases: ['v'] },
    { name: 'environment',                     type: String,  default: 'development',               aliases: ['e', 'env', { 'dev': 'development' }, { 'prod': 'production' }] },
    { name: 'cordova-output-path',             type: 'Path',  default: 'ember-cordova/cordova/www', aliases: ['op', 'out'] },
    { name: 'release',                         type: Boolean, default: false },
    { name: 'device',                          type: Boolean, default: false },
    { name: 'emulator',                        type: Boolean, default: false },
    { name: 'buildConfig',                     type: 'Path' },

    // iOS Signing Options
    { name: 'codeSignIdentity',                type: String },
    { name: 'provisioningProfile',             type: String },
    { name: 'codesignResourceRules',           type: String },

    // android Signing Options
    { name: 'keystore',                        type: String },
    { name: 'storePassword',                   type: String },
    { name: 'alias',                           type: String },
    { name: 'password',                        type: String },
    { name: 'keystoreType',                    type: String },

    //Gradle
    { name: 'gradleArg',                       type: Boolean, default: false },
    { name: 'cdvBuildMultipleApks',            type: String },
    { name: 'cdvVersionCode',                  type: String },
    { name: 'cdvReleaseSigningPropertiesFile', type: String },
    { name: 'cdvDebugSigningPropertiesFile',   type: String },
    { name: 'cdvMinSdkVersion',                type: String },
    { name: 'cdvBuildToolsVersion',            type: String },
    { name: 'cdvCompileSdkVersion',            type: String }
  ],
  /* eslint-enable max-len */

  run: function(options) {
    this._super.apply(this, arguments);

    var ui = this.ui;
    var project = this.project;
    var platform = options.platform;
    var isRelease = options.release ? true : false;
    var isEmulator = options.emulator ? true : false;
    var buildConfig = options.buildConfig;

    //Vars for live reload addon service
    this.project.targetIsCordova = true;
    this.project.CORDOVA_PLATFORM = platform;

    var validateLocationType = new ValidateLocationType({
      project: this.project,
      ui: this.ui
    });

    var validateAllowNavigation = new ValidateAllowNavigation({
      project: this.project,
      ui: this.ui
    });

    var validatePlatform = new ValidatePlatformTask({
      project: this.project,
      ui: this.ui,
      platform: platform
    });

    var hook = new HookTask({
      project: project,
      ui: ui
    });

    var emberBuild = new BuildTask({
      ui: ui,
      project: project,
      environment: options.environment,
      outputPath: options.cordovaOutputPath
    });

    var cordovaBuild = new CdvBuildTask({
      project: project,
      ui: ui,
      platform: platform,
      isRelease: isRelease,
      isEmulator: isEmulator,
      buildConfig: buildConfig,
      platformOpts: parsePlatformOpts(platform, options)
    });

    ui.writeLine(chalk.green('Building'));

    return validateLocationType.run(this.project.config())
      .then(validateAllowNavigation.prepare(false))
      .then(validatePlatform.prepare())
      .then(hook.prepare('beforeBuild'))
      .then(emberBuild.prepare())
      .then(cordovaBuild.prepare(platform, options.release))
      .then(hook.prepare('afterBuild'))
      .then(function() {
        ui.writeLine(chalk.green('Cordova Project Built.'));
      })
      .catch(function(e) {
        throw e;
      });
  }
});
<commit_msg>refactor(build): emulator and build refactor<commit_after>'use strict';

var Command         = require('./-command');

var CdvBuildTask    = require('../tasks/cordova-build');
var HookTask        = require('../tasks/run-hook');
var BuildTask       = require('../tasks/ember-build');
var chalk           = require('chalk');

var ValidateLocationType    = require('../tasks/validate/location-type');
var ValidatePlatformTask    = require('../tasks/validate/platform');
var ValidateAllowNavigation = require('../tasks/validate/allow-navigation');

module.exports = Command.extend({
  name: 'cordova:build',
  aliases: ['cdv:build', 'cdv:b'],
  description: 'Build the ember application for cordova',
  works: 'insideProject',

  /* eslint-disable max-len */
  availableOptions: [
    { name: 'platform',                        type: String,  default: 'ios' },
    { name: 'verbose',                         type: Boolean, default: false,                       aliases: ['v'] },
    { name: 'environment',                     type: String,  default: 'development',               aliases: ['e', 'env', { 'dev': 'development' }, { 'prod': 'production' }] },
    { name: 'cordova-output-path',             type: 'Path',  default: 'ember-cordova/cordova/www', aliases: ['op', 'out'] },
    { name: 'release',                         type: Boolean, default: false },
    { name: 'device',                          type: Boolean, default: false },
    { name: 'emulator',                        type: Boolean, default: true },
    { name: 'buildConfig',                     type: 'Path' },

    // iOS Signing Options
    { name: 'codeSignIdentity',                type: String },
    { name: 'provisioningProfile',             type: String },
    { name: 'codesignResourceRules',           type: String },

    // android Signing Options
    { name: 'keystore',                        type: String },
    { name: 'storePassword',                   type: String },
    { name: 'alias',                           type: String },
    { name: 'password',                        type: String },
    { name: 'keystoreType',                    type: String },

    //Gradle
    { name: 'gradleArg',                       type: Boolean, default: false },
    { name: 'cdvBuildMultipleApks',            type: String },
    { name: 'cdvVersionCode',                  type: String },
    { name: 'cdvReleaseSigningPropertiesFile', type: String },
    { name: 'cdvDebugSigningPropertiesFile',   type: String },
    { name: 'cdvMinSdkVersion',                type: String },
    { name: 'cdvBuildToolsVersion',            type: String },
    { name: 'cdvCompileSdkVersion',            type: String }
  ],
  /* eslint-enable max-len */

  run: function(options) {
    this._super.apply(this, arguments);

    var ui = this.ui;
    var project = this.project;
    var platform = options.platform;
    var isRelease = options.release !== false ? true : false;
    var isEmulator = options.device !== false ? true : false;

    //Vars for live reload addon service
    this.project.targetIsCordova = true;
    this.project.CORDOVA_PLATFORM = platform;

    var validateLocationType = new ValidateLocationType({
      project: this.project,
      ui: this.ui
    });

    var validateAllowNavigation = new ValidateAllowNavigation({
      project: this.project,
      ui: this.ui
    });

    var validatePlatform = new ValidatePlatformTask({
      project: this.project,
      ui: this.ui,
      platform: platform
    });

    var hook = new HookTask({
      project: project,
      ui: ui
    });

    var emberBuild = new BuildTask({
      ui: ui,
      project: project,
      environment: options.environment,
      outputPath: options.cordovaOutputPath
    });

    var cordovaBuild = new CdvBuildTask({
      project: project,
      ui: ui,
      platform: platform,
      isRelease: isRelease,
      isEmulator: isEmulator,
      buildConfig: options.buildConfig,
      platformOpts: parsePlatformOpts(platform, options)
    });

    ui.writeLine(chalk.green('Building'));

    return validateLocationType.run(this.project.config())
      .then(validateAllowNavigation.prepare(false))
      .then(validatePlatform.prepare())
      .then(hook.prepare('beforeBuild'))
      .then(emberBuild.prepare())
      .then(cordovaBuild.prepare(platform, options.release))
      .then(hook.prepare('afterBuild'))
      .then(function() {
        ui.writeLine(chalk.green('Cordova Project Built.'));
      })
      .catch(function(e) {
        throw e;
      });
  }
});
<|endoftext|>