<commit_before>Package.describe({
  name: "meteoric124:meteoric",
  summary: "Ionic components for Meteor. No Angular!",
  version: "1.2.4-beta.11",
  git: "https://github.com/JoeyAndres/meteor-ionic.git",
  documentation: "../README.md"
});

Cordova.depends({
  'ionic-plugin-keyboard': '1.0.9'
});

Package.onUse(function(api) {
  api.versionsFrom("1.3.2.4");

  api.use([
    'ecmascript'
  ]);

  api.use([
    "jandres:template-extension@4.0.7-alpha3",

    "meteoric124:template-scope@0.1.0-beta.7",
    "meteoric124:template-attribute-directive@0.1.0-beta.5",
    "meteoric124:template-assert-parent@0.1.0-alpha.1",

    "templating",
    "underscore",
    "reactive-var",
    "iron:router@1.0.0",
    "tracker",
    "session",
    "promise",
    "fourseven:scss@3.4.3",
    'jquery',

    "jandres:meteoric-sass@1.2.5"
  ], "client");

  api.addFiles([
    "styles/main.scss"
  ], "client");

  api.addFiles([
    "lib/meteoric.js",
    "lib/delegate.js",
    "lib/utility.js",
    "lib/polyfill.js",
    "lib/platform.js",

    // Utils
    'lib/utils/delegateService.js',
    'lib/utils/dom.js',
    'lib/utils/events.js',
    'lib/utils/gestures.js',
    'lib/utils/platform.js',
    'lib/utils/poly.js',
    'lib/utils/tap.js',
    'lib/utils/activator.js',
    'lib/utils/utils.js',
    'lib/utils/keyboard.js',
    'lib/utils/viewport.js',

    // Views
    'lib/views/view.js',
    'lib/views/scrollView.js',
    'lib/views/scrollViewNative.js',
    'lib/views/listView.js',
    'lib/views/modalView.js',
    'lib/views/sideMenuView.js',
    'lib/views/sliderView.js',
    'lib/views/slidesView.js',
    'lib/views/toggleView.js',

    // Controller.
    'lib/controller/headerBarController.js',
    'lib/controller/infiniteScrollController.js',
    'lib/controller/navBarController.js',
    'lib/controller/navViewController.js',
    'lib/controller/listController.js',
    'lib/controller/sideMenuController.js',
    'lib/controller/spinnerController.js',
    'lib/controller/scrollController.js',
    'lib/controller/viewController.js',
    'lib/controller/tabController.js',
    'lib/controller/tabsController.js',

    // Service.
    'lib/service/body.js',
    'lib/service/clickBlock.js',
    'lib/service/gesture.js',
    'lib/service/ionicConfig.js',
    'lib/service/platform.js',
    'lib/service/viewSwitcher.js',
    'lib/service/history.js',
    'lib/service/navBarDelegate.js',
    'lib/service/navViewDelegate.js',
    'lib/service/scrollDelegate.js',
    'lib/service/sideMenuDelegate.js',
    'lib/service/tabsDelegate.js',

    'lib/meteoric-config.js',

    "components/ionActionSheet/ionActionSheet.html",
    "components/ionActionSheet/ionActionSheet.js",

    "components/ionBackdrop/ionBackdrop.html",
    "components/ionBackdrop/ionBackdrop.js",

    "components/ionCheckbox/ionCheckbox.html",
    "components/ionCheckbox/ionCheckbox.js",

    "components/ionContent/ionContent.html",
    "components/ionContent/ionContent.js",

    "components/ionDeleteButton/ionDeleteButton.html",
    "components/ionDeleteButton/ionDeleteButton.js",

    "components/ionHeaderFooterBar/ionHeaderFooterBar.js",

    "components/ionFooterBar/ionFooterBar.html",
    "components/ionFooterBar/ionFooterBar.js",

    "components/ionHeaderBar/ionHeaderBar.html",
    "components/ionHeaderBar/ionHeaderBar.js",

    "components/ionInfiniteScroll/ionInfiniteScroll.html",
    "components/ionInfiniteScroll/ionInfiniteScroll.js",

    "components/ionItem/ionItem.html",
    "components/ionItem/ionItem.js",

    "components/ionList/ionList.html",
    "components/ionList/ionList.js",

    "components/ionLoading/ionLoading.html",
    "components/ionLoading/ionLoading.js",

    "components/ionModal/ionModal.html",
    "components/ionModal/ionModal.js",

    "components/ionNavBar/ionNavBar.html",
    "components/ionNavBar/ionNavBar.js",

    "components/ionOptionButton/ionOptionButton.html",
    "components/ionOptionButton/ionOptionButton.js",

    "components/ionNavBackButton/ionNavBackButton.html",
    "components/ionNavBackButton/ionNavBackButton.js",

    "components/ionNavButtons/ionNavButtons.html",
    "components/ionNavButtons/ionNavButtons.js",

    "components/ionNavTitle/ionNavTitle.html",
    "components/ionNavTitle/ionNavTitle.js",

    "components/exposeAsideWhen/exposeAsideWhen.js",
    "components/ionGesture/ionGesture.js",
    "components/menuClose/menuClose.js",
    "components/menuToggle/menuToggle.js",
    "components/navDirection/navDirection.js",
    "components/navTransition/navTransition.js",
    "components/ionKeyboard/keyboardAttach.js",

    "components/ionNavView/ionNavView.html",
    "components/ionNavView/ionNavView.js",

    "components/ionPane/ionPane.html",
    "components/ionPane/ionPane.js",

    "components/ionPopover/ionPopover.html",
    "components/ionPopover/ionPopover.js",

    "components/ionPopup/ionPopup.html",
    "components/ionPopup/ionPopup.js",

    "components/ionRadio/ionRadio.html",
    "components/ionRadio/ionRadio.js",

    "components/ionReorderButton/ionReorderButton.html",
    "components/ionReorderButton/ionReorderButton.js",

    "components/ionScroll/ionScroll.html",
    "components/ionScroll/ionScroll.js",

    "components/ionSideMenu/ionSideMenu.html",
    "components/ionSideMenu/ionSideMenu.js",

    "components/ionSideMenuContent/ionSideMenuContent.html",
    "components/ionSideMenuContent/ionSideMenuContent.js",

    "components/ionSideMenus/ionSideMenus.html",
    "components/ionSideMenus/ionSideMenus.js",

    "components/ionSpinner/ionSpinner.html",
    "components/ionSpinner/ionSpinner.js",

    "components/ionSlides/ionSlides.html",
    "components/ionSlides/ionSlides.js",

    "components/ionTabs/ionTabs.html",
    "components/ionTabs/ionTabs.js",

    "components/ionTabNav/ionTabNav.html",
    "components/ionTabNav/ionTabNav.js",

    "components/ionTab/ionTab.html",
    "components/ionTab/ionTab.js",

    "components/ionToggle/ionToggle.html",
    "components/ionToggle/ionToggle.js",

    "components/ionView/ionView.html",
    "components/ionView/ionView.js"

  ], "client");

  api.export([
    // Services.
    '$ionicGesture',
    '$ionicActionSheet',
    '$ionicBackdrop',
    '$ionicLoading',
    '$ionModal',
    '$ionicPlatform',
    '$ionicHistory',
    '$ionicPopup',
    '$ionicPopover',

    // ionic Delegates.
    '$ionicScrollDelegate',
    '$ionicSideMenuDelegate',
    '$ionicTabsDelegate'

    // Misc.
    // todo: export $ionicGoBack
    // todo: make 'meteoric' namespace not implicitly global. (i.e. window.meteoric).
  ], 'client');
});<commit_msg>Upgraded meteoric124:meteoric-sass@1.2.4-beta.12<commit_after>Package.describe({
  name: "meteoric124:meteoric",
  summary: "Ionic components for Meteor. No Angular!",
  version: "1.2.4-beta.12",
  git: "https://github.com/JoeyAndres/meteor-ionic.git",
  documentation: "../README.md"
});

Cordova.depends({
  'ionic-plugin-keyboard': '1.0.9'
});

Package.onUse(function(api) {
  api.versionsFrom("1.3.2.4");

  api.use([
    'ecmascript'
  ]);

  api.use([
    "jandres:template-extension@4.0.7-alpha3",

    "meteoric124:template-scope@0.1.0-beta.7",
    "meteoric124:template-attribute-directive@0.1.0-beta.5",
    "meteoric124:template-assert-parent@0.1.0-alpha.1",

    "templating",
    "underscore",
    "reactive-var",
    "iron:router@1.0.0",
    "tracker",
    "session",
    "promise",
    'jquery',

    "fourseven:scss@3.3.3_2",
    "meteoric124:meteoric-sass@1.2.7"
  ], "client");

  api.addFiles([
    "styles/main.scss"
  ], "client");

  api.addFiles([
    "lib/meteoric.js",
    "lib/delegate.js",
    "lib/utility.js",
    "lib/polyfill.js",
    "lib/platform.js",

    // Utils
    'lib/utils/delegateService.js',
    'lib/utils/dom.js',
    'lib/utils/events.js',
    'lib/utils/gestures.js',
    'lib/utils/platform.js',
    'lib/utils/poly.js',
    'lib/utils/tap.js',
    'lib/utils/activator.js',
    'lib/utils/utils.js',
    'lib/utils/keyboard.js',
    'lib/utils/viewport.js',

    // Views
    'lib/views/view.js',
    'lib/views/scrollView.js',
    'lib/views/scrollViewNative.js',
    'lib/views/listView.js',
    'lib/views/modalView.js',
    'lib/views/sideMenuView.js',
    'lib/views/sliderView.js',
    'lib/views/slidesView.js',
    'lib/views/toggleView.js',

    // Controller.
    'lib/controller/headerBarController.js',
    'lib/controller/infiniteScrollController.js',
    'lib/controller/navBarController.js',
    'lib/controller/navViewController.js',
    'lib/controller/listController.js',
    'lib/controller/sideMenuController.js',
    'lib/controller/spinnerController.js',
    'lib/controller/scrollController.js',
    'lib/controller/viewController.js',
    'lib/controller/tabController.js',
    'lib/controller/tabsController.js',

    // Service.
    'lib/service/body.js',
    'lib/service/clickBlock.js',
    'lib/service/gesture.js',
    'lib/service/ionicConfig.js',
    'lib/service/platform.js',
    'lib/service/viewSwitcher.js',
    'lib/service/history.js',
    'lib/service/navBarDelegate.js',
    'lib/service/navViewDelegate.js',
    'lib/service/scrollDelegate.js',
    'lib/service/sideMenuDelegate.js',
    'lib/service/tabsDelegate.js',

    'lib/meteoric-config.js',

    "components/ionActionSheet/ionActionSheet.html",
    "components/ionActionSheet/ionActionSheet.js",

    "components/ionBackdrop/ionBackdrop.html",
    "components/ionBackdrop/ionBackdrop.js",

    "components/ionCheckbox/ionCheckbox.html",
    "components/ionCheckbox/ionCheckbox.js",

    "components/ionContent/ionContent.html",
    "components/ionContent/ionContent.js",

    "components/ionDeleteButton/ionDeleteButton.html",
    "components/ionDeleteButton/ionDeleteButton.js",

    "components/ionHeaderFooterBar/ionHeaderFooterBar.js",

    "components/ionFooterBar/ionFooterBar.html",
    "components/ionFooterBar/ionFooterBar.js",

    "components/ionHeaderBar/ionHeaderBar.html",
    "components/ionHeaderBar/ionHeaderBar.js",

    "components/ionInfiniteScroll/ionInfiniteScroll.html",
    "components/ionInfiniteScroll/ionInfiniteScroll.js",

    "components/ionItem/ionItem.html",
    "components/ionItem/ionItem.js",

    "components/ionList/ionList.html",
    "components/ionList/ionList.js",

    "components/ionLoading/ionLoading.html",
    "components/ionLoading/ionLoading.js",

    "components/ionModal/ionModal.html",
    "components/ionModal/ionModal.js",

    "components/ionNavBar/ionNavBar.html",
    "components/ionNavBar/ionNavBar.js",

    "components/ionOptionButton/ionOptionButton.html",
    "components/ionOptionButton/ionOptionButton.js",

    "components/ionNavBackButton/ionNavBackButton.html",
    "components/ionNavBackButton/ionNavBackButton.js",

    "components/ionNavButtons/ionNavButtons.html",
    "components/ionNavButtons/ionNavButtons.js",

    "components/ionNavTitle/ionNavTitle.html",
    "components/ionNavTitle/ionNavTitle.js",

    "components/exposeAsideWhen/exposeAsideWhen.js",
    "components/ionGesture/ionGesture.js",
    "components/menuClose/menuClose.js",
    "components/menuToggle/menuToggle.js",
    "components/navDirection/navDirection.js",
    "components/navTransition/navTransition.js",
    "components/ionKeyboard/keyboardAttach.js",

    "components/ionNavView/ionNavView.html",
    "components/ionNavView/ionNavView.js",

    "components/ionPane/ionPane.html",
    "components/ionPane/ionPane.js",

    "components/ionPopover/ionPopover.html",
    "components/ionPopover/ionPopover.js",

    "components/ionPopup/ionPopup.html",
    "components/ionPopup/ionPopup.js",

    "components/ionRadio/ionRadio.html",
    "components/ionRadio/ionRadio.js",

    "components/ionReorderButton/ionReorderButton.html",
    "components/ionReorderButton/ionReorderButton.js",

    "components/ionScroll/ionScroll.html",
    "components/ionScroll/ionScroll.js",

    "components/ionSideMenu/ionSideMenu.html",
    "components/ionSideMenu/ionSideMenu.js",

    "components/ionSideMenuContent/ionSideMenuContent.html",
    "components/ionSideMenuContent/ionSideMenuContent.js",

    "components/ionSideMenus/ionSideMenus.html",
    "components/ionSideMenus/ionSideMenus.js",

    "components/ionSpinner/ionSpinner.html",
    "components/ionSpinner/ionSpinner.js",

    "components/ionSlides/ionSlides.html",
    "components/ionSlides/ionSlides.js",

    "components/ionTabs/ionTabs.html",
    "components/ionTabs/ionTabs.js",

    "components/ionTabNav/ionTabNav.html",
    "components/ionTabNav/ionTabNav.js",

    "components/ionTab/ionTab.html",
    "components/ionTab/ionTab.js",

    "components/ionToggle/ionToggle.html",
    "components/ionToggle/ionToggle.js",

    "components/ionView/ionView.html",
    "components/ionView/ionView.js"

  ], "client");

  api.export([
    // Services.
    '$ionicGesture',
    '$ionicActionSheet',
    '$ionicBackdrop',
    '$ionicLoading',
    '$ionModal',
    '$ionicPlatform',
    '$ionicHistory',
    '$ionicPopup',
    '$ionicPopover',

    // ionic Delegates.
    '$ionicScrollDelegate',
    '$ionicSideMenuDelegate',
    '$ionicTabsDelegate'

    // Misc.
    // todo: export $ionicGoBack
    // todo: make 'meteoric' namespace not implicitly global. (i.e. window.meteoric).
  ], 'client');
});<|endoftext|>


<commit_before>Package.describe({
  name: 'idmontie:simple-validation',
  version: '0.0.2',
  // Brief, one-line summary of the package.
  summary: 'Core validation package for checking data, nothing more',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/idmontie/meteor-simple-validation',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: '../README.md'
});

var load = function(api) {
  api.addFiles('core/validate.js');
  api.addFiles('validators/validate-not-empty.js');
  api.addFiles('validators/validate-is-string.js');
  api.addFiles('validators/validate-is-numeric.js');
}

Package.onUse(function(api) {
  api.versionsFrom('1.2.1');
  api.use('ecmascript');

  load(api);

  api.export('Validate');
  api.export('ValidateNotEmpty');
  api.export('ValidateIsString');
  api.export('ValidateIsNumeric');
});

Package.onTest(function(api) {
  api.use('ecmascript');
  api.use('tinytest');

  load(api);

  api.addFiles('tests/validate-tests.js');
  api.addFiles('tests/validate-not-empty-tests.js');
  api.addFiles('tests/validate-is-string-tests.js');
  api.addFiles('tests/validate-is-numeric-tests.js');
});
<commit_msg>Update package.js<commit_after>Package.describe({
  name: 'idmontie:simple-validation',
  version: '0.0.2',
  // Brief, one-line summary of the package.
  summary: 'Core validation package for checking data, nothing more',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/CapsuleCat/SimpleValidationMeteorPackage',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: '../README.md'
});

var load = function(api) {
  api.addFiles('core/validate.js');
  api.addFiles('validators/validate-not-empty.js');
  api.addFiles('validators/validate-is-string.js');
  api.addFiles('validators/validate-is-numeric.js');
}

Package.onUse(function(api) {
  api.versionsFrom('1.2.1');
  api.use('ecmascript');

  load(api);

  api.export('Validate');
  api.export('ValidateNotEmpty');
  api.export('ValidateIsString');
  api.export('ValidateIsNumeric');
});

Package.onTest(function(api) {
  api.use('ecmascript');
  api.use('tinytest');

  load(api);

  api.addFiles('tests/validate-tests.js');
  api.addFiles('tests/validate-not-empty-tests.js');
  api.addFiles('tests/validate-is-string-tests.js');
  api.addFiles('tests/validate-is-numeric-tests.js');
});
<|endoftext|>


<commit_before>'use strict';

/**
 * adonis-commands
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
*/

const BaseGenerator = require('./Base');
const path = require('path');
const inflect = require('i')();
const toFieldType = require('../helpers/str-to-field-type');
const toInputAttributeString = require('../helpers/field-type-to-input-attribute-string');

class ControllerGenerator extends BaseGenerator {

  /**
   * returns signature to be used by ace
   * @return {String}
   *
   * @public
   */
  get signature() {
    const resource = '{-r,--resource?:Create a resourceful Controller}';
    const api = '{-a,--api?:Create a resourceful API Controller}';
    const jsonapi = '{-j,--jsonapi?:Create a resourceful JSON API Controller}';

    return `g:controller {name} ${resource} ${api} ${jsonapi} {fields...}`;
  }

  /**
   * returns description to be used by ace as help command
   * @return {String}
   *
   * @public
   */
  get description() {
    return 'Create a new controller for APIs';
  }

  /**
   * handle method will be executed by ace. Here we
   * create the controller to controller directory.
   * @param  {Object} args
   * @param  {Object} options
   *
   * @public
   */
  * handle(args, options) {
    if (options.api || options.jsonapi) {
      options.resource = true;
    }

    const name = args.name;
    const entity = this._makeEntityName(name, 'controller', true);
    const toPath = path.join(this.helpers.appPath(), 'Http/Controllers', `${entity.entityPath}.js`);

    const templateOptions = {
      methods: ['index', 'store', 'show', 'update', 'destroy'],
      api: options.api || false,
      json: options.jsonapi || false,
      resource: options.resource || false,
      controllerName: entity.entityName,
      modelName: inflect.classify(name),
      singularCamelCase: inflect.camelize(name, false),
      pluralCamelCase: inflect.pluralize(inflect.camelize(name, false)),
    };

    const fields = args['fields...'] || [];
    const fieldTypes = fields.map((field) => toFieldType(field, templateOptions.modelName));
    templateOptions.with = fieldTypes
      .filter((field) => field.type === 'relation')
      .map((x) => `'${x.name}'`)
      .join(', ');

    if (options.api) {
      templateOptions.inputOnly = fieldTypes.map(toInputAttributeString).join(', ');
    }

    if (options.jsonapi) {
      templateOptions.attributes = fieldTypes
        .filter(({ type }) => type !== 'relation')
        .map(({ name: attributeName }) => `'${inflect.dasherize(attributeName)}'`)
        .join(', ');

      templateOptions.relations = fieldTypes
        .filter(({ type }) => type === 'relation');
      templateOptions.belongsTos = templateOptions.relations
        .filter(({ relation: { type } }) => type === 'belongsTo');
      templateOptions.foreignKeys = templateOptions.belongsTos
        .map(({ name, relation: { foreignKey } }) => `${foreignKey}: ${name}`);
    }

    yield this._wrapWrite('controller', toPath, templateOptions);
  }

}

module.exports = ControllerGenerator;
<commit_msg>Fixes eslint error<commit_after>'use strict';

/**
 * adonis-commands
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
*/

const BaseGenerator = require('./Base');
const path = require('path');
const inflect = require('i')();
const toFieldType = require('../helpers/str-to-field-type');
const toInputAttributeString = require('../helpers/field-type-to-input-attribute-string');

class ControllerGenerator extends BaseGenerator {

  /**
   * returns signature to be used by ace
   * @return {String}
   *
   * @public
   */
  get signature() {
    const resource = '{-r,--resource?:Create a resourceful Controller}';
    const api = '{-a,--api?:Create a resourceful API Controller}';
    const jsonapi = '{-j,--jsonapi?:Create a resourceful JSON API Controller}';

    return `g:controller {name} ${resource} ${api} ${jsonapi} {fields...}`;
  }

  /**
   * returns description to be used by ace as help command
   * @return {String}
   *
   * @public
   */
  get description() {
    return 'Create a new controller for APIs';
  }

  /**
   * handle method will be executed by ace. Here we
   * create the controller to controller directory.
   * @param  {Object} args
   * @param  {Object} options
   *
   * @public
   */
  * handle(args, options) {
    if (options.api || options.jsonapi) {
      options.resource = true;
    }

    const name = args.name;
    const entity = this._makeEntityName(name, 'controller', true);
    const toPath = path.join(this.helpers.appPath(), 'Http/Controllers', `${entity.entityPath}.js`);

    const templateOptions = {
      methods: ['index', 'store', 'show', 'update', 'destroy'],
      api: options.api || false,
      json: options.jsonapi || false,
      resource: options.resource || false,
      controllerName: entity.entityName,
      modelName: inflect.classify(name),
      singularCamelCase: inflect.camelize(name, false),
      pluralCamelCase: inflect.pluralize(inflect.camelize(name, false)),
    };

    const fields = args['fields...'] || [];
    const fieldTypes = fields.map((field) => toFieldType(field, templateOptions.modelName));
    templateOptions.with = fieldTypes
      .filter((field) => field.type === 'relation')
      .map((x) => `'${x.name}'`)
      .join(', ');

    if (options.api) {
      templateOptions.inputOnly = fieldTypes.map(toInputAttributeString).join(', ');
    }

    if (options.jsonapi) {
      templateOptions.attributes = fieldTypes
        .filter(({ type }) => type !== 'relation')
        .map(({ name: attributeName }) => `'${inflect.dasherize(attributeName)}'`)
        .join(', ');

      templateOptions.relations = fieldTypes
        .filter(({ type }) => type === 'relation');
      templateOptions.belongsTos = templateOptions.relations
        .filter(({ relation: { type } }) => type === 'belongsTo');
      templateOptions.foreignKeys = templateOptions.belongsTos
        .map(({ name: rName, relation: { foreignKey } }) => `${foreignKey}: ${rName}`);
    }

    yield this._wrapWrite('controller', toPath, templateOptions);
  }

}

module.exports = ControllerGenerator;
<|endoftext|>


<commit_before>/**
 *
 */
function GroupHoldr(settings) {
    "use strict";
    if(!this || this === window) {
        return new GroupHoldr(settings);
    }
    var self = this,

        // Associative array of strings to groups, where groups are each some
        // sort of array (either typical or associative).
        groups,

        // Associative array containing "add", "del", "get", and "set" keys to
        // those appropriate functions (e.x. functions.add.MyGroup is the same
        // as self.addMyGroup).
        functions,

        // Array of string names, each of which is tied to a group.
        group_names,

        // Associative array keying each group to the function it uses: Array
        // for regular arrays, and Object for associative arrays.
        group_types,

        // Associative array keying each group to the string name of the
        // function it uses: "Array" for regular arrays, and "Object" for
        // associative arrays.
        group_type_names;

    /**
     * @constructor
     */
    self.reset = function(settings) {
        // The group_names and group_types arguments must be provided
        if(!settings.hasOwnProperty("group_names")) {
            throw new Error("No group_names array provided to GroupHoldr");
        }
        if(!settings.hasOwnProperty("group_types")) {
            throw new Error("No group_types object provided to GroupHoldr");
        }

        // The functions containers start blank, but are filled in setGroupNames
        functions = {
            "set": {},
            "get": {},
            "add": {},
            "del": {}
        };
        setGroupNames(settings.group_names, settings.group_types);
    };

    /**
     * Meaty function to reset, given an array of names an object of types
     * Any pre-existing functions are cleared, and new ones are added as
     * member objects and to {functions}
     *
     * @param {String[]} names   An array of names of groupings to be made
     * @param {Object} types   An associative array of the function types of the
     *                         names given in names.
     */
    function setGroupNames(names, types) {
        if(!(names instanceof Array)) {
            throw new Error("No array of names given to setGroupNames");
        }

        // If there already were group names, clear them
        if(group_names) {
            clearFunctions();
        }

        // Set the new group_names, as ucFirst and removing duplicates
        group_names = names.map(function(name_old) {
            var name_new = ucFirst(name_old);
            // If the name changed, record the new name in types as well
            types[name_new] = types[name_old];
            return name_new;
        });
        group_names.sort();

        // Reset the group types and type names, to be filled next
        group_types = {}
        group_type_names = {};

        // If group_types is an object, set custom group types for everything
        if(typeof(types) == "object") {
            group_names.forEach(function(name) {
                group_types[name] = getTypeFunction(types[name]);
                group_type_names[name] = getTypeName(types[name]);
            });
        }
        // Otherwise assume everything uses the same one, such as from a string
        else {
            var type_func = getTypeFunction(types),
                type_name = getTypeName(types);
            group_names.forEach(function(name) {
                group_types[name] = type_func;
                group_type_names[name] = type_name;
            });
        }

        // Create the containers, and set the modifying functions
        setGroups();
        setFunctions();
    }

    /**
     * Removes any pre-existing "set", "get", etc. functions
     */
    function clearFunctions() {
        group_names.forEach(function(name) {
            // Delete member variable functions
            delete self["set" + name + "Group"];
            delete self["get" + name + "Group"];
            delete self["add" + name];
            delete self["del" + name];

            // Delete functions under .functions by making each typeit a new {}
            functions.set = {};
            functions.get = {};
            functions.add = {};
            functions.del = {};
        });
    }

    /**
     * Resets groups to an empty object, and fills it with a new group_type for
     * each name in group_names
     */
    function setGroups() {
        groups = {};
        group_names.forEach(function(name) {
            groups[name] = new group_types[name]();
        });
    }

    /**
     * Calls the function setters for each name in group_names
     * @remarks Those are: createFunction<XYZ>: "Set", "Get", "Add", "Del"
     */
    function setFunctions() {
        group_names.forEach(function(name) {
            createFunctionSet(name);
            createFunctionGet(name);
            createFunctionAdd(name);
            createFunctionDel(name);
        });
    }

    /* Function generators
    */

    /**
     * Creates the set<type> function under self and functions.set
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionSet(name) {
        /**
         * Set function for a group of type <container> and name <name>
         * holding objects of type <type>
         *
         * @alias {setName}
         * @param {<container>} value   A new <container> for the group
         * @return {self}   The containing GroupHoldr
         */
        functions.set[name] = self["set" + name + "Group"] = function(value) {
            if(value.constructor != group_types[name]) {
                throw new Error(name + " must be of type "
                        + group_type_names[name]);
            }
            groups[name] = value;
            return self;
        };
    }

    /**
     * Creates the get<type> function under self and functions.get
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionGet(name) {
        /**
         * Get function for a group of type <container> and name <name>
         * holding objects of type <type>
         * Returns the container group itself or an object within, based on key
         *
         * @alias {setName}
         * @param {<type>} key   The key of an item to be retrieved from the
         *                       group. If not given, the group is returned
         * @return {self}   The containing GroupHoldr
         */
        functions.get[name] = self["get" + name + "Group"] = function(key) {
            var group = groups[name];
            if(arguments.length < 1) {
                return group;
            }
            return group[key];
        };
    }

    /**
     * Creates the add<type> function under self and functions.add
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionAdd(name) {
        var group = groups[name];
        if(group_types[name] == Object) {
            /**
             * Get function for a group of type Object and name <name>
             * holding objects of type <type>
             * Adds the value object to the group under key
             *
             * @alias {setName}
             * @param {<type>} key   The key of an item to be retrieved from the
             *                       group. If not given, the group is returned
             * @param value   The value, of any object type, to be added
             * @return {self}   The containing GroupHoldr
             */
            functions.add[name] = self["add" + name] = function(key, value) {
                group[key] = value;
                return self;
            };
        }
        else {
            /**
             * Get function for a group of type Array and name <name>
             * holding objects of type <type>
             * Pushes the value object to the group
             *
             * @alias {setName}
             * @param value   The value, of any object type, to be added
             * @return {self}   The containing GroupHoldr
             */
            functions.add[name] = self["add" + name] = function(value) {
                group.push(value);
                return self;
            };
        }
    }

    /**
     * Creates the del<type> (delete) function under self and functions.del
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionDel(name) {
        var group = groups[name];
        if(group_types[name] == Object) {
            /**
             * Delete function for a group of type Object and name <name>
             * holding objects of type <type>
             * Removes the object in the group keyed by the key
             *
             * @alias {setName}
             * @param {<type>} key   The item key to be deleted from the group
             * @return {self}   The containing GroupHoldr
             */
            functions.del[name] = self["del" + name] = function(key) {
                delete group[key];
                return self;
            };
        }
        else {
            /**
             * Delete function for a group of type Array and name <name>
             * holding objects of type <type>
             * Removes the keyed object from the group
             *
             * @alias {setName}
             * @param {<type>} key   The item key to be deleted from the group
             * @return {self}   The containing GroupHoldr
             */
            functions.del[name] = self["del" + name] = function(key) {
                group = group.splice(group.indexOf(key), 1);
                return self;
            };
        }
    }

    /* Group/ordering manipulators
    */

    /**
     * Deletes a given object from a group by calling Array.splice on
     * the result of Array.indexOf
     *
     * @param {String} group_name   The string name of the group to delete an
     *                              object from.
     * @param {Number} object   The object to be deleted from the group.
     */
    self.deleteObject = function(group_name, object) {
        groups[group_name].splice(groups[group_name].indexOf(object), 1);
    };

    /**
     * Deletes a given index from a group by calling Array.splice.
     *
     * @param {String} group_name   The string name of the group to delete an
     *                              object from.
     * @param {Number} index   The index to be deleted from the group.
     * @param {Number} [max]   How many elements to delete after that index (if
     *                         falsy, just the first 1).
     */
    self.deleteIndex = function(group_name, index, max) {
        groups[group_name].splice(index, max || 1);
    };

    /**
     * Switches an object from group_old to group_new by removing it from the
     * old group and adding it to the new. If the new group uses an associative
     * array, a key should be passed in (which defaults to undefined).
     *
     * @param {Mixed} object   The object to be moved from one group to another.
     * @param {String} group_old   The string name of the object's old group.
     * @param {String} group_new   The string name of the object's new group.
     * @param {String} [key_new]   A key for the object to be placed in the new
     *                             group, required only if the group contains an
     *                             associative array.
     */
    self.switchObjectGroup = function(object, group_old, group_new, key_new) {
        self.deleteObject(group_old, object);
        functions.add[group_new](object, key_new);
    };

    /* Simple gets
    */

    self.getFunctions = function() {
        return functions;
    }

    self.getGroups = function() {
        return groups;
    }

    self.getGroupNames = function() {
        return group_names;
    }

    /* Utilities
    */

    /**
     * Returns the name of a type specified by a string ("Array" or "Object")
     *
     * @param {String} str   The name of the type. If falsy, defaults to Array
     * @return {String}
     * @remarks The type is determined by the str[0]; if it exists and is 'o',
     *          the outcome is "Object", otherwise it's "Array".
     */
    function getTypeName(str) {
        if(str && str.charAt && str.charAt(0).toLowerCase() == 'o') {
            return "Object";
        }
        return "Array";
    }

    /**
     * Returns function specified by a string (Array or Object)
     *
     * @param {String} str   The name of the type. If falsy, defaults to Array
     * @return {Function}
     * @remarks The type is determined by the str[0]; if it exists and is 'o',
     *          the outcome is Object, otherwise it's Array.
     */
    function getTypeFunction(str) {
        if(str && str.charAt && str.charAt(0).toLowerCase() == 'o') {
            return Object;
        }
        return Array;
    }

    /**
     * Uppercases the first character in a string
     *
     * @param {String} str
     * @return {String}
     */
    function ucFirst(str) {
        return str[0].toUpperCase() + str.slice(1);
    }

    self.reset(settings || {});
}<commit_msg>Added applyAll and callAll to GroupHoldr<commit_after>/**
 *
 */
function GroupHoldr(settings) {
    "use strict";
    if(!this || this === window) {
        return new GroupHoldr(settings);
    }
    var self = this,

        // Associative array of strings to groups, where groups are each some
        // sort of array (either typical or associative).
        groups,

        // Associative array containing "add", "del", "get", and "set" keys to
        // those appropriate functions (e.x. functions.add.MyGroup is the same
        // as self.addMyGroup).
        functions,

        // Array of string names, each of which is tied to a group.
        group_names,

        // Associative array keying each group to the function it uses: Array
        // for regular arrays, and Object for associative arrays.
        group_types,

        // Associative array keying each group to the string name of the
        // function it uses: "Array" for regular arrays, and "Object" for
        // associative arrays.
        group_type_names;

    /**
     * @constructor
     */
    self.reset = function(settings) {
        // The group_names and group_types arguments must be provided
        if(!settings.hasOwnProperty("group_names")) {
            throw new Error("No group_names array provided to GroupHoldr");
        }
        if(!settings.hasOwnProperty("group_types")) {
            throw new Error("No group_types object provided to GroupHoldr");
        }

        // The functions containers start blank, but are filled in setGroupNames
        functions = {
            "set": {},
            "get": {},
            "add": {},
            "del": {}
        };
        setGroupNames(settings.group_names, settings.group_types);
    };

    /**
     * Meaty function to reset, given an array of names an object of types
     * Any pre-existing functions are cleared, and new ones are added as
     * member objects and to {functions}
     *
     * @param {String[]} names   An array of names of groupings to be made
     * @param {Object} types   An associative array of the function types of the
     *                         names given in names.
     */
    function setGroupNames(names, types) {
        if(!(names instanceof Array)) {
            throw new Error("No array of names given to setGroupNames");
        }

        // If there already were group names, clear them
        if(group_names) {
            clearFunctions();
        }

        // Set the new group_names, as ucFirst and removing duplicates
        group_names = names.map(function(name_old) {
            var name_new = ucFirst(name_old);
            // If the name changed, record the new name in types as well
            types[name_new] = types[name_old];
            return name_new;
        });
        group_names.sort();

        // Reset the group types and type names, to be filled next
        group_types = {}
        group_type_names = {};

        // If group_types is an object, set custom group types for everything
        if(typeof(types) == "object") {
            group_names.forEach(function(name) {
                group_types[name] = getTypeFunction(types[name]);
                group_type_names[name] = getTypeName(types[name]);
            });
        }
        // Otherwise assume everything uses the same one, such as from a string
        else {
            var type_func = getTypeFunction(types),
                type_name = getTypeName(types);
            group_names.forEach(function(name) {
                group_types[name] = type_func;
                group_type_names[name] = type_name;
            });
        }

        // Create the containers, and set the modifying functions
        setGroups();
        setFunctions();
    }

    /**
     * Removes any pre-existing "set", "get", etc. functions
     */
    function clearFunctions() {
        group_names.forEach(function(name) {
            // Delete member variable functions
            delete self["set" + name + "Group"];
            delete self["get" + name + "Group"];
            delete self["add" + name];
            delete self["del" + name];

            // Delete functions under .functions by making each type a new {}
            functions.set = {};
            functions.get = {};
            functions.add = {};
            functions.del = {};
        });
    }

    /**
     * Resets groups to an empty object, and fills it with a new group_type for
     * each name in group_names
     */
    function setGroups() {
        groups = {};
        group_names.forEach(function(name) {
            groups[name] = new group_types[name]();
        });
    }

    /**
     * Calls the function setters for each name in group_names
     * @remarks Those are: createFunction<XYZ>: "Set", "Get", "Add", "Del"
     */
    function setFunctions() {
        group_names.forEach(function(name) {
            createFunctionSet(name);
            createFunctionGet(name);
            createFunctionAdd(name);
            createFunctionDel(name);
        });
    }

    /* Function generators
    */

    /**
     * Creates the set<type> function under self and functions.set
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionSet(name) {
        /**
         * Set function for a group of type <container> and name <name>
         * holding objects of type <type>
         *
         * @alias {setName}
         * @param {<container>} value   A new <container> for the group
         * @return {self}   The containing GroupHoldr
         */
        functions.set[name] = self["set" + name + "Group"] = function(value) {
            if(value.constructor != group_types[name]) {
                throw new Error(name + " must be of type "
                        + group_type_names[name]);
            }
            groups[name] = value;
            return self;
        };
    }

    /**
     * Creates the get<type> function under self and functions.get
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionGet(name) {
        /**
         * Get function for a group of type <container> and name <name>
         * holding objects of type <type>
         * Returns the container group itself or an object within, based on key
         *
         * @alias {setName}
         * @param {<type>} key   The key of an item to be retrieved from the
         *                       group. If not given, the group is returned
         * @return {self}   The containing GroupHoldr
         */
        functions.get[name] = self["get" + name + "Group"] = function(key) {
            var group = groups[name];
            if(arguments.length < 1) {
                return group;
            }
            return group[key];
        };
    }

    /**
     * Creates the add<type> function under self and functions.add
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionAdd(name) {
        var group = groups[name];
        if(group_types[name] == Object) {
            /**
             * Get function for a group of type Object and name <name>
             * holding objects of type <type>
             * Adds the value object to the group under key
             *
             * @alias {setName}
             * @param {<type>} key   The key of an item to be retrieved from the
             *                       group. If not given, the group is returned
             * @param value   The value, of any object type, to be added
             * @return {self}   The containing GroupHoldr
             */
            functions.add[name] = self["add" + name] = function(key, value) {
                group[key] = value;
                return self;
            };
        }
        else {
            /**
             * Get function for a group of type Array and name <name>
             * holding objects of type <type>
             * Pushes the value object to the group
             *
             * @alias {setName}
             * @param value   The value, of any object type, to be added
             * @return {self}   The containing GroupHoldr
             */
            functions.add[name] = self["add" + name] = function(value) {
                group.push(value);
                return self;
            };
        }
    }

    /**
     * Creates the del<type> (delete) function under self and functions.del
     *
     * @param {String} name   The name of the group, from group_names
     */
    function createFunctionDel(name) {
        var group = groups[name];
        if(group_types[name] == Object) {
            /**
             * Delete function for a group of type Object and name <name>
             * holding objects of type <type>
             * Removes the object in the group keyed by the key
             *
             * @alias {setName}
             * @param {<type>} key   The item key to be deleted from the group
             * @return {self}   The containing GroupHoldr
             */
            functions.del[name] = self["del" + name] = function(key) {
                delete group[key];
                return self;
            };
        }
        else {
            /**
             * Delete function for a group of type Array and name <name>
             * holding objects of type <type>
             * Removes the keyed object from the group
             *
             * @alias {setName}
             * @param {<type>} key   The item key to be deleted from the group
             * @return {self}   The containing GroupHoldr
             */
            functions.del[name] = self["del" + name] = function(key) {
                group = group.splice(group.indexOf(key), 1);
                return self;
            };
        }
    }

    /* Group/ordering manipulators
    */

    /**
     * Deletes a given object from a group by calling Array.splice on
     * the result of Array.indexOf
     *
     * @param {String} group_name   The string name of the group to delete an
     *                              object from.
     * @param {Number} object   The object to be deleted from the group.
     */
    self.deleteObject = function(group_name, object) {
        groups[group_name].splice(groups[group_name].indexOf(object), 1);
    };

    /**
     * Deletes a given index from a group by calling Array.splice.
     *
     * @param {String} group_name   The string name of the group to delete an
     *                              object from.
     * @param {Number} index   The index to be deleted from the group.
     * @param {Number} [max]   How many elements to delete after that index (if
     *                         falsy, just the first 1).
     */
    self.deleteIndex = function(group_name, index, max) {
        groups[group_name].splice(index, max || 1);
    };

    /**
     * Switches an object from group_old to group_new by removing it from the
     * old group and adding it to the new. If the new group uses an associative
     * array, a key should be passed in (which defaults to undefined).
     *
     * @param {Mixed} object   The object to be moved from one group to another.
     * @param {String} group_old   The string name of the object's old group.
     * @param {String} group_new   The string name of the object's new group.
     * @param {String} [key_new]   A key for the object to be placed in the new
     *                             group, required only if the group contains an
     *                             associative array.
     */
    self.switchObjectGroup = function(object, group_old, group_new, key_new) {
        self.deleteObject(group_old, object);
        functions.add[group_new](object, key_new);
    };

    // apply takes a single given array and sets it as the arguments

    /**
     * Calls a function for each group, with that group as the first argument.
     * Extra arguments may be passed in an array after scope and func, as in
     * Function.apply's standard.
     *
     * @param {Mixed} [scope]   An optional scope to call this from (if falsy,
     *                          defaults to self).
     * @param {Function} func   A function to apply to each group.
     * @param {Array} [args]   An optional array of arguments to pass to the
     *                         function after each group.
     */
    self.applyAll = function(scope, func, args) {
        var i;

        if(!args) {
            args = [ undefined ];
        } else {
            args.unshift(undefined);
        }

        if(!scope) {
            scope = self;
        }

        for(i = group_names.length - 1; i >= 0; i -= 1) {
            args[0] = groups[group_names[i]];
            func.apply(scope, args);
        }
    };

    /**
     * Calls a function for each group, with that group as the first argument.
     * Extra arguments may be passed after scope and func natively, as in
     * Function.call's standard.
     *
     * @param {Mixed} [scope]   An optional scope to call this from (if falsy,
     *                          defaults to self).
     * @param {Function} func   A function to apply to each group.
     */
    self.callAll = function(scope, func) {
        var args = Array.prototype.slice.call(arguments, 1),
            group, i, i;

        if(!scope) {
            scope = self;
        }

        for(i = group_names.length - 1; i >= 0; i -= 1) {
            args[0] = groups[group_names[i]];
            func.apply(scope, args);
        }
    };

    /* Simple gets
    */

    self.getFunctions = function() {
        return functions;
    }

    self.getGroups = function() {
        return groups;
    }

    self.getGroupNames = function() {
        return group_names;
    }

    /* Utilities
    */

    /**
     * Returns the name of a type specified by a string ("Array" or "Object")
     *
     * @param {String} str   The name of the type. If falsy, defaults to Array
     * @return {String}
     * @remarks The type is determined by the str[0]; if it exists and is 'o',
     *          the outcome is "Object", otherwise it's "Array".
     */
    function getTypeName(str) {
        if(str && str.charAt && str.charAt(0).toLowerCase() == 'o') {
            return "Object";
        }
        return "Array";
    }

    /**
     * Returns function specified by a string (Array or Object)
     *
     * @param {String} str   The name of the type. If falsy, defaults to Array
     * @return {Function}
     * @remarks The type is determined by the str[0]; if it exists and is 'o',
     *          the outcome is Object, otherwise it's Array.
     */
    function getTypeFunction(str) {
        if(str && str.charAt && str.charAt(0).toLowerCase() == 'o') {
            return Object;
        }
        return Array;
    }

    /**
     * Uppercases the first character in a string
     *
     * @param {String} str
     * @return {String}
     */
    function ucFirst(str) {
        return str[0].toUpperCase() + str.slice(1);
    }

    self.reset(settings || {});
}<|endoftext|>


<commit_before>process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const environment = require('./environment')

module.exports = environment.toWebpackConfig()
<commit_msg>further tweaks to webpacker config for travisci<commit_after>process.env.NODE_ENV = process.env.NODE_ENV || 'test'

const environment = require('./environment')

module.exports = environment.toWebpackConfig()
<|endoftext|>


<commit_before>// Backbone.Component
// ==================
//
// A thin layer on top of Backbone's view class to add nested child views.

Backbone.Component = Backbone.View.extend({

  // Override constructor so Components can use initialise normally.
  constructor: function() {
    this._setup();
    Backbone.View.apply(this, arguments);
  },

  // Public API
  // ----------

  // Add a child view to the end of the element.
  // Optionally specify a selector within the view to attach to.
  // Aliased to `add`.
  append: function(view, selector) {
    this._addChild(view, selector, 'append');
    return this;
  },

  // Add a child view to the beginning of the view's element.
  // Optionally specify a selector within the view to attach to.
  prepend: function(view, selector) {
    this._addChild(view, selector, 'prepend');
    return this;
  },

  // Private methods
  // ----------

  // Initial setup. Create new child array, alias add to append, and wrap
  // render and remove methods.
  _setup: function() {
    this._children = [];
    this.add = this.append;
    this.render = this._wrapRender();
    this.remove = this._wrapRemove();
  },

  // Add a child view to an internal array, keeping a reference to
  // the element and attach method it should use.
  _addChild: function(view, selector, method) {
    var target = selector ? this.$(selector) : this.$el;
    var child = { view: view, target: target, method: method || 'append' };

    // Assign a method to the child so it can remove itself from
    // `_children` array when it's removed.
    view._removeFromParent = _.bind(this._removeFromParent, this, child);

    this._children.push(child);
  },

  // Call remove for each child added to the view.
  _removeChildren: function() {
    _.invoke(_.pluck(this._children, 'view'), 'remove');
  },

  // Template method to remove a view from its parent.
  _removeFromParent: function(child) {
    this._children = _.without(this._children, child);
  },

  // Wrap render to automatically attach all children.
  _wrapRender: function() {
    var wrapper = function(render) {
      render.apply(this, _.rest(arguments));
      this._attachChildren();
      return this;
    };

    var originalRender = _.bind(this.render, this);
    return _.wrap(originalRender, wrapper);
  },

  // Wrap remove to automatically remove all children and itself from
  // its parent.
  _wrapRemove: function() {
    var wrapper = function(remove) {
      this._removeFromParent();
      this._removeChildren();
      remove.apply(this, _.rest(arguments));
      return this;
    };

    var originalRemove = _.bind(this.remove, this);
    return _.wrap(originalRemove, wrapper);
  },

  // Attach child to the correct element and with correct method.
  // Defaults to `this.$el` and `append`.
  _attachChild: function(child) {
    child.target[child.method](child.view.render().$el);
  },

  // Attach all children in the right order.
  // Call `delegateEvents` for each child view so handlers are correctly
  // bound after being attached.
  _attachChildren: function() {
    _.each(this._children, function(child) {
      this._attachChild(child);
      child.view.delegateEvents();
    }, this);
  }
});
<commit_msg>alias add at the prototype level<commit_after>// Backbone.Component
// ==================
//
// A thin layer on top of Backbone's view class to add nested child views.

Backbone.Component = Backbone.View.extend({

  // Override constructor so Components can use `initialize` normally.
  constructor: function() {
    this._setup();
    Backbone.View.apply(this, arguments);
  },

  // Public API
  // ----------

  // Add a child view to the end of the element.
  // Optionally specify a selector within the view to attach to.
  // Aliased to `add`.
  append: function(view, selector) {
    this._addChild(view, selector, 'append');
    return this;
  },

  // Add a child view to the beginning of the view's element.
  // Optionally specify a selector within the view to attach to.
  prepend: function(view, selector) {
    this._addChild(view, selector, 'prepend');
    return this;
  },

  // Private methods
  // ----------

  // Initial setup. Create new child array, and wrap `render` and `remove`
  // methods.
  _setup: function() {
    this._children = [];
    this.render = this._wrapRender();
    this.remove = this._wrapRemove();
  },

  // Add a child view to an internal array, keeping a reference to
  // the element and attach method it should use.
  _addChild: function(view, selector, method) {
    var target = selector ? this.$(selector) : this.$el;
    var child = { view: view, target: target, method: method || 'append' };

    // Assign a method to the child so it can remove itself from `_children`
    // array when it's removed.
    view._removeFromParent = _.bind(this._removeFromParent, this, child);

    this._children.push(child);
  },

  // Call `remove` for each child added to the view.
  _removeChildren: function() {
    _.invoke(_.pluck(this._children, 'view'), 'remove');
  },

  // Template method to remove a view from its parent.
  _removeFromParent: function(child) {
    this._children = _.without(this._children, child);
  },

  // Wrap `render` to automatically attach all children.
  _wrapRender: function() {
    var wrapper = function(render) {
      render.apply(this, _.rest(arguments));
      this._attachChildren();
      return this;
    };

    var originalRender = _.bind(this.render, this);
    return _.wrap(originalRender, wrapper);
  },

  // Wrap `remove` to automatically remove all children and itself from its
  // parent.
  _wrapRemove: function() {
    var wrapper = function(remove) {
      this._removeFromParent();
      this._removeChildren();
      remove.apply(this, _.rest(arguments));
      return this;
    };

    var originalRemove = _.bind(this.remove, this);
    return _.wrap(originalRemove, wrapper);
  },

  // Attach child to the correct element and with the correct method.
  // Defaults to `this.$el` and `append`.
  _attachChild: function(child) {
    child.target[child.method](child.view.render().$el);
  },

  // Attach all children in the right order, and call `delegateEvents` for each
  // child view so handlers are correctly bound after being attached.
  _attachChildren: function() {
    _.each(this._children, function(child) {
      this._attachChild(child);
      child.view.delegateEvents();
    }, this);
  }
});

// Alias `add` to `append`.
Backbone.Component.prototype.add = Backbone.Component.prototype.append;
<|endoftext|>


<commit_before>// Backbone.Component
// ==================
//
// A thin layer on top of Backbone's view class to add nested child views.

Backbone.Component = Backbone.View.extend({

  // Override constructor so Components can use `initialize` normally.
  constructor: function() {
    this._setup();
    Backbone.View.apply(this, arguments);
  },

  // Public API
  // ----------

  // Add a child view to the end of the element.
  // Optionally specify a selector within the view to attach to.
  // Aliased to `add`.
  append: function(view, selector) {
    this._addChild(view, selector, 'append');
    return this;
  },

  // Add a child view to the beginning of the view's element.
  // Optionally specify a selector within the view to attach to.
  prepend: function(view, selector) {
    this._addChild(view, selector, 'prepend');
    return this;
  },

  // Retrieve the list of child views.
  children: function() {
    return _.pluck(this._children, 'view');
  },

  // Remove all child views added to this one.
  empty: function() {
    this._removeChildren();
    return this;
  },

  // Render the existing template with the provided data.
  renderTemplate: function(data) {
    this.$el.html(this._compile(this.template)(data));
    return this;
  },

  // Wraps `_.template`. Can be replaced by any object that responds to
  // `compile` and returns a compiled template function.
  renderer: {
    compile: function(template) {
      this._template = this._template || _.partial(_.template, template);
      return this._template;
    }
  },

  // Private methods
  // ---------------

  // Initial setup. Create new child array, and wrap `render` and `remove`
  // methods.
  _setup: function() {
    // Mixin renderer to view instance, so that compiled templates aren't
    // shared.
    _.extend(this, { _compile: this.renderer.compile });

    this._children = [];
    this.render = this._wrapRender();
    this.remove = this._wrapRemove();
  },

  // Add a child view to an internal array, keeping a reference to the element
  // and attach method it should use.
  _addChild: function(view, selector, method) {
    var child = { view: view, selector: selector, method: method || 'append' };

    // Assign a method to the child so it can remove itself from `_children`
    // array when it's removed.
    // Written as an anonymous function to prevent it being bound multiple times
    // in grandparent-parent-child situations.
    var removeFromParent = function(child) {
      this._children = _.without(this._children, child);
    };
    view._removeFromParent = _.bind(removeFromParent, this, child);

    this._children.push(child);
  },

  // Call `remove` for each child added to the view.
  _removeChildren: function() {
    _.invoke(this.children(), 'remove');
    this._children = [];
  },

  // Replaced by a function scoped to the parent if the component is added as a
  // child (in `_addChild`) but otherwise does nothing (as the component
  // wouldn't have a parent).
  _removeFromParent: function() {
  },

  // Wrap `render` to automatically attach all children.
  _wrapRender: function() {
    var wrapper = function(render) {
      var args = _.rest(arguments);

      if (this._isIE) {
        this._detachChildren();
      }
      render.apply(this, args);
      this._attachChildren();

      return Backbone.Component.prototype.render.apply(this, args);
    };

    var originalRender = _.bind(this.render, this);
    return _.wrap(originalRender, wrapper);
  },

  // Wrap `remove` to automatically remove all children and itself from its
  // parent.
  _wrapRemove: function() {
    var wrapper = function(remove) {
      var args = _.rest(arguments);

      this._removeFromParent();
      this._removeChildren();
      remove.apply(this, args);

      return Backbone.Component.prototype.remove.apply(this, args);
    };

    var originalRemove = _.bind(this.remove, this);
    return _.wrap(originalRemove, wrapper);
  },

  // Attach child to the correct element and with the correct method. Defaults
  // to `this.$el` and `append`.
  // Only call `render` on the child the first time.
  _attachChild: function(child) {
    var target = child.selector ? this.$(child.selector) : this.$el;
    if (!child.rendered) {
      child.view.render();
    }
    target[child.method](child.view.$el);
  },

  // Attach all children in the right order, and call `delegateEvents` for each
  // child view so handlers are correctly bound after being attached.
  // Only call `delegateEvents` on the child the first time.
  _attachChildren: function() {
    _.each(this._children, function(child) {
      this._attachChild(child);
      if (!child.rendered) {
        child.view.delegateEvents();
      }
      child.rendered = true;
    }, this);
  },

  _detachChildren: function() {
    _.each(this._children, function(child) {
      if (child.view.el) {
        child.view.$el.detach();
      }
    }, this);
  },

  _isIE: (function() {
    // old MSIE < IE 11
    if (document.all) {
      return true;
    }
    // MSIE 11
    return (!(window.ActiveXObject) && "ActiveXObject" in window);
  })()

});

// Alias `add` to `append`.
Backbone.Component.prototype.add = Backbone.Component.prototype.append;

<commit_msg>silly style guide<commit_after>// Backbone.Component
// ==================
//
// A thin layer on top of Backbone's view class to add nested child views.

Backbone.Component = Backbone.View.extend({

  // Override constructor so Components can use `initialize` normally.
  constructor: function() {
    this._setup();
    Backbone.View.apply(this, arguments);
  },

  // Public API
  // ----------

  // Add a child view to the end of the element.
  // Optionally specify a selector within the view to attach to.
  // Aliased to `add`.
  append: function(view, selector) {
    this._addChild(view, selector, 'append');
    return this;
  },

  // Add a child view to the beginning of the view's element.
  // Optionally specify a selector within the view to attach to.
  prepend: function(view, selector) {
    this._addChild(view, selector, 'prepend');
    return this;
  },

  // Retrieve the list of child views.
  children: function() {
    return _.pluck(this._children, 'view');
  },

  // Remove all child views added to this one.
  empty: function() {
    this._removeChildren();
    return this;
  },

  // Render the existing template with the provided data.
  renderTemplate: function(data) {
    this.$el.html(this._compile(this.template)(data));
    return this;
  },

  // Wraps `_.template`. Can be replaced by any object that responds to
  // `compile` and returns a compiled template function.
  renderer: {
    compile: function(template) {
      this._template = this._template || _.partial(_.template, template);
      return this._template;
    }
  },

  // Private methods
  // ---------------

  // Initial setup. Create new child array, and wrap `render` and `remove`
  // methods.
  _setup: function() {
    // Mixin renderer to view instance, so that compiled templates aren't
    // shared.
    _.extend(this, { _compile: this.renderer.compile });

    this._children = [];
    this.render = this._wrapRender();
    this.remove = this._wrapRemove();
  },

  // Add a child view to an internal array, keeping a reference to the element
  // and attach method it should use.
  _addChild: function(view, selector, method) {
    var child = { view: view, selector: selector, method: method || 'append' };

    // Assign a method to the child so it can remove itself from `_children`
    // array when it's removed.
    // Written as an anonymous function to prevent it being bound multiple times
    // in grandparent-parent-child situations.
    var removeFromParent = function(child) {
      this._children = _.without(this._children, child);
    };
    view._removeFromParent = _.bind(removeFromParent, this, child);

    this._children.push(child);
  },

  // Call `remove` for each child added to the view.
  _removeChildren: function() {
    _.invoke(this.children(), 'remove');
    this._children = [];
  },

  // Replaced by a function scoped to the parent if the component is added as a
  // child (in `_addChild`) but otherwise does nothing (as the component
  // wouldn't have a parent).
  _removeFromParent: function() {
  },

  // Wrap `render` to automatically attach all children.
  _wrapRender: function() {
    var wrapper = function(render) {
      var args = _.rest(arguments);

      if (this._isIE) {
        this._detachChildren();
      }
      render.apply(this, args);
      this._attachChildren();

      return Backbone.Component.prototype.render.apply(this, args);
    };

    var originalRender = _.bind(this.render, this);
    return _.wrap(originalRender, wrapper);
  },

  // Wrap `remove` to automatically remove all children and itself from its
  // parent.
  _wrapRemove: function() {
    var wrapper = function(remove) {
      var args = _.rest(arguments);

      this._removeFromParent();
      this._removeChildren();
      remove.apply(this, args);

      return Backbone.Component.prototype.remove.apply(this, args);
    };

    var originalRemove = _.bind(this.remove, this);
    return _.wrap(originalRemove, wrapper);
  },

  // Attach child to the correct element and with the correct method. Defaults
  // to `this.$el` and `append`.
  // Only call `render` on the child the first time.
  _attachChild: function(child) {
    var target = child.selector ? this.$(child.selector) : this.$el;
    if (!child.rendered) {
      child.view.render();
    }
    target[child.method](child.view.$el);
  },

  // Attach all children in the right order, and call `delegateEvents` for each
  // child view so handlers are correctly bound after being attached.
  // Only call `delegateEvents` on the child the first time.
  _attachChildren: function() {
    _.each(this._children, function(child) {
      this._attachChild(child);
      if (!child.rendered) {
        child.view.delegateEvents();
      }
      child.rendered = true;
    }, this);
  },

  _detachChildren: function() {
    _.each(this._children, function(child) {
      if (child.view.el) {
        child.view.$el.detach();
      }
    }, this);
  },

  _isIE: (function() {
    // old MSIE < IE 11
    if (document.all) {
      return true;
    }
    // MSIE 11
    return (!(window.ActiveXObject) && 'ActiveXObject' in window);
  })()

});

// Alias `add` to `append`.
Backbone.Component.prototype.add = Backbone.Component.prototype.append;

<|endoftext|>


<commit_before>(function() {
  var Typeahead = function(models, options) {
    // The first parameter 'model' is an optional parameter
    // If given an array, a copy will be made and passed to preInitialize
    if (_.isArray(models)) {
      // It is copied to prevent overwrite
      // TODO this copy may be unnecessary
      models = models.slice(0);
      if (!_.isObject(options)) options = {};
    } else if (_.isObject(models)) {
      options = models;
      // TODO Should also error if no collection was provided in options
    } else {
      throw new Error('A typeahead must be created with either initial models or have its collection specified in an options object');
    }

    // Initialize
    this.preInitialize.call(this, models, options);
    Backbone.View.call(this, options);
    this.postInitialize.call(this);
  }

  Typeahead.extend = Backbone.View.extend;

  // TODO Use a preInit/postInit style view
  Typeahead.ItemView = Backbone.View.extend({
    tagName: 'li',
    events: {
      'click': 'selectItem',
      'mouseover': 'activateItem',
    },
    initialize: function(options) {
      // A reference to the parent view (aka the Typeahead) is required
      this.parent = options.parent;
    },
    render: function() {
      this.$el.html(_.template(this.template, this.model.toJSON()));
      return this;
    },
    // TODO Or use triggers instead?
    selectItem: function() {
      this.parent.selectModel(this.model);
    },
    activateItem: function() {
      this.parent.activateModel(this.model);
    },
  });

  _.extend(Typeahead.prototype, Backbone.View.prototype, {
    // Pre-initialization should set up the collection type (remote / local)
    preInitialize: function(models, options) {
      // Set sane defaults
      // TODO Allow compound keys? Introspect first model for a default key?
      options.key || (options.key = 'name');
      options.limit || (options.limit = 8);

      if (_.isUndefined(options.collection) && _.isArray(models)) {
        // TODO Any properties of collections that options can't handle?
        options.collection = new Backbone.Collection(models, options);
      }

      // Build a item view if one was not provided
      // TODO confirm that any given view is a Backbone.View object
      this.view = options.view;

      // TODO Uhoh, context
      var self = this;

      if (_.isUndefined(this.view)) {
        // TODO provide the partially extended view for users to extend
        this.view = Typeahead.ItemView.extend({
          // Dynamically construct a template with the key
          // TODO Allow the item template to be passed through options
          template: options.itemTemplate || '<%- ' + options.key + ' %>',
        });
      }

      // Attach native events, deferring to custom events
      this.events = _.extend({}, this.nativeEvents, _.result(this, 'events'));

      // TODO Listen to changes on the collection
    },
    // Models were emptied by the preInitialization function
    // The parent Backbone.View constructor function has finished
    // Options have already been parsed by _context()
    postInitialize: function() {
      // TODO Build the cached lookup of models by the given key, which must
      // be updated when the collection changes and an option to turn off
      this.results = []; // matched results as an array of Backbone Models

      // Boolean toggles
      this.focused = false; // Is the input in focus?
      this.shown = false; // Is the menu shown?
      this.mousedover = false; // Is the mouse over the typeahead (incl. menu)?
    },
    template: '<input type="text" class="form-control" placeholder="Search" />

',
    nativeEvents: {
      'keyup': 'keyup',
      'keypress': 'keypress',
      'keydown': 'keydown',
      'blur input': 'blur',
      'focus input': 'focus',
      'mouseenter': 'mouseenter',
      'mouseleave': 'mouseleave',
    },
    // The render function should be overwritten on extended typeaheads
    render: function() {
      this.$el.html(this.template);
      this.$menu = this.$('ul');
      this.$input = this.$('input');
      return this;
    },
    // Called by searchInput and whenever models change
    rerender: function(models) {
      this.$menu.empty();
      _.each(models, this.renderModel, this);
      models.length ? this.show() : this.hide();
    },
    renderModel: function(model) {
      // TODO Do not re-render views?
      this.$menu.append((new this.view({model: model, parent: this})).render().el);
    },
    // Return the models with a key that matches a portion of the given value
    // TODO compound keys! cached values! the kitchen sink!
    search: function(value) {
      // Use a regex to quickly perform a case-insensitive match
      var re = new RegExp(value, 'i');
      var key = this.options.key;
      return this.collection.filter(function(model) {
          return re.test(model.get(key));
      });
    },
    // Pull the value from the search input and re-render the matched models
    searchInput: function() {
      // TODO it'd be nice to limit the results during array iteration
      this.results = this.search(this.$input.val()).slice(0, this.options.limit);
      this.rerender(this.results);
    },
    select: function() {
      // TODO This may go wrong with different templates
      var index = this.$menu.find('.active').index();
      this.selectModel(this.results[index]);
    },
    selectModel: function(model) {
      // Update the input field with the key attribute of the select model
      this.$input.val(model.get(this.options.key));
      // Hide the menu
      this.hide();
      // TODO What other parameters should be in the trigger?
      this.trigger('selected', model, this.collection)
      // Empty the results
      this.results = [];
    },
    activateModel: function(model) {
      this.$menu.find('.active').removeClass('active');
    },
    // Misc. events
    keyup: function(evt) {
      switch(evt.keyCode) {
        case 40: // Down arrow
        case 38: // Up arrow
        case 16: // Shift
        case 17: // Ctrl
        case 18: // Alt
          break;
        // case 9: // Tab - disabled to prevent rogue select on tabbed focus
        // TODO tab should also leave focus
        case 13: // Enter
          // TODO shown needs to be returned to its original function (as an
          // indicator of whether the menu is currently displayed or not)
          if (!this.shown) return;
          this.select();
          break;
        case 27: // escape
          if (!this.shown) return;
          this.hide();
          break
        default:
          this.searchInput();
      }
      evt.stopPropagation();
      evt.preventDefault();
    },
    // Menu state
    focus: function() {
      this.focused = true;
      // TODO Only show the menu if no item has been selected
      if (!this.shown) this.show();
    },
    blur: function() {
      this.focused = false;
      if (!this.mousedover && this.shown) this.hide();
    },
    mouseenter: function() {
      this.mousedover = true;
      // TODO Re-add 'active' class to the current target
    },
    mouseleave: function() {
      this.mousedover = false;
      if (!this.focused && this.shown) this.hide();
    },
    // Allow the user to change their selection with the keyboard
    keydown: function(evt) {
      // TODO I still hate this array check
      this.suppressKeyPressRepeat = ~$.inArray(evt.keyCode, [40,38,9,13,27]);
      this.move(evt);
    },
    keypress: function(evt) {
      // The suppressKeyPressRepeat check exists because keydown and keypress
      // may fire for the same event
      if (this.suppressKeyPressRepeat) return;
      this.move(evt);
    },
    move: function(evt) {
      if (!this.shown) return;
      switch(evt.keyCode) {
        case 9: // Tab
        case 13: // Enter
        case 27: // Escape
          evt.preventDefault();
          break;
        case 38: // Up arrow
          evt.preventDefault();
          this.prevItem();
          break;
        case 40: // Down arrow
          evt.preventDefault();
          this.nextItem();
          break;
      }
      evt.stopPropagation();
    },
    prevItem: function() {
      // TODO should there be signals for prev and next?
      var active = this.$menu.find('.active').removeClass('active');
      var prev = active.prev();
      if (!prev.length) prev = this.$menu.find('li').last();
      prev.addClass('active');
    },
    nextItem: function() {
      var active = this.$menu.find('.active').removeClass('active');
      var next = active.next();
      if (!next.length) next = this.$menu.find('li').first();
      next.addClass('active');
    },
    // Show or hide the menu depending on the typeahead's state
    show: function() {
      // DO not show if there are no results
      if (!this.results.length) return;
      var pos = $.extend(
        {},
        this.$input.position(),
        // Calling the [0] index returns the vanilla HTML object
        {height: this.$input[0].offsetHeight}
      );
      this.$menu.css({
        top: pos.top + pos.height,
        left: pos.left
      }).show();
      this.shown = true;
      return this;
    },
    hide: function() {
      this.$menu.hide();
      this.shown = false;
      return this;
    },
  });

  Backbone.Typeahead = Typeahead;

}).call(this);
<commit_msg>Added a convenience method for clearing the search input<commit_after>(function() {
  var Typeahead = function(models, options) {
    // The first parameter 'model' is an optional parameter
    // If given an array, a copy will be made and passed to preInitialize
    if (_.isArray(models)) {
      // It is copied to prevent overwrite
      // TODO this copy may be unnecessary
      models = models.slice(0);
      if (!_.isObject(options)) options = {};
    } else if (_.isObject(models)) {
      options = models;
      // TODO Should also error if no collection was provided in options
    } else {
      throw new Error('A typeahead must be created with either initial models or have its collection specified in an options object');
    }

    // Initialize
    this.preInitialize.call(this, models, options);
    Backbone.View.call(this, options);
    this.postInitialize.call(this);
  }

  Typeahead.VERSION = '0.1.0';
  Typeahead.extend = Backbone.View.extend;

  // TODO Use a preInit/postInit style view
  Typeahead.ItemView = Backbone.View.extend({
    tagName: 'li',
    events: {
      'click': 'selectItem',
      'mouseover': 'activateItem',
    },
    initialize: function(options) {
      // A reference to the parent view (aka the Typeahead) is required
      this.parent = options.parent;
    },
    render: function() {
      this.$el.html(_.template(this.template, this.model.toJSON()));
      return this;
    },
    // TODO Or use triggers instead?
    selectItem: function() {
      this.parent.selectModel(this.model);
    },
    activateItem: function() {
      this.parent.activateModel(this.model);
    },
  });

  _.extend(Typeahead.prototype, Backbone.View.prototype, {
    // Pre-initialization should set up the collection type (remote / local)
    preInitialize: function(models, options) {
      // Set sane defaults
      // TODO Allow compound keys? Introspect first model for a default key?
      options.key || (options.key = 'name');
      options.limit || (options.limit = 8);

      if (_.isUndefined(options.collection) && _.isArray(models)) {
        // TODO Any properties of collections that options can't handle?
        options.collection = new Backbone.Collection(models, options);
      }

      // Build a item view if one was not provided
      // TODO confirm that any given view is a Backbone.View object
      this.view = options.view;

      // TODO Uhoh, context
      var self = this;

      if (_.isUndefined(this.view)) {
        // TODO provide the partially extended view for users to extend
        this.view = Typeahead.ItemView.extend({
          // Dynamically construct a template with the key
          // TODO Allow the item template to be passed through options
          template: options.itemTemplate || '<%- ' + options.key + ' %>',
        });
      }

      // Attach native events, deferring to custom events
      this.events = _.extend({}, this.nativeEvents, _.result(this, 'events'));

      // TODO Listen to changes on the collection
    },
    // Models were emptied by the preInitialization function
    // The parent Backbone.View constructor function has finished
    // Options have already been parsed by _context()
    postInitialize: function() {
      // TODO Build the cached lookup of models by the given key, which must
      // be updated when the collection changes and an option to turn off
      this.results = []; // matched results as an array of Backbone Models

      // Boolean toggles
      this.focused = false; // Is the input in focus?
      this.shown = false; // Is the menu shown?
      this.mousedover = false; // Is the mouse over the typeahead (incl. menu)?
    },
    template: '<input type="text" class="form-control" placeholder="Search" />

',
    nativeEvents: {
      'keyup': 'keyup',
      'keypress': 'keypress',
      'keydown': 'keydown',
      'blur input': 'blur',
      'focus input': 'focus',
      'mouseenter': 'mouseenter',
      'mouseleave': 'mouseleave',
    },
    // The render function should be overwritten on extended typeaheads
    render: function() {
      this.$el.html(this.template);
      this.$menu = this.$('ul');
      this.$input = this.$('input');
      return this;
    },
    // Called by searchInput and whenever models change
    rerender: function(models) {
      this.$menu.empty();
      _.each(models, this.renderModel, this);
      models.length ? this.show() : this.hide();
    },
    renderModel: function(model) {
      // TODO Do not re-render views?
      this.$menu.append((new this.view({model: model, parent: this})).render().el);
    },
    // Return the models with a key that matches a portion of the given value
    // TODO compound keys! cached values! the kitchen sink!
    search: function(value) {
      // Use a regex to quickly perform a case-insensitive match
      var re = new RegExp(value, 'i');
      var key = this.options.key;
      return this.collection.filter(function(model) {
          return re.test(model.get(key));
      });
    },
    // Convenience method for clearing the search input
    clearInput: function() {
      this.$input.val('');
    },
    // Pull the value from the search input and re-render the matched models
    searchInput: function() {
      // TODO it'd be nice to limit the results during array iteration
      this.results = this.search(this.$input.val()).slice(0, this.options.limit);
      this.rerender(this.results);
    },
    select: function() {
      // TODO This may go wrong with different templates
      var index = this.$menu.find('.active').index();
      this.selectModel(this.results[index]);
    },
    selectModel: function(model) {
      // Update the input field with the key attribute of the select model
      this.$input.val(model.get(this.options.key));
      // Hide the menu
      this.hide();
      // TODO What other parameters should be in the trigger?
      this.trigger('selected', model, this.collection)
      // Empty the results
      this.results = [];
    },
    activateModel: function(model) {
      this.$menu.find('.active').removeClass('active');
    },
    // Misc. events
    keyup: function(evt) {
      switch(evt.keyCode) {
        case 40: // Down arrow
        case 38: // Up arrow
        case 16: // Shift
        case 17: // Ctrl
        case 18: // Alt
          break;
        // case 9: // Tab - disabled to prevent rogue select on tabbed focus
        // TODO tab should also leave focus
        case 13: // Enter
          // TODO shown needs to be returned to its original function (as an
          // indicator of whether the menu is currently displayed or not)
          if (!this.shown) return;
          this.select();
          break;
        case 27: // escape
          if (!this.shown) return;
          this.hide();
          break
        default:
          this.searchInput();
      }
      evt.stopPropagation();
      evt.preventDefault();
    },
    // Menu state
    focus: function() {
      this.focused = true;
      // TODO Only show the menu if no item has been selected
      if (!this.shown) this.show();
    },
    blur: function() {
      this.focused = false;
      if (!this.mousedover && this.shown) this.hide();
    },
    mouseenter: function() {
      this.mousedover = true;
      // TODO Re-add 'active' class to the current target
    },
    mouseleave: function() {
      this.mousedover = false;
      if (!this.focused && this.shown) this.hide();
    },
    // Allow the user to change their selection with the keyboard
    keydown: function(evt) {
      // TODO I still hate this array check
      this.suppressKeyPressRepeat = ~$.inArray(evt.keyCode, [40,38,9,13,27]);
      this.move(evt);
    },
    keypress: function(evt) {
      // The suppressKeyPressRepeat check exists because keydown and keypress
      // may fire for the same event
      if (this.suppressKeyPressRepeat) return;
      this.move(evt);
    },
    move: function(evt) {
      if (!this.shown) return;
      switch(evt.keyCode) {
        case 9: // Tab
        case 13: // Enter
        case 27: // Escape
          evt.preventDefault();
          break;
        case 38: // Up arrow
          evt.preventDefault();
          this.prevItem();
          break;
        case 40: // Down arrow
          evt.preventDefault();
          this.nextItem();
          break;
      }
      evt.stopPropagation();
    },
    prevItem: function() {
      // TODO should there be signals for prev and next?
      var active = this.$menu.find('.active').removeClass('active');
      var prev = active.prev();
      if (!prev.length) prev = this.$menu.find('li').last();
      prev.addClass('active');
    },
    nextItem: function() {
      var active = this.$menu.find('.active').removeClass('active');
      var next = active.next();
      if (!next.length) next = this.$menu.find('li').first();
      next.addClass('active');
    },
    // Show or hide the menu depending on the typeahead's state
    show: function() {
      // DO not show if there are no results
      if (!this.results.length) return;
      var pos = $.extend(
        {},
        this.$input.position(),
        // Calling the [0] index returns the vanilla HTML object
        {height: this.$input[0].offsetHeight}
      );
      this.$menu.css({
        top: pos.top + pos.height,
        left: pos.left
      }).show();
      this.shown = true;
      return this;
    },
    hide: function() {
      this.$menu.hide();
      this.shown = false;
      return this;
    },
  });

  Backbone.Typeahead = Typeahead;

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


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')
const parser = require("./parseFile")
const glob = require('glob')
const stripJs = require('strip-js')

function readModFile(modId) {
    var fileName = path.join(__dirname, '/mods/', modId, '/mod.txt')
    let data = parser.readFile(fileName)
    return {
        id: modId,
        title: data.Name,
        author: data.Author,
        version: data.Version,
        description: stripJs(data.Description),
        logo: '/mods/' + modId + '/media/' + data.Logo
    }
}

module.exports = {
    listMods: (req, res) => {
        glob('mods/*/mod.txt', (err, files) => {
            res.json({
                mods: files.map(file => readModFile(file.replace(/mods\/(.*?)\/mod.txt/, '$1')))
            })
        })
    },
    getMediaFile: (req, res) => {
        var file = path.join(__dirname, 'mods', req.params.mod, req.params.file)
        if (fs.existsSync(file)) {
            res.sendFile(file)
        } else {
            res.sendFile(path.join(__dirname, 'plug.png'))
        }
    },

    createMod: (req,res) =>{
        console.log(req.body)
        const dPath = path.join(__dirname, "mods", req.body.id)
        if(fs.existsSync(dPath)) {
            console.log("Project already exits!")
            res.json({error:"A mod with this ID already exists!"})
            return
        }
        fs.mkdirSync(dPath);
        //Create Text
        let txt = ""
        let rules = JSON.parse(fs.readFileSync("./templates/mod.json","utf8"))
        rules.forEach(line => {
            switch(line.typ){
                case "copy":
                    txt += line.text + "\n"
                    break
                case "combine":
                    if(line.quote){
                        txt += line.text + '\"' + req.body[line.value] + '\" \n'
                    } else{
                        txt += line.text + req.body[line.value] + "\n"
                    }
            }
        })
        //Create mod.txt
        const file = path.join(__dirname, "/mods/", req.body.id, "/mod.txt")
        fs.writeFile(file, txt, (err) => {
            if (err) console.log(err)
        })
        let newMod = parser(file);
        res.json(newMod)
    },

    updateMod: (req, res) => {
        let fileName = path.join(__dirname, "mods", req.params.mod, '/mod.txt')
        if (!fs.existsSync(fileName)) {
            res.json({error: 'Mod does not exist'})
        } else {
            let mod = fs.readFileSync(fileName).toString()
            mod = mod.replace(/Name\s*=\s*.*\n/i, 'Name="' + req.body.title + '"\n')
            mod = mod.replace(/Version\s*=\s*.*\n/i, 'Version="' + req.body.version + '"\n')
            mod = mod.replace(/Author\s*=\s*.*\n/i, 'Author="' + req.body.author + '"\n')
            fs.writeFile(fileName, mod, err => {
                if (err) {
                    res.json({error: err})
                } else {
                    res.json(readModFile(req.params.mod))
                }
            })
        }
    }
}
<commit_msg>Fixed parser call in modHandler<commit_after>'use strict'

const fs = require('fs')
const path = require('path')
const parser = require("./parseFile")
const glob = require('glob')
const stripJs = require('strip-js')

function readModFile(modId) {
    var fileName = path.join(__dirname, '/mods/', modId, '/mod.txt')
    let data = parser.readFile(fileName)
    return {
        id: modId,
        title: data.Name,
        author: data.Author,
        version: data.Version,
        description: stripJs(data.Description),
        logo: '/mods/' + modId + '/media/' + data.Logo
    }
}

module.exports = {
    listMods: (req, res) => {
        glob('mods/*/mod.txt', (err, files) => {
            res.json({
                mods: files.map(file => readModFile(file.replace(/mods\/(.*?)\/mod.txt/, '$1')))
            })
        })
    },
    getMediaFile: (req, res) => {
        var file = path.join(__dirname, 'mods', req.params.mod, req.params.file)
        if (fs.existsSync(file)) {
            res.sendFile(file)
        } else {
            res.sendFile(path.join(__dirname, 'plug.png'))
        }
    },

    createMod: (req,res) =>{
        console.log(req.body)
        const dPath = path.join(__dirname, "mods", req.body.id)
        if(fs.existsSync(dPath)) {
            console.log("Project already exits!")
            res.json({error:"A mod with this ID already exists!"})
            return
        }
        fs.mkdirSync(dPath);
        //Create Text
        let txt = ""
        let rules = JSON.parse(fs.readFileSync("./templates/mod.json","utf8"))
        rules.forEach(line => {
            switch(line.typ){
                case "copy":
                    txt += line.text + "\n"
                    break
                case "combine":
                    if(line.quote){
                        txt += line.text + '\"' + req.body[line.value] + '\" \n'
                    } else{
                        txt += line.text + req.body[line.value] + "\n"
                    }
            }
        })
        //Create mod.txt
        const file = path.join(__dirname, "/mods/", req.body.id, "/mod.txt")
        fs.writeFile(file, txt, (err) => {
            if (err) console.log(err)
        })
        let newMod = parser.readFile(file);
        res.json(newMod)
    },

    updateMod: (req, res) => {
        let fileName = path.join(__dirname, "mods", req.params.mod, '/mod.txt')
        if (!fs.existsSync(fileName)) {
            res.json({error: 'Mod does not exist'})
        } else {
            let mod = fs.readFileSync(fileName).toString()
            mod = mod.replace(/Name\s*=\s*.*\n/i, 'Name="' + req.body.title + '"\n')
            mod = mod.replace(/Version\s*=\s*.*\n/i, 'Version="' + req.body.version + '"\n')
            mod = mod.replace(/Author\s*=\s*.*\n/i, 'Author="' + req.body.author + '"\n')
            fs.writeFile(fileName, mod, err => {
                if (err) {
                    res.json({error: err})
                } else {
                    res.json(readModFile(req.params.mod))
                }
            })
        }
    }
}
<|endoftext|>


<commit_before>'use strict';

/*  weak */
// This file is auto-written and used by Gatsby to require
// files from your pages directory.
module.exports = function (callback) {
  var context = require.context('./pages', true, /(coffee|cjsx|jsx|js|markdown|md|ipynb|html|json|yaml|toml)$/); // eslint-disable-line
  if (module.hot) {
    module.hot.accept(context.id, function () {
      context = require.context('./pages', true, /(coffee|cjsx|jsx|js|markdown|md|ipynb|html|json|yaml|toml)$/); // eslint-disable-line
      return callback(context);
    });
  }
  return callback(context);
};<commit_msg>Remove useless file<commit_after><|endoftext|>