<commit_before>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-lwm2m-lib
 *
 * iotagent-lwm2m-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-lwm2m-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-lwm2m-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    objectRegistry = require('./objectRegistry'),
    errors = require('../../errors'),
    logger = require('logops'),
    _ = require('underscore'),
    observers = {};

/**
 * Extract Object type and id from the request URI, returning it using the callback.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 */
function extractUriInfo(req, res, callback) {
    if (req.urlObj.pathname.match(/\/\d+\/\d+\/\d+/)) {
        var resourceIndex = req.urlObj.pathname.lastIndexOf('/'),
            resourceId = req.urlObj.pathname.substring(resourceIndex + 1),
            objectUri = req.urlObj.pathname.substring(0, resourceIndex);

        callback(null, objectUri, resourceId);
    } else if (req.urlObj.pathname.match(/\/\d+(\/\d+)?/)) {
        callback(null, req.urlObj.pathname, null);
    } else {
        callback(new errors.WrongObjectUri(req.urlObj.pathname));
    }
}

/**
 * Invoke the user handler for this operation, with all the information from the query parameters as its arguments.
 * This method handling gives the client an opportunity to change or overwrite the values before returning them to
 * the client. The handler must call the callback with the overwritten value it wants to return. If the callback is
 * called without parameters, the original value is returned instead.
 *
 * @param {Object} queryParams      Object containing all the query parameters.
 * @param {Function} handler        User handler to be invoked.
 */
function applyHandler(resourceId, handler, storedObject, callback) {
    if (storedObject.attributes[resourceId]) {
        handler(
            storedObject.objectType,
            storedObject.objectId,
            resourceId,
            storedObject.attributes[resourceId],

            function handleReadOverwrite(error, result) {
                if (result) {
                    callback(error, result);
                } else {
                    callback(error, storedObject.attributes[resourceId]);
                }
            });
    } else {
        callback(new errors.ResourceNotFound(storedObject.objectType, storedObject.objectId, resourceId));
    }
}

/**
 * Generates the end of request handler that will generate the final response to the COAP Client.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 * @returns {Function}          Request handler, receiving an optional error and the read operation result.
 */
function endRead(req, res) {
    return function (error, result) {
        var body;

        if (error) {
            res.code = error.code;
            body = '';
        } else {
            res.code = '2.05';
            body = result;
        }

        if (_.some(req.options, _.matches({name: 'Observe'}))) {
            res.setOption('Observe', 1);
            res.write(body);
        } else {
            res.end(body);
        }
    };
}

/**
 * Constructs the resource observer. The observer listens in the object registry bus for attribute modifications,
 * sending a new information update to the server each time the selected attribute changes its value.
 *
 * @param {Object} stream           Stream where the new data will be written to the server.
 * @param {Number} resourceId       Resource to read from.
 * @param {Object} storedObject     Object instance to observe.
 * @returns {{uri: (object.objectUri|*|string), stream: *, observation: number}}
 * @constructor
 */
function ResourceObserver(stream, resourceId, storedObject) {
    var data = {
        id: storedObject.objectUri + '/' + resourceId,
        uri: storedObject.objectUri,
        stream: stream,
        observation: 1,
        listener: function newModification(method, id, value) {
            if (method === 'setResource' && resourceId === id) {
                stream.setOption('Observe', data.observation);
                stream.write(value.toString());
                data.observation++;
                data.lastObservation = Date.now();
            }
        }
    };

    function getLastMeasure() {
        objectRegistry.get(storedObject.objectUri, function (error, obj) {
            if (error) {
                logger.error('Error retrieving the last resource value');
            } else {
                data.listener('setResource', resourceId, obj.attributes[resourceId]);
            }
        });
    }

    objectRegistry.getAttributes(storedObject.objectUri + '/' + resourceId, function(error, attributes) {
        if (error) {
            logger.error('Couldn\'t find attributes for the [%s] resource URI. Falling back to value changed');
        }

        if (attributes) {
            if (attributes.pmax) {
                data.scheduler = setInterval(function handleMaxPeriod() {
                    getLastMeasure(storedObject.objectUri, resourceId);
                }, attributes.pmax);
            }
        } else {
            objectRegistry.bus.on(storedObject.objectUri, data.listener);
        }
    });

    return data;
}

/**
 * If the Read request comes with the observation option, apart from resolving the information request, a subscription
 * has to be created, so every time the selected resource changes in value (or in a timely basis, depending on the
 * configuration), the server is updated on the new value.
 *
 * @param {Object} req              Arriving COAP Request to be handled.
 * @param {Object} res              Outgoing COAP Response.
 * @param {Number} resourceId       ID of the resource to be observed.
 * @param {Object} storedObject     Object instance to be read.
 */
function createObservers(req, res, resourceId, storedObject, callback) {
    if (_.some(req.options, _.matches({name: 'Observe'}))) {
        var observer = new ResourceObserver(res, resourceId, storedObject);
        observers[observer.id] = observer;
    }

    callback(null, storedObject);
}

function addAttribute(previous, value) {
    return previous + ';' + value[0] + '=' + value[1];
}

function discoverResourceAttributes(objectUri, resourceId, callback) {
    objectRegistry.getAttributes(objectUri + '/' + resourceId, function (error, attributes) {
        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectUri + '/' + resourceId + '>';

            if (attributes) {
                payload = _.pairs(attributes).reduce(addAttribute, payload);
            }

            callback(null, payload);
        }
    });
}

function discoverObjectResources(objectUri, callback) {
    async.series([
        apply(objectRegistry.get, objectUri),
        apply(objectRegistry.getAttributes, objectUri)
    ], function handleGetObject(error, results) {
        var obj = results[0],
            attributes = results[1];

        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectUri + '>',
                resources = _.keys(obj.attributes);

            if (attributes) {
                payload += _.pairs(attributes).reduce(addAttribute, '');
            }

            for (var i = 0; i < resources.length; i++) {
                payload += ',<' + objectUri + '/' + resources[i] + '>';
            }

            callback(null, payload);
        }
    });
}

function discoverObjectTypeInstances(objectTypeUri, callback) {
    async.series([
        objectRegistry.list,
        apply(objectRegistry.getAttributes, objectTypeUri)
    ], function handleListResult(error, results) {
        var objList = results[0],
            attributes = results[1];

        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectTypeUri + '>',
                objectType = objectTypeUri.substring(1);

            if (attributes) {
                payload += _.pairs(attributes).reduce(addAttribute, '');
            }

            for (var i = 0; i < objList.length; i++) {
                if (objList[i].objectType === objectType) {
                    payload += ',<' + objList[i].objectUri + '>';
                }
            }

            callback(null, payload);
        }
    });
}

/**
 * Handle the read operation.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 * @param {Function} handler    User handler to be executed if everything goes ok.
 */
function handleRead(req, res, handler) {
    extractUriInfo(req, res, function (error, objectUri, resourceId) {
        if (error) {
            endRead(req, res)(error);
        } else {
            if (_.some(req.options, _.matches({name: 'Accept', value: 'application/link-format'}))) {
                if (resourceId) {
                    discoverResourceAttributes(objectUri, resourceId, endRead(req, res));
                } else if (objectUri.match(/\/\d+\/\d+/)) {
                    discoverObjectResources(objectUri, endRead(req, res));
                } else {
                    discoverObjectTypeInstances(objectUri, endRead(req, res));
                }
            } else {
                async.waterfall([
                    apply(objectRegistry.get, objectUri),
                    apply(createObservers, req, res, resourceId),
                    apply(applyHandler, resourceId, handler)
                ], endRead(req, res));
            }
        }
    });
}

function cancelObservation(objUri, resourceId, callback) {
    if (observers[objUri + '/' + resourceId]) {
        if (observers[objUri + '/' + resourceId].stream) {
            observers[objUri + '/' + resourceId].stream.end();
        }

        if (observers[objUri + '/' + resourceId]) {
            clearInterval(observers[objUri + '/' + resourceId].scheduler);
        }

        objectRegistry.bus.removeListener(objUri, observers[objUri + '/' + resourceId].listener);
        delete observers[objUri + '/' + resourceId];
    } else {
        logger.error('Tried to remove an unexistant observation on obj URI [%s] and resourceId [%s]',
            objUri, resourceId);
    }

    callback(null);
}

function listObservers(callback) {
    callback(null, _.values(observers));
}

function cancelAllObservers(callback) {
    var observers = _.values(observers),
        cancellations = [];

    for (var i = 0; i < observers.length; i++) {
        cancellations.push(apply(cancelObservation, observers[i].objUri, observers[i].resourceId));
    }

    async.series(cancellations, callback);
}

exports.handle = handleRead;
exports.cancel = cancelObservation;
exports.list = listObservers;
exports.cancelAll = cancelAllObservers;<commit_msg>FIX Number payload not transmitted<commit_after>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-lwm2m-lib
 *
 * iotagent-lwm2m-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-lwm2m-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-lwm2m-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    objectRegistry = require('./objectRegistry'),
    errors = require('../../errors'),
    logger = require('logops'),
    _ = require('underscore'),
    observers = {};

/**
 * Extract Object type and id from the request URI, returning it using the callback.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 */
function extractUriInfo(req, res, callback) {
    if (req.urlObj.pathname.match(/\/\d+\/\d+\/\d+/)) {
        var resourceIndex = req.urlObj.pathname.lastIndexOf('/'),
            resourceId = req.urlObj.pathname.substring(resourceIndex + 1),
            objectUri = req.urlObj.pathname.substring(0, resourceIndex);

        callback(null, objectUri, resourceId);
    } else if (req.urlObj.pathname.match(/\/\d+(\/\d+)?/)) {
        callback(null, req.urlObj.pathname, null);
    } else {
        callback(new errors.WrongObjectUri(req.urlObj.pathname));
    }
}

/**
 * Invoke the user handler for this operation, with all the information from the query parameters as its arguments.
 * This method handling gives the client an opportunity to change or overwrite the values before returning them to
 * the client. The handler must call the callback with the overwritten value it wants to return. If the callback is
 * called without parameters, the original value is returned instead.
 *
 * @param {Object} queryParams      Object containing all the query parameters.
 * @param {Function} handler        User handler to be invoked.
 */
function applyHandler(resourceId, handler, storedObject, callback) {
    if (storedObject.attributes[resourceId]) {
        handler(
            storedObject.objectType,
            storedObject.objectId,
            resourceId,
            storedObject.attributes[resourceId],

            function handleReadOverwrite(error, result) {
                if (result) {
                    callback(error, result);
                } else {
                    callback(error, storedObject.attributes[resourceId]);
                }
            });
    } else {
        callback(new errors.ResourceNotFound(storedObject.objectType, storedObject.objectId, resourceId));
    }
}

/**
 * Generates the end of request handler that will generate the final response to the COAP Client.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 * @returns {Function}          Request handler, receiving an optional error and the read operation result.
 */
function endRead(req, res) {
    return function (error, result) {
        var body;

        if (error) {
            res.code = error.code;
            body = '';
        } else {
            res.code = '2.05';
            body = result.toString();
        }

        if (_.some(req.options, _.matches({name: 'Observe'}))) {
            res.setOption('Observe', 1);
            res.write(body);
        } else {
            res.end(body);
        }
    };
}

/**
 * Constructs the resource observer. The observer listens in the object registry bus for attribute modifications,
 * sending a new information update to the server each time the selected attribute changes its value.
 *
 * @param {Object} stream           Stream where the new data will be written to the server.
 * @param {Number} resourceId       Resource to read from.
 * @param {Object} storedObject     Object instance to observe.
 * @returns {{uri: (object.objectUri|*|string), stream: *, observation: number}}
 * @constructor
 */
function ResourceObserver(stream, resourceId, storedObject) {
    var data = {
        id: storedObject.objectUri + '/' + resourceId,
        uri: storedObject.objectUri,
        stream: stream,
        observation: 1,
        listener: function newModification(method, id, value) {
            if (method === 'setResource' && resourceId === id) {
                stream.setOption('Observe', data.observation);
                stream.write(value.toString());
                data.observation++;
                data.lastObservation = Date.now();
            }
        }
    };

    function getLastMeasure() {
        objectRegistry.get(storedObject.objectUri, function (error, obj) {
            if (error) {
                logger.error('Error retrieving the last resource value');
            } else {
                data.listener('setResource', resourceId, obj.attributes[resourceId]);
            }
        });
    }

    objectRegistry.getAttributes(storedObject.objectUri + '/' + resourceId, function(error, attributes) {
        if (error) {
            logger.error('Couldn\'t find attributes for the [%s] resource URI. Falling back to value changed');
        }

        if (attributes) {
            if (attributes.pmax) {
                data.scheduler = setInterval(function handleMaxPeriod() {
                    getLastMeasure(storedObject.objectUri, resourceId);
                }, attributes.pmax);
            }
        } else {
            objectRegistry.bus.on(storedObject.objectUri, data.listener);
        }
    });

    return data;
}

/**
 * If the Read request comes with the observation option, apart from resolving the information request, a subscription
 * has to be created, so every time the selected resource changes in value (or in a timely basis, depending on the
 * configuration), the server is updated on the new value.
 *
 * @param {Object} req              Arriving COAP Request to be handled.
 * @param {Object} res              Outgoing COAP Response.
 * @param {Number} resourceId       ID of the resource to be observed.
 * @param {Object} storedObject     Object instance to be read.
 */
function createObservers(req, res, resourceId, storedObject, callback) {
    if (_.some(req.options, _.matches({name: 'Observe'}))) {
        var observer = new ResourceObserver(res, resourceId, storedObject);
        observers[observer.id] = observer;
    }

    callback(null, storedObject);
}

function addAttribute(previous, value) {
    return previous + ';' + value[0] + '=' + value[1];
}

function discoverResourceAttributes(objectUri, resourceId, callback) {
    objectRegistry.getAttributes(objectUri + '/' + resourceId, function (error, attributes) {
        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectUri + '/' + resourceId + '>';

            if (attributes) {
                payload = _.pairs(attributes).reduce(addAttribute, payload);
            }

            callback(null, payload);
        }
    });
}

function discoverObjectResources(objectUri, callback) {
    async.series([
        apply(objectRegistry.get, objectUri),
        apply(objectRegistry.getAttributes, objectUri)
    ], function handleGetObject(error, results) {
        var obj = results[0],
            attributes = results[1];

        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectUri + '>',
                resources = _.keys(obj.attributes);

            if (attributes) {
                payload += _.pairs(attributes).reduce(addAttribute, '');
            }

            for (var i = 0; i < resources.length; i++) {
                payload += ',<' + objectUri + '/' + resources[i] + '>';
            }

            callback(null, payload);
        }
    });
}

function discoverObjectTypeInstances(objectTypeUri, callback) {
    async.series([
        objectRegistry.list,
        apply(objectRegistry.getAttributes, objectTypeUri)
    ], function handleListResult(error, results) {
        var objList = results[0],
            attributes = results[1];

        if (error) {
            callback(error);
        } else {
            var payload = '<' + objectTypeUri + '>',
                objectType = objectTypeUri.substring(1);

            if (attributes) {
                payload += _.pairs(attributes).reduce(addAttribute, '');
            }

            for (var i = 0; i < objList.length; i++) {
                if (objList[i].objectType === objectType) {
                    payload += ',<' + objList[i].objectUri + '>';
                }
            }

            callback(null, payload);
        }
    });
}

/**
 * Handle the read operation.
 *
 * @param {Object} req          Arriving COAP Request to be handled.
 * @param {Object} res          Outgoing COAP Response.
 * @param {Function} handler    User handler to be executed if everything goes ok.
 */
function handleRead(req, res, handler) {
    extractUriInfo(req, res, function (error, objectUri, resourceId) {
        if (error) {
            endRead(req, res)(error);
        } else {
            if (_.some(req.options, _.matches({name: 'Accept', value: 'application/link-format'}))) {
                if (resourceId) {
                    discoverResourceAttributes(objectUri, resourceId, endRead(req, res));
                } else if (objectUri.match(/\/\d+\/\d+/)) {
                    discoverObjectResources(objectUri, endRead(req, res));
                } else {
                    discoverObjectTypeInstances(objectUri, endRead(req, res));
                }
            } else {
                async.waterfall([
                    apply(objectRegistry.get, objectUri),
                    apply(createObservers, req, res, resourceId),
                    apply(applyHandler, resourceId, handler)
                ], endRead(req, res));
            }
        }
    });
}

function cancelObservation(objUri, resourceId, callback) {
    if (observers[objUri + '/' + resourceId]) {
        if (observers[objUri + '/' + resourceId].stream) {
            observers[objUri + '/' + resourceId].stream.end();
        }

        if (observers[objUri + '/' + resourceId]) {
            clearInterval(observers[objUri + '/' + resourceId].scheduler);
        }

        objectRegistry.bus.removeListener(objUri, observers[objUri + '/' + resourceId].listener);
        delete observers[objUri + '/' + resourceId];
    } else {
        logger.error('Tried to remove an unexistant observation on obj URI [%s] and resourceId [%s]',
            objUri, resourceId);
    }

    callback(null);
}

function listObservers(callback) {
    callback(null, _.values(observers));
}

function cancelAllObservers(callback) {
    var observers = _.values(observers),
        cancellations = [];

    for (var i = 0; i < observers.length; i++) {
        cancellations.push(apply(cancelObservation, observers[i].objUri, observers[i].resourceId));
    }

    async.series(cancellations, callback);
}

exports.handle = handleRead;
exports.cancel = cancelObservation;
exports.list = listObservers;
exports.cancelAll = cancelAllObservers;<|endoftext|>


<commit_before>/*eslint no-process-env:0 no-param-reassign:0*/

'use strict';

var util = require('util');
var winston = require('winston');
var winex = require('winex');
var config = require('config').get('logging');

var sysLogLevels = {
  levels: {
    emerg: 0,
    alert: 1,
    crit: 2,
    error: 3,
    warning: 4,
    notice: 5,
    info: 6,
    debug: 7
  }
};

// Transports are defined in configs as:
//
// logging: {
//   transports: [
//     {
//       Console: {
//         level: 'info',
//         json: true,
//         prettyPrint: true
//       }
//     },
//     {
//       ...
//     }
//   ]
// }
//
var logTransports = config.transports.map(function(t) {
  var cls = Object.keys(t)[0];
  var opts = t[cls];
  var Transport;
  if (cls === 'Loggly') {
    require('winston-loggly');
  }
  Transport = winston.transports[cls];
  return new Transport(opts);
});

var winstonOpts = {
  levels: sysLogLevels.levels,
  transports: logTransports
};

var winstonLog = new winston.Logger(winstonOpts);
var doNop = {nop: !!(process.env.NODE_ENV === 'test')};
var Log = winex.factory(winstonLog, {}, doNop);

function _logger(level) {
  return function(message, meta, error) {
    var log = new Log();

    if (util.isError(meta)) {
      error = meta;
      meta = null;
    }

    if (meta) {
      log.addMeta(meta);
    }

    if (error) {
      log.addError(error);
    }

    log[level](message);
  };
}

module.exports = {
  Log: Log
};

Object.keys(winstonOpts.levels).forEach(function (level) {
  module.exports[level] = _logger(level);
});
<commit_msg>Remove dependencies on config<commit_after>/*eslint no-process-env:0 no-param-reassign:0*/

'use strict';

var util = require('util');
var winston = require('winston');
var winex = require('winex');

var sysLogLevels = {
  levels: {
    emerg: 0,
    alert: 1,
    crit: 2,
    error: 3,
    warning: 4,
    notice: 5,
    info: 6,
    debug: 7
  }
};

var loggingModule = {
  Log: null,
};

// Transports are defined in configs as:
//
// logging: {
//   transports:
// }
//

/**
 * Setup loggings
 *
 * @param {Object}   options            Options
 * @param {Object[]} options.transports Should look like:
 *                                      [
 *                                        {
 *                                          Console: {
 *                                            level: 'info',
 *                                            json: true,
 *                                            prettyPrint: true
 *                                          }
 *                                        },
 *                                        {
 *                                          ...
 *                                        }
 *                                      ]
 *
 * @return {void}
 */
loggingModule.config = function (options) {
  var logTransports;
  var winstonOpts;
  var winstonLog;
  var doNop;

  if (loggingModule.Log) {
    throw new Error('config cannot be called twice');
  }

  logTransports = options.transports.map(function(t) {
    var cls = Object.keys(t)[0];
    var opts = t[cls];
    var Transport;
    if (cls === 'Loggly') {
      require('winston-loggly');
    }
    Transport = winston.transports[cls];
    return new Transport(opts);
  });

  winstonOpts = {
    levels: sysLogLevels.levels,
    transports: logTransports
  };

  winstonLog = new winston.Logger(winstonOpts);
  doNop = {nop: !!(process.env.NODE_ENV === 'test')};
  loggingModule.Log = winex.factory(winstonLog, {}, doNop);
};

function _logger(level) {
  return function(message, meta, error) {
    var log = new loggingModule.Log();

    if (util.isError(meta)) {
      error = meta;
      meta = null;
    }

    if (meta) {
      log.addMeta(meta);
    }

    if (error) {
      log.addError(error);
    }

    log[level](message);
  };
}

Object.keys(sysLogLevels.levels).forEach(function (level) {
  loggingModule[level] = _logger(level);
});

module.exports = loggingModule;
<|endoftext|>


<commit_before>'use strict';

// Modules
var path = require('path');
var fs = require('fs');
var _ = require('lodash');

// "Constants"
var PLUGIN_NAME = 'kalabox-plugin-drush';

module.exports = function(argv, app, events, engine, tasks) {

  // Helpers
  /**
   * Gets plugin conf from the appconfig or from CLI arg
   **/
  var getOpts = function() {
    // Grab our options from config
    var opts = app.config.pluginConf[PLUGIN_NAME];

    // Override any config coming in on the CLI
    for (var key in opts) {
      if (argv[key]) {
        opts[key] = argv[key];
      }
    }
    return opts;
  };

  /**
   * Returns an arrayed set of drush-ready commands
   **/
  var getCmd = function() {
    // @todo: not sure if the command structure is different on D7 vs D6
    // Grab our options from config so we can filter these out
    var cmd = argv._;
    delete argv._;
    var strip = app.config.pluginConf[PLUGIN_NAME];

    // Remove
    for (var key in strip) {
      delete argv[key];
    }

    for (var opt in argv) {
      cmd.push('--' + opt + '=' + argv[opt]);
    }

    return cmd;
  };

  /**
   * Create a symlink from the local.aliases.drushrc.php file to ~/.drush/kalabox/<appname>.aliases.drushrc.php
   **/
  var copyLocalAlias = function() {
    var home =
      process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
    var drushPath = path.resolve(home, '.drush');
    // Create ~/.drush dir if it doesn't exist.
    if (!fs.existsSync(drushPath)) {
      fs.mkdirSync(drushPath);
    }

    // Define the paths
    var src = path.resolve(
      app.root, 'config', 'drush', 'local.aliases.drushrc.php'
    );
    var dst = path.resolve(drushPath, app.domain + '.aliases.drushrc.php');

    // Create the symlink
    if (process.platform !== 'win32') {
      if (!fs.existsSync(dst)) {
        fs.symlinkSync(src, dst);
      }
    }
  };

  /**
   * Some drupal sites dont use settings.php and drush will fail without this
   * @todo : there should be a way for the pressflow plugin to handle this?
   */
  var getPressflowSettings = function() {
    var pressflowSettings = {
      databases: {
        default: {
          default: {
            driver: 'mysql',
            prefix: '',
            database: 'kalabox',
            username: 'kalabox',
            password: '',
            host: app.domain,
            port: 3306,
          }
        }
      },
      conf: {
        'pressflow_smart_start': 1
      }
    };
    return JSON.stringify(pressflowSettings);
  };

  /**
   * Runs a git command on the app data container
   **/
  var runDrushCMD = function(cmd, opts, done) {
    // @todo: needs to come from a DEEPER PLACE
    engine.run(
      'kalabox/drush:stable',
      cmd,
      process.stdout,
      {
        Env: [
          'DRUSH_VERSION=' + opts['drush-version'],
          'APPNAME=' +  app.name,
          'APPDOMAIN=' +  app.domain,
          'PRESSFLOW_SETTINGS=' + getPressflowSettings()
        ],
        HostConfig: {
          VolumesFrom: [app.dataContainerName]
        }
      },
      {
        Binds: [
          app.config.homeBind + ':/ssh:rw',
          app.rootBind + ':/src:rw'
        ]
      },
      done
    );
  };

  // Events
  // Install the util container for our things
  events.on('post-install', function(app, done) {
    engine.build({name: 'kalabox/drush:stable'}, done);
  });

  // Updates kalabox aliases when app is started.
  // This allows for both kbox drush to be used
  // and local drush to be used via: drush @<appname> status
  events.on('post-start-component', function(component, done) {
    // Only run on the db container
    if (component.name !== 'db') {
      done();
    }
    else {
      engine.inspect(component.containerId, function(err, data) {
        var key = '3306/tcp';
        if (data && data.NetworkSettings.Ports[key]) {
          var port = data.NetworkSettings.Ports[key][0].HostPort;
          var commands = [
            [
              'sed',
              '-i',
              's/\'host\'.*/\'host\' => \'' + app.domain + '\',/g',
              '/src/config/drush/aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's/\'uri\'.*/\'uri\' => \'' + app.domain + '\',/g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's/aliases\\[\'.*/aliases[\'' + app.name + '\'] = array(/g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's@\'root\'.*@\'root\' => \'' +
              path.join(app.config.codeRoot, app.name) + '\',@g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's%\'db-url.*%\'db-url\' => \'mysql://kalabox@' + app.domain +
              ':' + port + '/kalabox\',%g',
              '/src/config/drush/local.aliases.drushrc.php'
            ]
          ];

          _.map(commands, function(cmd) {
            engine.run(
              'kalabox/debian:stable',
              cmd,
              process.stdout,
              {
                'Env': ['APPDOMAIN=' + app.domain]
              },
              {
                Binds: [app.rootBind + ':/src:rw']
              },
              function(err) {
                if (err) {
                  console.log(err);
                }
              }
            );
          });
          copyLocalAlias();
        }
        done();
      });
    }
  });

  // Tasks
  // drush wrapper: kbox drush COMMAND
  tasks.registerTask([app.name, 'drush'], function(done) {
    var opts = getOpts();
    var cmd = getCmd();
    cmd.unshift('@dev');
    runDrushCMD(cmd, opts, done);
  });

};
<commit_msg>#304: Update for new engine run<commit_after>'use strict';

// Modules
var path = require('path');
var fs = require('fs');
var _ = require('lodash');

// "Constants"
var PLUGIN_NAME = 'kalabox-plugin-drush';

module.exports = function(argv, app, events, engine, tasks) {

  // Helpers
  /**
   * Gets plugin conf from the appconfig or from CLI arg
   **/
  var getOpts = function() {
    // Grab our options from config
    var opts = app.config.pluginConf[PLUGIN_NAME];

    // Override any config coming in on the CLI
    for (var key in opts) {
      if (argv[key]) {
        opts[key] = argv[key];
      }
    }
    return opts;
  };

  /**
   * Some drupal sites dont use settings.php and drush will fail without this
   * @todo : there should be a way for the pressflow plugin to handle this?
   */
  var getPressflowSettings = function() {
    var pressflowSettings = {
      databases: {
        default: {
          default: {
            driver: 'mysql',
            prefix: '',
            database: 'kalabox',
            username: 'kalabox',
            password: '',
            host: app.domain,
            port: 3306,
          }
        }
      },
      conf: {
        'pressflow_smart_start': 1
      }
    };
    return JSON.stringify(pressflowSettings);
  };

  /**
   * Runs a git command on the app data container
   **/
  var runDrushCMD = function(cmd, opts, done) {
    // @todo: needs to come from a DEEPER PLACE
    engine.run(
      'kalabox/drush:stable',
      cmd,
      {
        Env: [
          'DRUSH_VERSION=' + opts['drush-version'],
          'APPNAME=' +  app.name,
          'APPDOMAIN=' +  app.domain,
          'PRESSFLOW_SETTINGS=' + getPressflowSettings()
        ],
        HostConfig: {
          VolumesFrom: [app.dataContainerName]
        }
      },
      {
        Binds: [
          app.config.homeBind + ':/ssh:rw',
          app.rootBind + ':/src:rw'
        ]
      },
      done
    );
  };

  /**
   * Returns an arrayed set of drush-ready commands
   **/
  var getCmd = function() {
    // @todo: not sure if the command structure is different on D7 vs D6
    // Grab our options from config so we can filter these out
    var cmd = argv._;
    delete argv._;
    var strip = app.config.pluginConf[PLUGIN_NAME];

    // Remove
    for (var key in strip) {
      delete argv[key];
    }

    for (var opt in argv) {
      cmd.push('--' + opt + '=' + argv[opt]);
    }

    return cmd;
  };

  /**
   * Create a symlink from the local.aliases.drushrc.php file to ~/.drush/kalabox/<appname>.aliases.drushrc.php
   **/
  var copyLocalAlias = function() {
    var home =
      process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
    var drushPath = path.resolve(home, '.drush');
    // Create ~/.drush dir if it doesn't exist.
    if (!fs.existsSync(drushPath)) {
      fs.mkdirSync(drushPath);
    }

    // Define the paths
    var src = path.resolve(
      app.root, 'config', 'drush', 'local.aliases.drushrc.php'
    );
    var dst = path.resolve(drushPath, app.domain + '.aliases.drushrc.php');

    // Create the symlink
    if (process.platform !== 'win32') {
      if (!fs.existsSync(dst) && fs.existsSync(src)) {
        fs.symlinkSync(src, dst);
      }
    }
  };

  // Events
  // Install the util container for our things
  events.on('post-install', function(app, done) {
    engine.build({name: 'kalabox/drush:stable'}, done);
  });

  // Updates kalabox aliases when app is started.
  // This allows for both kbox drush to be used
  // and local drush to be used via: drush @<appname> status
  events.on('post-start-component', function(component, done) {
    // Only run on the db container
    if (component.name !== 'db') {
      done();
    }
    else {
      engine.inspect(component.containerId, function(err, data) {
        var key = '3306/tcp';
        if (data && data.NetworkSettings.Ports[key]) {
          var port = data.NetworkSettings.Ports[key][0].HostPort;
          var commands = [
            [
              'sed',
              '-i',
              's/\'host\'.*/\'host\' => \'' + app.domain + '\',/g',
              '/src/config/drush/aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's/\'uri\'.*/\'uri\' => \'' + app.domain + '\',/g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's/aliases\\[\'.*/aliases[\'' + app.name + '\'] = array(/g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's@\'root\'.*@\'root\' => \'' +
              path.join(app.config.codeRoot, app.name) + '\',@g',
              '/src/config/drush/local.aliases.drushrc.php'
            ],
            [
              'sed',
              '-i',
              's%\'db-url.*%\'db-url\' => \'mysql://kalabox@' + app.domain +
              ':' + port + '/kalabox\',%g',
              '/src/config/drush/local.aliases.drushrc.php'
            ]
          ];

          _.map(commands, function(cmd) {
            engine.run(
              'kalabox/debian:stable',
              cmd,
              {
                'Env': ['APPDOMAIN=' + app.domain]
              },
              {
                Binds: [app.rootBind + ':/src:rw']
              },
              function(err) {
                if (err) {
                  console.log(err);
                }
              }
            );
          });
          copyLocalAlias();
        }
        done();
      });
    }
  });

  // Tasks
  // drush wrapper: kbox drush COMMAND
  tasks.registerTask([app.name, 'drush'], function(done) {
    var opts = getOpts();
    var cmd = getCmd();
    cmd.unshift('@dev');
    runDrushCMD(cmd, opts, done);
  });

};
<|endoftext|>


<commit_before>// Malarkey Dependecy
// v1.3.0
// https://github.com/yuanqing/malarkey

(function(root) {

  'use strict';

  var STOPPED  = 0;
  var STOPPING = 1;
  var RUNNING  = 2;

  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  function noop() {}

  function Malarkey(elem, opts) {

    // allow `Malarkey` to be called without the `new` keyword
    var self = this;
    if (!(self instanceof Malarkey)) {
      return new Malarkey(elem, opts);
    }

    // default `opts`
    opts = opts || {};
    var loop = opts.loop;
    var typeSpeed = opts.speed || opts.typeSpeed || 50;
    var deleteSpeed = opts.speed || opts.deleteSpeed || 50;
    var pauseDelay = opts.delay || opts.pauseDelay || 2000;
    var postfix = opts.postfix || '';
    var getter = opts.getter || function(elem) {
      return elem.innerHTML;
    };
    var setter = opts.setter || function(elem, val) {
      elem.innerHTML = val;
    };

    // the function queue
    var fnQueue = [];
    var argsQueue = [];
    var i = -1;
    var state = STOPPED;
    var pauseCb = noop;
    function enqueue(fn, args) {
      fnQueue.push(fn);
      argsQueue.push(args);
      if (state != RUNNING) {
        state = RUNNING;
        // wait for the remaining functions to be enqueued
        setTimeout(function() {
          next();
        }, 0);
      }
      return self;
    }
    function next() {
      if (state != RUNNING) {
        state = STOPPED;
        pauseCb(elem);
        pauseCb = noop;
        return;
      }
      if (++i == fnQueue.length) {
        if (!loop) {
          state = STOPPED;
          return;
        }
        i = 0;
      }
      fnQueue[i].apply(null, [].concat(next, argsQueue[i]));
    }

    // internal functions that are `enqueued` via the respective public methods
    function _type(cb, str, speed) {
      var len = str.length;
      if (!len) {
        return cb();
      }
      (function t(i) {
        setTimeout(function() {
          setter(elem, getter(elem) + str[i]);
          i += 1;
          if (i < len) {
            t(i);
          } else {
            cb();
          }
        }, speed);
      })(0);
    }
    function _delete(cb, x, speed) {
      var curr = getter(elem);
      var count = curr.length; // default to deleting entire contents of `elem`
      if (x != null) {
        if (typeof x == 'string') {
          // delete the string `x` if and only if `elem` ends with `x`
          if (endsWith(curr, x + postfix)) {
            count = x.length + postfix.length;
          } else {
            count = 0;
          }
        } else {
          // delete the last `x` characters from `elem`
          if (x > -1) {
            count = Math.min(x, count);
          }
        }
      }
      if (!count) {
        return cb();
      }
      (function d(count) {
        setTimeout(function() {
          var curr = getter(elem);
          if (count) {
            // drop last char
            setter(elem, curr.substring(0, curr.length-1));
            d(count - 1);
          } else {
            cb();
          }
        }, speed);
      })(count);
    }
    function _clear(cb) {
      setter(elem, '');
      cb();
    }
    function _call(cb, fn) {
      fn.call(cb, elem);
    }

    // expose the public methods
    self.type = function(str, speed) {
      return enqueue(_type, [str + postfix, speed || typeSpeed]);
    };
    self.delete = function(x, speed) {
      return enqueue(_delete, [x, speed || deleteSpeed]);
    };
    self.clear = function() {
      return enqueue(_clear);
    };
    self.pause = function(delay) {
      return enqueue(setTimeout, [delay || pauseDelay]);
    };
    self.call = function(fn) {
      return enqueue(_call, [fn]);
    };
    self.triggerPause = function(cb) {
      state = STOPPING;
      pauseCb = cb || noop;
      return self;
    };
    self.triggerResume = function() {
      if (state != RUNNING) { // ie. `STOPPED` or `STOPPING`
        var prevState = state;
        state = RUNNING;
        if (prevState == STOPPED) {
          next();
        }
      }
      return self;
    };
    self.isRunning = function() {
      return state != STOPPED; // ie. `RUNNING` or `STOPPING`
    };

  }

  /* istanbul ignore else */
  if (typeof module == 'object') {
    module.exports = Malarkey;
  } else {
    root.malarkey = Malarkey;
  }
})(this);

(function(root){
	'use strict';

	function qna(container, snippetNodes, snippets, options ) {
		var self = { };

		// DOM Elements
		var fContainerNode;
		var fSnippetNodes;
		var fFormNode;

		var fSnippets;
		var fResponder;
		var fAnswer

		init( );

		// Interface
		self.ask     = ask;
		self.respond = respond;
		self.answer  = answer;

		return self;

		function init ( ) {

			options = options || { };

			fContainerNode = getNode(document, container);
			fSnippetNodes  = getNodes(fContainerNode, snippetNodes);

			fSnippets  = snippets;

			fResponder = options.responder;
			fFormNode  = getNode(fContainerNode, options.form);
			fAnswer    = options.answer;

			if (fFormNode !== undefined && fFormNode !== null) {
				captureFormEvent(fFormNode, answerOrRespond);
			}

		}

		// Public Methods

		function ask ( callback ) {

			var nodes    = Array.prototype.slice.call(fSnippetNodes);
			var snippets = Array.prototype.slice.call(fSnippets);

			clearNodes();

			type(nodes, snippets, callback);
		}

		function respond ( ) {
			var args     = Array.prototype.slice.call(arguments);
			var response = fResponder.apply(this, args);

			fSnippets = response;
			ask( );
		}

		function answer (answer) {
			fAnswer = answer;
		}

		// Private Methods
		function clearNodes ( ) {
			for ( var i = 0; i < fSnippetNodes.length; i++) {
				var node = fSnippetNodes[i];
				var m = malarkey(node);
				m.clear();
			}
		}

		function type (nodes, snippets, callback) {

			if ( nodes.length === 0 || snippets.length === 0 ) {
				if (callback !== undefined ) { callback(); }
				return;
			}

			var node   = nodes.shift();
			var snippet = snippets.shift();

			var options = { };

			options.pauseDelay = snippet.pauseDelay || 750;
			options.typeSpeed  = snippet.typeSpeed  || 50;

			var next = function () {
				type(nodes, snippets, callback);
			};

			var addIsTypingClass = function (el) {
				el.classList.add('is-typing');
			    this();
			};

			var removeIsTypingClass = function (el) {
				el.classList.add('is-typing');
			    this();
			};

			var typeWriter = malarkey(node, options);

			typeWriter.call(addIsTypingClass);
			typeWriter.type(snippet.str);
			typeWriter.pause();
			typeWriter.call(removeIsTypingClass);
			typeWriter.call(next);
		}

		function answerOrRespond (event) {
			if ( fAnswer !== undefined ) {
				fAnswer.respond(event, fFormNode);
			} else if (fResponder !== undefined) {
				responed(event, fFormNode);
			}
		}

		function captureFormEvent (formNode, callback) {

			if ( formNode.addEventListener !== undefined ) {
			    formNode.addEventListener("submit", callback, false); //Modern browsers
			} else if ( formNode.attachEvent !== undefined ){
				formNode.attachEvent('onsubmit', callback); //Old IE
			}
		}

		function getNodes (container, node) {
			if (typeof node === 'string') {
				return container.querySelectorAll(node);
			}

			return node;
		}

		function getNode (container, node) {
			if (typeof node === 'string') {
				return container.querySelector(node);
			}

			return node;
		}

	}

	if (typeof module == 'object') {
		module.exports = qna;
	} else {
		root.qna = qna;
	}
})(this);<commit_msg>remove typing class bug<commit_after>// Malarkey Dependecy
// v1.3.0
// https://github.com/yuanqing/malarkey

(function(root) {

  'use strict';

  var STOPPED  = 0;
  var STOPPING = 1;
  var RUNNING  = 2;

  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  function noop() {}

  function Malarkey(elem, opts) {

    // allow `Malarkey` to be called without the `new` keyword
    var self = this;
    if (!(self instanceof Malarkey)) {
      return new Malarkey(elem, opts);
    }

    // default `opts`
    opts = opts || {};
    var loop = opts.loop;
    var typeSpeed = opts.speed || opts.typeSpeed || 50;
    var deleteSpeed = opts.speed || opts.deleteSpeed || 50;
    var pauseDelay = opts.delay || opts.pauseDelay || 2000;
    var postfix = opts.postfix || '';
    var getter = opts.getter || function(elem) {
      return elem.innerHTML;
    };
    var setter = opts.setter || function(elem, val) {
      elem.innerHTML = val;
    };

    // the function queue
    var fnQueue = [];
    var argsQueue = [];
    var i = -1;
    var state = STOPPED;
    var pauseCb = noop;
    function enqueue(fn, args) {
      fnQueue.push(fn);
      argsQueue.push(args);
      if (state != RUNNING) {
        state = RUNNING;
        // wait for the remaining functions to be enqueued
        setTimeout(function() {
          next();
        }, 0);
      }
      return self;
    }
    function next() {
      if (state != RUNNING) {
        state = STOPPED;
        pauseCb(elem);
        pauseCb = noop;
        return;
      }
      if (++i == fnQueue.length) {
        if (!loop) {
          state = STOPPED;
          return;
        }
        i = 0;
      }
      fnQueue[i].apply(null, [].concat(next, argsQueue[i]));
    }

    // internal functions that are `enqueued` via the respective public methods
    function _type(cb, str, speed) {
      var len = str.length;
      if (!len) {
        return cb();
      }
      (function t(i) {
        setTimeout(function() {
          setter(elem, getter(elem) + str[i]);
          i += 1;
          if (i < len) {
            t(i);
          } else {
            cb();
          }
        }, speed);
      })(0);
    }
    function _delete(cb, x, speed) {
      var curr = getter(elem);
      var count = curr.length; // default to deleting entire contents of `elem`
      if (x != null) {
        if (typeof x == 'string') {
          // delete the string `x` if and only if `elem` ends with `x`
          if (endsWith(curr, x + postfix)) {
            count = x.length + postfix.length;
          } else {
            count = 0;
          }
        } else {
          // delete the last `x` characters from `elem`
          if (x > -1) {
            count = Math.min(x, count);
          }
        }
      }
      if (!count) {
        return cb();
      }
      (function d(count) {
        setTimeout(function() {
          var curr = getter(elem);
          if (count) {
            // drop last char
            setter(elem, curr.substring(0, curr.length-1));
            d(count - 1);
          } else {
            cb();
          }
        }, speed);
      })(count);
    }
    function _clear(cb) {
      setter(elem, '');
      cb();
    }
    function _call(cb, fn) {
      fn.call(cb, elem);
    }

    // expose the public methods
    self.type = function(str, speed) {
      return enqueue(_type, [str + postfix, speed || typeSpeed]);
    };
    self.delete = function(x, speed) {
      return enqueue(_delete, [x, speed || deleteSpeed]);
    };
    self.clear = function() {
      return enqueue(_clear);
    };
    self.pause = function(delay) {
      return enqueue(setTimeout, [delay || pauseDelay]);
    };
    self.call = function(fn) {
      return enqueue(_call, [fn]);
    };
    self.triggerPause = function(cb) {
      state = STOPPING;
      pauseCb = cb || noop;
      return self;
    };
    self.triggerResume = function() {
      if (state != RUNNING) { // ie. `STOPPED` or `STOPPING`
        var prevState = state;
        state = RUNNING;
        if (prevState == STOPPED) {
          next();
        }
      }
      return self;
    };
    self.isRunning = function() {
      return state != STOPPED; // ie. `RUNNING` or `STOPPING`
    };

  }

  /* istanbul ignore else */
  if (typeof module == 'object') {
    module.exports = Malarkey;
  } else {
    root.malarkey = Malarkey;
  }
})(this);

(function(root){
	'use strict';

	function qna(container, snippetNodes, snippets, options ) {
		var self = { };

		// DOM Elements
		var fContainerNode;
		var fSnippetNodes;
		var fFormNode;

		var fSnippets;
		var fResponder;
		var fAnswer

		init( );

		// Interface
		self.ask     = ask;
		self.respond = respond;
		self.answer  = answer;

		return self;

		function init ( ) {

			options = options || { };

			fContainerNode = getNode(document, container);
			fSnippetNodes  = getNodes(fContainerNode, snippetNodes);

			fSnippets  = snippets;

			fResponder = options.responder;
			fFormNode  = getNode(fContainerNode, options.form);
			fAnswer    = options.answer;

			if (fFormNode !== undefined && fFormNode !== null) {
				captureFormEvent(fFormNode, answerOrRespond);
			}

		}

		// Public Methods

		function ask ( callback ) {

			var nodes    = Array.prototype.slice.call(fSnippetNodes);
			var snippets = Array.prototype.slice.call(fSnippets);

			clearNodes();

			type(nodes, snippets, callback);
		}

		function respond ( ) {
			var args     = Array.prototype.slice.call(arguments);
			var response = fResponder.apply(this, args);

			fSnippets = response;
			ask( );
		}

		function answer (answer) {
			fAnswer = answer;
		}

		// Private Methods
		function clearNodes ( ) {
			for ( var i = 0; i < fSnippetNodes.length; i++) {
				var node = fSnippetNodes[i];
				var m = malarkey(node);
				m.clear();
			}
		}

		function type (nodes, snippets, callback) {

			if ( nodes.length === 0 || snippets.length === 0 ) {
				if (callback !== undefined ) { callback(); }
				return;
			}

			var node   = nodes.shift();
			var snippet = snippets.shift();

			var options = { };

			options.pauseDelay = snippet.pauseDelay || 750;
			options.typeSpeed  = snippet.typeSpeed  || 50;

			var next = function () {
				type(nodes, snippets, callback);
			};

			var addIsTypingClass = function (el) {
				el.classList.add('is-typing');
			    this();
			};

			var removeIsTypingClass = function (el) {
				el.classList.remove('is-typing');
			    this();
			};

			var typeWriter = malarkey(node, options);

			typeWriter.call(addIsTypingClass);
			typeWriter.type(snippet.str);
			typeWriter.pause();
			typeWriter.call(removeIsTypingClass);
			typeWriter.call(next);
		}

		function answerOrRespond (event) {
			if ( fAnswer !== undefined ) {
				fAnswer.respond(event, fFormNode);
			} else if (fResponder !== undefined) {
				responed(event, fFormNode);
			}
		}

		function captureFormEvent (formNode, callback) {

			if ( formNode.addEventListener !== undefined ) {
			    formNode.addEventListener("submit", callback, false); //Modern browsers
			} else if ( formNode.attachEvent !== undefined ){
				formNode.attachEvent('onsubmit', callback); //Old IE
			}
		}

		function getNodes (container, node) {
			if (typeof node === 'string') {
				return container.querySelectorAll(node);
			}

			return node;
		}

		function getNode (container, node) {
			if (typeof node === 'string') {
				return container.querySelector(node);
			}

			return node;
		}

	}

	if (typeof module == 'object') {
		module.exports = qna;
	} else {
		root.qna = qna;
	}
})(this);<|endoftext|>


<commit_before>var localForage = require('localforage');
var asyncEach = require('async-each');
var type = require('component-type');

/**
 * Setup `localForage`.
 */

localForage.config({
  name: 'storage'
});

/**
 * Expose `storage`.
 */

module.exports = storage;

/**
 * Functional proxy to get/set/del methods.
 *
 * @param {String|Array|Object} key
 * @param {Mixed|Null} val
 * @param {Function} cb
 */

function storage(key, val, cb) {
  switch (arguments.length) {
    case 3: return val === null
      ? del(key, cb)
      : set(key, val, cb);
    case 2: return type(key) == 'object'
      ? set(key, val)
      : get(key, val);
    default:
      throw new TypeError('Not valid arguments length');
  }
}

/**
 * Expose methods.
 */

storage.forage = localForage;
storage.get = get;
storage.set = set;
storage.del = del;
storage.count = count;
storage.clear = clear;

/**
 * Get `key`.
 *
 * @param {Array|Mixed} key
 * @param {Function} cb
 */

function get(key, cb) {
  type(key) != 'array'
    ? localForage.getItem(key).then(wrap(cb, true), cb)
    : asyncEach(key, get, cb);
}

/**
 * Set `val` to `key`.
 *
 * @param {Array|Mixed} key
 * @param {Mixed} val
 * @param {Function} cb
 */

function set(key, val, cb) {
  type(key) != 'object'
    ? localForage.setItem(key, val).then(wrap(cb), cb)
    : asyncEach(Object.keys(key), function(subkey, next) {
        set(subkey, key[subkey], next);
      }, val);
}

/**
 * Delete `key`.
 *
 * @param {Array|Mixed} key
 * @param {Function} cb
 */

function del(key, cb) {
  type(key) != 'array'
    ? localForage.removeItem(key).then(wrap(cb), cb)
    : asyncEach(key, del, cb);
}

/**
 * Clear.
 *
 * @param {Function} cb
 */

function clear(cb) {
  localForage.clear().then(wrap(cb), cb);
}

/**
 * Get records count.
 *
 * @param {Functionc} cb
 */

function count(cb) {
  localForage.length().then(wrap(cb, true), cb);
}

/**
 * Wrap promise style response to callback style.
 * If `cb` does not specified use console.log to display result.
 *
 * @param {Function} cb
 * @param {Boolean} [hasResult]
 * @return {Function} cb
 */

function wrap(cb, hasResult) {
  return function(res) {
    hasResult
      ? type(cb) == 'function' ? cb(null, res) : console.log(res)
      : cb();
  };
}
<commit_msg>add error wrapper<commit_after>var localForage = require('localforage');
var asyncEach = require('async-each');
var type = require('component-type');

/**
 * Setup `localForage`.
 */

localForage.config({
  name: 'storage'
});

/**
 * Expose `storage`.
 */

module.exports = storage;

/**
 * Functional proxy to get/set/del methods.
 *
 * @param {String|Array|Object} key
 * @param {Mixed|Null} val
 * @param {Function} cb
 */

function storage(key, val, cb) {
  switch (arguments.length) {
    case 3: return val === null
      ? del(key, cb)
      : set(key, val, cb);
    case 2: return type(key) == 'object'
      ? set(key, val)
      : get(key, val);
    default:
      throw new TypeError('Not valid arguments length');
  }
}

/**
 * Expose methods.
 */

storage.forage = localForage;
storage.get = get;
storage.set = set;
storage.del = del;
storage.count = count;
storage.clear = clear;

/**
 * Get `key`.
 *
 * @param {Array|Mixed} key
 * @param {Function} cb
 */

function get(key, cb) {
  type(key) != 'array'
    ? localForage.getItem(key).then(wrapSuccess(cb, true), wrapError(cb))
    : asyncEach(key, get, function(err, res) {
        !err ? wrapSuccess(cb, true)(res) : wrapError(cb)(err);
      });
}

/**
 * Set `val` to `key`.
 *
 * @param {Array|Mixed} key
 * @param {Mixed} val
 * @param {Function} cb
 */

function set(key, val, cb) {
  type(key) != 'object'
    ? localForage.setItem(key, val).then(wrapSuccess(cb), wrapError(cb))
    : asyncEach(Object.keys(key), function(subkey, next) {
        set(subkey, key[subkey], next);
      }, val);
}

/**
 * Delete `key`.
 *
 * @param {Array|Mixed} key
 * @param {Function} cb
 */

function del(key, cb) {
  type(key) != 'array'
    ? localForage.removeItem(key).then(wrapSuccess(cb), wrapError(cb))
    : asyncEach(key, del, cb);
}

/**
 * Clear.
 *
 * @param {Function} cb
 */

function clear(cb) {
  localForage.clear().then(wrapSuccess(cb), wrapError(cb));
}

/**
 * Count records.
 *
 * @param {Functionc} cb
 */

function count(cb) {
  localForage.length().then(wrapSuccess(cb, true), wrapError(cb));
}

/**
 * Wrap promise style response to callback style.
 * If `cb` does not specified use console.log to display result.
 *
 * @param {Function} cb
 * @param {Boolean} [hasResult]
 * @return {Function}
 */

function wrapSuccess(cb, hasResult) {
  return function(res) {
    if (hasResult) type(cb) == 'function' ? cb(null, res) : console.log(res);
    else if (type(cb) == 'function') cb();
  };
}

/**
 * Wrap error callback, and throw err, when it's missing.
 *
 * @param {Function} cb
 * @return {Function}
 */

function wrapError(cb) {
  return function(err) {
    if (type(cb) == 'function') cb(err);
    else throw err;
  };
}
<|endoftext|>


<commit_before>'use strict'

module.exports = function SQL (template) {
    var args = Array.from(arguments).slice(1)
    var sql = ''
    var values = []

    for (var i = 0, stringsLength = template.length, argsLength = args.length; i < stringsLength; i++) {
        sql += template[i]
        if (typeof args[i] === 'object' && args[i] !== null && args[i].raw) {
            sql += args[i].value
        } else if (i < argsLength) {
            values.push(args[i])
            sql += '?'
        }
    }

    return [ sql, values ]
}
<commit_msg>clean up code<commit_after>'use strict'

module.exports = function SQL (template) {
    var args = Array.prototype.slice.call(arguments, 1)
    var sql = ''
    var values = []
    var i = 0
    var templateLength = template.length
    var argsLength = args.length

    for (i; i < templateLength; i++) {
        sql += template[i]

        if (typeof args[i] === 'object' && args[i] !== null) {
            sql += args[i].value
        } else if (i < argsLength) {
            values.push(args[i])
            sql += '?'
        }
    }

    return [ sql, values ]
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @license
 * (c) 2014 Cluster Labs, Inc. https://cluster.co/
 * License: MIT
 */

/** @ignore */
var iter = skit.platform.iter;

/**
 * A simple PubSub for publishing and subscribing to notifications by name.
 *
 * @class
 */
var PubSub = function() {
  this.listeners_ = [];
  this.listenersById_ = {};
  this.lastId_ = 0;
};

var shared_;

/**
 * @return {PubSub} A shared instance of PubSub for all to enjoy.
 */
PubSub.sharedPubSub = function() {
  if (!shared_) {
    shared_ = new PubSub();
  }
  return shared_;
};

/**
 * Subscribe for notifications for a given event name.
 *
 * @param {string} eventName The name of the event you want to be notified
 *     about.
 * @param {function} callback The callback function to call when the
 *     specified event happens.
 * @param {Object=} opt_context The context to call the callback function
 *     in when the event happens.
 * @return {number} A listener ID, which can later be used to unsubscibe.
 */
PubSub.prototype.subscribe = function(eventName, callback, opt_context) {
  if (!(eventName in this.listeners_)) {
    this.listeners_[eventName] = [];
  }

  var id = this.lastId_++;
  this.listeners_[eventName].push(id);
  this.listenersById_[id] = [callback, opt_context || null];

  return id;
};

/**
 * Unsubscribe for notifications we previously subscribed to.
 *
 * @param {number} subscriptionId The subscription ID returned by subscribe().
 */
PubSub.prototype.unsubscribe = function(subscriptionId) {
  delete this.listenersById_[subscriptionId];
};

/**
 * Publish an event by name. Listeners previously added by calling subscribe()
 * will be notified.
 *
 * @param {string} eventName The event name to notify subscribers about.
 * @param {...Object} var_args The arguments to pass to the subscriber callbacks.
 */
PubSub.prototype.publish = function(eventName, var_args) {
  var args = Array.prototype.slice.call(arguments, 1);
  var listeners = this.listeners_[eventName] || [];

  var cleanedListeners = [];
  iter.forEach(listeners, function(subscriptionId) {
    var listenerAndContext = this.listenersById_[subscriptionId];
    if (!listenerAndContext) {
      return;
    }

    var listener = listenerAndContext[0];
    var context = listenerAndContext[1];
    listener.apply(context, args);

    cleanedListeners.push(subscriptionId);
  }, this);
  this.listeners_[eventName] = cleanedListeners;
};

module.exports = PubSub;<commit_msg>Fix race condition where adding a subscription during publish() would drop the subscription.<commit_after>'use strict';

/**
 * @license
 * (c) 2014 Cluster Labs, Inc. https://cluster.co/
 * License: MIT
 */

/** @ignore */
var iter = skit.platform.iter;

/**
 * A simple PubSub for publishing and subscribing to notifications by name.
 *
 * @class
 */
var PubSub = function() {
  this.listeners_ = [];
  this.listenersById_ = {};
  this.lastId_ = 0;
};

var shared_;

/**
 * @return {PubSub} A shared instance of PubSub for all to enjoy.
 */
PubSub.sharedPubSub = function() {
  if (!shared_) {
    shared_ = new PubSub();
  }
  return shared_;
};

/**
 * Subscribe for notifications for a given event name.
 *
 * @param {string} eventName The name of the event you want to be notified
 *     about.
 * @param {function} callback The callback function to call when the
 *     specified event happens.
 * @param {Object=} opt_context The context to call the callback function
 *     in when the event happens.
 * @return {number} A listener ID, which can later be used to unsubscibe.
 */
PubSub.prototype.subscribe = function(eventName, callback, opt_context) {
  if (!(eventName in this.listeners_)) {
    this.listeners_[eventName] = [];
  }

  var id = this.lastId_++;
  this.listeners_[eventName].push(id);
  this.listenersById_[id] = [callback, opt_context || null];

  return id;
};

/**
 * Unsubscribe for notifications we previously subscribed to.
 *
 * @param {number} subscriptionId The subscription ID returned by subscribe().
 */
PubSub.prototype.unsubscribe = function(subscriptionId) {
  delete this.listenersById_[subscriptionId];
};

/**
 * Publish an event by name. Listeners previously added by calling subscribe()
 * will be notified.
 *
 * @param {string} eventName The event name to notify subscribers about.
 * @param {...Object} var_args The arguments to pass to the subscriber callbacks.
 */
PubSub.prototype.publish = function(eventName, var_args) {
  var args = Array.prototype.slice.call(arguments, 1);
  var listeners = this.listeners_[eventName] || [];
  // In case this gets modified while we're iterating.
  this.listeners_[eventName] = [];

  var cleanedListeners = [];
  iter.forEach(listeners, function(subscriptionId) {
    var listenerAndContext = this.listenersById_[subscriptionId];
    if (!listenerAndContext) {
      return;
    }

    var listener = listenerAndContext[0];
    var context = listenerAndContext[1];
    listener.apply(context, args);

    cleanedListeners.push(subscriptionId);
  }, this);
  this.listeners_[eventName] = cleanedListeners.concat(this.listeners_[eventName]);
};

module.exports = PubSub;<|endoftext|>


<commit_before>module.exports.format = function (path) {
    var formattedPath = path;
    formattedPath = formattedPath.replace(/^The\s+/gi, '');
    formattedPath = formattedPath.replace(/\s+(?:\(|\[)?Dis(?:c|k) (?:One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|\d)+(?:\)|\])?/gi, '');
    formattedPath = formattedPath.replace(/\s+/gi, '_');
    formattedPath = formattedPath.replace(/(?:\'|`)/gi, '');
    formattedPath = replaceAccented(formattedPath);
    formattedPath = replaceSymbols(formattedPath);
    formattedPath = formattedPath.replace(/(?:[^a-z0-9])+/gi, '_');

    //take paths which begin with _ and Base64 encode them
    if (formattedPath.substring(0, 1) == '_') formattedPath = '_' + require('urlsafe-base64').encode(new Buffer(path));

    return formattedPath;
};

function replaceAccented(str) {
    str = str.replace(/(?:À|Á|Â|Ä|Å|Ã)/g, 'A');
    str = str.replace(/(?:Ç)/g, 'C');
    str = str.replace(/(?:È|É|Ê|Ë)/g, 'E');
    str = str.replace(/(?:Ì|Í|Î|Ï)/g, 'I');
    str = str.replace(/(?:Ò|Ó|Ô|Ö|Õ)/g, 'O');
    str = str.replace(/(?:Ù|Ú|Û|Ü)/g, 'U');
    str = str.replace(/(?:Ý)/g, 'Y');
    str = str.replace(/(?:Ñ)/g, 'N');
    str = str.replace(/(?:Æ)/g, 'Ae');
    str = str.replace(/(?:ß)/g, 'ss');
    str = str.replace(/(?:à|á|â|ä|å|ã)/g, 'a');
    str = str.replace(/(?:ç)/g, 'c');
    str = str.replace(/(?:è|é|ê|ë)/g, 'e');
    str = str.replace(/(?:ì|í|î|ï)/g, 'i');
    str = str.replace(/(?:ò|ó|ô|ö|õ)/g, 'o');
    str = str.replace(/(?:ù|ú|û|ü)/g, 'u');
    str = str.replace(/(?:ý)/g, 'y');
    str = str.replace(/(?:ñ)/g, 'n');
    str = str.replace(/(?:æ)/g, 'ae');
    return str;
}

function replaceSymbols(str) {
    str = str.replace(/(?:\(|\))/g, '');
    str = str.replace(/(?:\.|\?)/g, '');
    str = str.replace(/(?:_+)/g, '_');
    return str;
}<commit_msg>Switched from UTF-8 characters to Unicode escape sequences (what a pain...)<commit_after>module.exports.format = function (path) {
    var formattedPath = path;
    formattedPath = formattedPath.replace(/^The\s+/gi, '');
    formattedPath = formattedPath.replace(/\s+(?:\(|\[)?Dis(?:c|k) (?:One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|\d)+(?:\)|\])?/gi, '');
    formattedPath = formattedPath.replace(/\s+/gi, '_');
    formattedPath = formattedPath.replace(/(?:\'|`)/gi, '');
    formattedPath = replaceAccented(formattedPath);
    formattedPath = replaceSymbols(formattedPath);
    formattedPath = formattedPath.replace(/(?:[^a-z0-9])+/gi, '_');

    //take paths which begin with _ and Base64 encode them
    if (formattedPath.substring(0, 1) == '_') formattedPath = '_' + require('urlsafe-base64').encode(new Buffer(path));

    return formattedPath;
};

function replaceAccented(str) {
    str = str.replace(/(?:\xC0|\xC1|\xC2|\xC3|\xC4|\xC5)/g, 'A');
    str = str.replace(/(?:\xC7)/g, 'C');
    str = str.replace(/(?:\xC8|\xC9|\xCA|\xCB)/g, 'E');
    str = str.replace(/(?:\xCC|\xCD|\xCE|\xCF)/g, 'I');
    str = str.replace(/(?:\xD2|\xD3|\xD4|\xD6|\xD5)/g, 'O');
    str = str.replace(/(?:\xD9|\xDA|\xDB|\xDC)/g, 'U');
    str = str.replace(/(?:\xDD)/g, 'Y');
    str = str.replace(/(?:\xD1)/g, 'N');
    str = str.replace(/(?:\xC6)/g, 'Ae');
    str = str.replace(/(?:\xDF)/g, 'ss');
    str = str.replace(/(?:\xE0|\xE1|\xE2|\xE4|\xE5|\xE3)/g, 'a');
    str = str.replace(/(?:\xE7)/g, 'c');
    str = str.replace(/(?:\xE8|\xE9|\xEA|\xEB)/g, 'e');
    str = str.replace(/(?:\xEC|\xED|\xEE|\xEF)/g, 'i');
    str = str.replace(/(?:\xF2|\xF3|\xF4|\xF6|\xF5)/g, 'o');
    str = str.replace(/(?:\xF9|\xFA|\xFB|\xFC)/g, 'u');
    str = str.replace(/(?:\xFD)/g, 'y');
    str = str.replace(/(?:\xF1)/g, 'n');
    str = str.replace(/(?:\xE6)/g, 'ae');
    return str;
}

function replaceSymbols(str) {
    str = str.replace(/(?:\(|\))/g, '');
    str = str.replace(/(?:\.|\?)/g, '');
    str = str.replace(/(?:_+)/g, '_');
    return str;
}<|endoftext|>


<commit_before>// TODO: make as API
module.exports = {};
<commit_msg>Exports GitterBot as module<commit_after>module.exports = require('./lib/GitterBot');
<|endoftext|>


<commit_before>if (typeof AFRAME === 'undefined') {
  throw new Error('Component attempted to register before AFRAME was available.');
}

var LSystem;

// As we use webpack for compiling the source, it's used to bundle the
// web worker into a blob via: https://github.com/webpack/worker-loader
// Which works without additional changes, besides using `require` inside
// the worker instead of importScripts().
var LSystemWorker = require("worker?inline!./LSystemWorker.js");

/**
 * Lindenmayer-System component for A-Frame.
 */
AFRAME.registerComponent('lsystem', {
  schema: {

    axiom: {
      type: 'string',
      default: 'F'
    },

    productions: {
      default: 'F:FF',
      // return an array of production tuples ([[from, to], ['F', 'F+F']])
      parse: function (value) {
        return value.split(' ').map(function (splitValue) {
          return splitValue.split(':');
        })
      }
    },

    segmentMixins: {
      type: 'string',
      parse: function (value) {
        return value.split(' ').map(function (splitValue) {
          return splitValue.split(':');
        })
      }
    },

    iterations: {
      type: 'int',
      default: 1
    },

    angle: {
      type: 'number',
      default: 45.0
    },

    /* For fixed segment length */
    segmentLength: {
      type: 'number',
      default: 1.125
    },

    /* Translate a segment, based on it's bonding box, may not be optimal!
      TODO: implementation!
     */
    dynamicSegmentLength: {
      default: true
    },

    mergeGeometries: {
      type: 'boolean',
      default: true
    },

    functionsInProductions: {
      type: 'boolean',
      default: true
    }
  },

  /**
   * Called once when component is attached. Generally for initial setup.
   */
  init: function () {
    if(LSystem === undefined) {
      LSystem = require('lindenmayer');
    }

    this.sceneEl = document.querySelector('a-scene');

    this.stack = [];
    this.X = new THREE.Vector3(1, 0, 0);
    this.Y = new THREE.Vector3(0, 1, 0);
    this.Z = new THREE.Vector3(0, 0, 1);
    this.xPosRotation = new THREE.Quaternion();
    this.xNegRotation = new THREE.Quaternion();
    this.yPosRotation = new THREE.Quaternion();
    this.yNegRotation = new THREE.Quaternion();
    this.zPosRotation = new THREE.Quaternion();
    this.zNegRotation = new THREE.Quaternion();
    this.yReverseRotation = new THREE.Quaternion();
    this.colors = [0x8c651b, 0x7d9322, 0x3d9322];
    this.colorIndex = 0;

    var self = this;

    this.LSystem = new LSystem({
      axiom: 'FF+F',
      productions: {'F': 'FF'},
      finals: {
				'F': () => {self.pushSegment.bind(self, 'F')()},
				'+': () => { self.currentSegment.quaternion.multiply(self.yPosRotation)},
				'-': () => { self.currentSegment.quaternion.multiply(self.yNegRotation)},
				'&': () => { self.currentSegment.quaternion.multiply(self.zNegRotation)},
				'^': () => { self.currentSegment.quaternion.multiply(self.zPosRotation)},
				'\\': () =>{ self.currentSegment.quaternion.multiply(self.xNegRotation)},
				'<': () => { self.currentSegment.quaternion.multiply(self.xNegRotation)},
				'/': () => { self.currentSegment.quaternion.multiply(self.xPosRotation)},
				'>': () => { self.currentSegment.quaternion.multiply(self.xPosRotation)},
				'|': () => { self.currentSegment.quaternion.multiply(self.yReverseRotation)},
				'!': () => {
					self.currentSegment.scale.set(self.currentSegment.scale.x, self.currentSegment.scale.y + 0.2, self.currentSegment.scale.z + 0.2);
					colorIndex = Math.min(colors.length - 1, colorIndex + 1);
					for (let face of self.currentSegment.geometry.faces) {
						face.color.setHex(colors[colorIndex]);
					}
					self.currentSegment.geometry.colorsNeedUpdate = true;
				},
				'\'': () => {
					self.currentSegment.scale.set(self.currentSegment.scale.x, self.currentSegment.scale.y - 0.2, self.currentSegment.scale.z - 0.2);
					colorIndex = Math.max(0, colorIndex - 1);
					for (let face of self.currentSegment.geometry.faces) {
						face.color.setHex(colors[colorIndex]);
					}
					self.currentSegment.geometry.colorsNeedUpdate = true;
				},
				'[': () => { self.stack.push(self.currentSegment.clone()) },
				']': () => { self.currentSegment = self.stack.pop() }
			}
    });

    this.initWorker();

  },

  /**
   * Called when component is attached and when component data changes.
   * Generally modifies the entity based on the data.
   */
  update: function (oldData) {
    // var diffData = diff(data, oldData || {});
    // console.log(diffData);

    // TODO: Check if only angle changed or axiom or productions
    //
    console.log('update');
    this.el.removeObject3D('mesh');
    if(this.mixinGroups) {
      for (let group of this.mixinGroups) {
        console.log(group);
        this.el.removeChild(group[1]);
      }
    }

    // Collect mixin info
    if(this.data.segmentMixins && this.data.segmentMixins.length !== 0) {
      this.mixinIDForSymbol = new Map();
      this.mixinGroups = new Map();
      for (let segmentMixin of this.data.segmentMixins) {

        this.mixinIDForSymbol.set(segmentMixin[0], segmentMixin[1]);
        let segmentGroup = document.createElement('a-entity');
        segmentGroup.setAttribute('id', segmentMixin[1] + '-group');
        segmentGroup.setAttribute('position', { x: 0, y: 0, z: 0});
        this.mixinGroups.set(segmentMixin[0], segmentGroup);
        this.el.appendChild(segmentGroup);

      }
    }

    this.LSystem.setAxiom(this.data.axiom);

    var params = {
      axiom: 		this.data.axiom,
      productions: this.data.productions,
      iterations: this.data.iterations
    }

    if(Date.now() - this.worker.startTime > 1000 ) {
      // if we got user input, but worker is running for over a second
      // terminate old worker and start new one.
      this.worker.terminate();
      this.initWorker();
    }
    // post params to worker
    this.worker.startTime = Date.now();
    this.worker.postMessage(params);
  },

  initWorker: function() {
    this.worker = new LSystemWorker();
    this.worker.onmessage = this.onWorkerUpdate.bind(this);
  },

  pushSegment: function(symbol) {
    let self = this;
    // TODO: When no mixins defined, or mixins are very basic, or forced by flag
    // then do a faster geometry merge without creating entities first

    // let newSegment = new THREE.Mesh( this.geometry );

    // let newSegment = this.currentSegment.clone();
    // newSegment.matrixAutoUpdate = false;
    // newSegment.updateMatrix();

    let newSegment = document.createElement('a-entity');

    newSegment.setAttribute('mixin', this.mixinIDForSymbol.get(symbol));

    let currentQuaternion = this.currentSegment.quaternion.clone();
    let currentPosition = this.currentSegment.position.clone();

    newSegment.addEventListener('loaded', function (e) {
      newSegment.object3D.children[0].translateX(-self.data.segmentLength/2);
      newSegment.object3D.quaternion.copy(currentQuaternion);
      newSegment.object3D.position.copy(currentPosition);
      newSegment.setAttribute('geometry', 'skipCache', true);
      newSegment.setAttribute('geometry', 'buffer', false);
      newSegment.setAttribute('geometry', 'mergeTo', '#' + self.mixinIDForSymbol.get(symbol) + '-group');

      // newSegment.addEventListener('geometry-merged', function (e) {
      //   console.log('merged.');
      //
      //});
    });

    this.sceneEl.appendChild(newSegment);

    // TODO: if mergeGeometries === false, this.modelGroups.get('symbol').push(newSegment)

    // this.modelGroups.get(symbol).geometry.merge(newSegment.geometry, newSegment.matrix);

    // ALTERNATIVE MAYBE?: but use new merge api.
    // and define #id of the fullModel to merge into.
    // this.modelGroups.geometry.merge(newSegment.geometry, newSegment.matrix);

    this.currentSegment.translateX(-(this.data.segmentLength));
  },

  updateTurtleGraphics: function() {
		console.log('updateTurtleGraphics');

		colorIndex = 0; // Reset color index
		angle = this.data.angle;
    this.lineWidth = 0.0005;
    this.lineLength = 0.125;
		// Set quaternions based on angle slider
		this.xPosRotation.setFromAxisAngle( this.X, (Math.PI / 180) * angle );
		this.xNegRotation.setFromAxisAngle( this.X, (Math.PI / 180) * -angle );

		this.yPosRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * angle );
		this.yNegRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * -angle );
		this.yReverseRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * 180 );

		this.zPosRotation.setFromAxisAngle( this.Z, (Math.PI / 180) * angle );
		this.zNegRotation.setFromAxisAngle( this.Z, (Math.PI / 180) * -angle );
		//
		// this.geometry = new THREE.CylinderGeometry(this.lineWidth, this.lineWidth, self.data.lineLength, 3);
		// this.geometry.rotateZ((Math.PI / 180) * 90);
		// this.geometry.translate( -(this.data.segmentLength/2), 0, 0 );
		// for (let face of this.geometry.faces) {
		// 	face.color.setHex(this.colors[colorIndex]);
		// }
		// this.geometry.colorsNeedUpdate = true;

    // TODO: Make currentSegment only an empty shell Object3D, fill with
    // Geometry only when pushing a new segment, then deciding by smybol (F, L, whatever)
    // Bzw. on pushSegment create new THREE.Mesh( chosengeometry ), _copy_ the matrix
    // from currentSegment into it, then merge geometry into according fullModel (eg. branches, leaves) to apply individual materials.
    this.currentSegment = new THREE.Object3D();

		// this.currentSegment.matrixAutoUpdate = false;
    let material = new THREE.MeshLambertMaterial({
      shading: THREE.SmoothShading,
      vertexColors: THREE.FaceColors
    });

		// this.fullModel = new THREE.Mesh(new THREE.Geometry(), material);
    // this.el.setObject3D('mesh', this.fullModel);

		this.LSystem.final();
    // TODO: then add all fullModels (branches, leaves) into object3d
    //
    // for (let fullModel of fullModels) {
    //   this.el.object3D.add(fullModel);
    // }
	},

  onWorkerUpdate: function(e) {
		// Received updated Axiom from worker (which applied productions)
		this.LSystem.setAxiom(e.data.result);
		this.updateTurtleGraphics();
	},

  /**
   * Called when a component is removed (e.g., via removeAttribute).
   * Generally undoes all modifications to the entity.
   */
  remove: function () { },

  /**
   * Called on each scene tick.
   */
  // tick: function (t) { },

  /**
   * Called when entity pauses.
   * Use to stop or remove any dynamic or background behavior such as events.
   */
  pause: function () { },

  /**
   * Called when entity resumes.
   * Use to continue or add any dynamic or background behavior such as events.
   */
  play: function () { },
});
<commit_msg>added a comment for last commits change<commit_after>if (typeof AFRAME === 'undefined') {
  throw new Error('Component attempted to register before AFRAME was available.');
}

var LSystem;

// As we use webpack for compiling the source, it's used to bundle the
// web worker into a blob via: https://github.com/webpack/worker-loader
// Which works without additional changes, besides using `require` inside
// the worker instead of importScripts().
var LSystemWorker = require("worker?inline!./LSystemWorker.js");

/**
 * Lindenmayer-System component for A-Frame.
 */
AFRAME.registerComponent('lsystem', {
  schema: {

    axiom: {
      type: 'string',
      default: 'F'
    },

    productions: {
      default: 'F:FF',
      // return an array of production tuples ([[from, to], ['F', 'F+F']])
      parse: function (value) {
        return value.split(' ').map(function (splitValue) {
          return splitValue.split(':');
        })
      }
    },

    segmentMixins: {
      type: 'string',
      parse: function (value) {
        return value.split(' ').map(function (splitValue) {
          return splitValue.split(':');
        })
      }
    },

    iterations: {
      type: 'int',
      default: 1
    },

    angle: {
      type: 'number',
      default: 45.0
    },

    /* For fixed segment length */
    segmentLength: {
      type: 'number',
      default: 1.125
    },

    /* Translate a segment, based on it's bonding box, may not be optimal!
      TODO: implementation!
     */
    dynamicSegmentLength: {
      default: true
    },

    mergeGeometries: {
      type: 'boolean',
      default: true
    },

    functionsInProductions: {
      type: 'boolean',
      default: true
    }
  },

  /**
   * Called once when component is attached. Generally for initial setup.
   */
  init: function () {
    if(LSystem === undefined) {
      LSystem = require('lindenmayer');
    }

    this.sceneEl = document.querySelector('a-scene');

    this.stack = [];
    this.X = new THREE.Vector3(1, 0, 0);
    this.Y = new THREE.Vector3(0, 1, 0);
    this.Z = new THREE.Vector3(0, 0, 1);
    this.xPosRotation = new THREE.Quaternion();
    this.xNegRotation = new THREE.Quaternion();
    this.yPosRotation = new THREE.Quaternion();
    this.yNegRotation = new THREE.Quaternion();
    this.zPosRotation = new THREE.Quaternion();
    this.zNegRotation = new THREE.Quaternion();
    this.yReverseRotation = new THREE.Quaternion();
    this.colors = [0x8c651b, 0x7d9322, 0x3d9322];
    this.colorIndex = 0;

    var self = this;

    this.LSystem = new LSystem({
      axiom: 'FF+F',
      productions: {'F': 'FF'},
      finals: {
				'F': () => {self.pushSegment.bind(self, 'F')()},
				'+': () => { self.currentSegment.quaternion.multiply(self.yPosRotation)},
				'-': () => { self.currentSegment.quaternion.multiply(self.yNegRotation)},
				'&': () => { self.currentSegment.quaternion.multiply(self.zNegRotation)},
				'^': () => { self.currentSegment.quaternion.multiply(self.zPosRotation)},
				'\\': () =>{ self.currentSegment.quaternion.multiply(self.xNegRotation)},
				'<': () => { self.currentSegment.quaternion.multiply(self.xNegRotation)},
				'/': () => { self.currentSegment.quaternion.multiply(self.xPosRotation)},
				'>': () => { self.currentSegment.quaternion.multiply(self.xPosRotation)},
				'|': () => { self.currentSegment.quaternion.multiply(self.yReverseRotation)},
				'!': () => {
					self.currentSegment.scale.set(self.currentSegment.scale.x, self.currentSegment.scale.y + 0.2, self.currentSegment.scale.z + 0.2);
					colorIndex = Math.min(colors.length - 1, colorIndex + 1);
					for (let face of self.currentSegment.geometry.faces) {
						face.color.setHex(colors[colorIndex]);
					}
					self.currentSegment.geometry.colorsNeedUpdate = true;
				},
				'\'': () => {
					self.currentSegment.scale.set(self.currentSegment.scale.x, self.currentSegment.scale.y - 0.2, self.currentSegment.scale.z - 0.2);
					colorIndex = Math.max(0, colorIndex - 1);
					for (let face of self.currentSegment.geometry.faces) {
						face.color.setHex(colors[colorIndex]);
					}
					self.currentSegment.geometry.colorsNeedUpdate = true;
				},
				'[': () => { self.stack.push(self.currentSegment.clone()) },
				']': () => { self.currentSegment = self.stack.pop() }
			}
    });

    this.initWorker();

  },

  /**
   * Called when component is attached and when component data changes.
   * Generally modifies the entity based on the data.
   */
  update: function (oldData) {
    // var diffData = diff(data, oldData || {});
    // console.log(diffData);

    // TODO: Check if only angle changed or axiom or productions
    //
    console.log('update');
    this.el.removeObject3D('mesh');
    if(this.mixinGroups) {
      for (let group of this.mixinGroups) {
        console.log(group);
        this.el.removeChild(group[1]);
      }
    }

    // Collect mixin info
    if(this.data.segmentMixins && this.data.segmentMixins.length !== 0) {
      this.mixinIDForSymbol = new Map();
      this.mixinGroups = new Map();
      for (let segmentMixin of this.data.segmentMixins) {

        this.mixinIDForSymbol.set(segmentMixin[0], segmentMixin[1]);
        let segmentGroup = document.createElement('a-entity');
        segmentGroup.setAttribute('id', segmentMixin[1] + '-group');
        segmentGroup.setAttribute('position', { x: 0, y: 0, z: 0});
        this.mixinGroups.set(segmentMixin[0], segmentGroup);
        this.el.appendChild(segmentGroup);

      }
    }

    this.LSystem.setAxiom(this.data.axiom);

    var params = {
      axiom: 		this.data.axiom,
      productions: this.data.productions,
      iterations: this.data.iterations
    }

    if(Date.now() - this.worker.startTime > 1000 ) {
      // if we got user input, but worker is running for over a second
      // terminate old worker and start new one.
      this.worker.terminate();
      this.initWorker();
    }
    // post params to worker
    this.worker.startTime = Date.now();
    this.worker.postMessage(params);
  },

  initWorker: function() {
    this.worker = new LSystemWorker();
    this.worker.onmessage = this.onWorkerUpdate.bind(this);
  },

  pushSegment: function(symbol) {
    let self = this;
    // TODO: When no mixins defined, or mixins are very basic, or forced by flag
    // then do a faster geometry merge without creating entities first

    // let newSegment = new THREE.Mesh( this.geometry );

    // let newSegment = this.currentSegment.clone();
    // newSegment.matrixAutoUpdate = false;
    // newSegment.updateMatrix();

    let newSegment = document.createElement('a-entity');

    newSegment.setAttribute('mixin', this.mixinIDForSymbol.get(symbol));

    let currentQuaternion = this.currentSegment.quaternion.clone();
    let currentPosition = this.currentSegment.position.clone();

    newSegment.addEventListener('loaded', function (e) {
      // Offset child element of object3D, to rotate around end point
      // IMPORTANT: It may change that A-Frame puts objects into a group
      // FIXME: offset all children instead of the first, in case there are multiple
      // geometries in there?!?
      newSegment.object3D.children[0].translateX(-self.data.segmentLength/2);

      newSegment.object3D.quaternion.copy(currentQuaternion);
      newSegment.object3D.position.copy(currentPosition);
      newSegment.setAttribute('geometry', 'skipCache', true);
      newSegment.setAttribute('geometry', 'buffer', false);
      newSegment.setAttribute('geometry', 'mergeTo', '#' + self.mixinIDForSymbol.get(symbol) + '-group');

      // newSegment.addEventListener('geometry-merged', function (e) {
      //   console.log('merged.');
      //
      //});
    });

    this.sceneEl.appendChild(newSegment);

    // TODO: if mergeGeometries === false, this.modelGroups.get('symbol').push(newSegment)

    // this.modelGroups.get(symbol).geometry.merge(newSegment.geometry, newSegment.matrix);

    // ALTERNATIVE MAYBE?: but use new merge api.
    // and define #id of the fullModel to merge into.
    // this.modelGroups.geometry.merge(newSegment.geometry, newSegment.matrix);

    this.currentSegment.translateX(-(this.data.segmentLength));
  },

  updateTurtleGraphics: function() {
		console.log('updateTurtleGraphics');

		colorIndex = 0; // Reset color index
		angle = this.data.angle;
    this.lineWidth = 0.0005;
    this.lineLength = 0.125;
		// Set quaternions based on angle slider
		this.xPosRotation.setFromAxisAngle( this.X, (Math.PI / 180) * angle );
		this.xNegRotation.setFromAxisAngle( this.X, (Math.PI / 180) * -angle );

		this.yPosRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * angle );
		this.yNegRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * -angle );
		this.yReverseRotation.setFromAxisAngle( this.Y, (Math.PI / 180) * 180 );

		this.zPosRotation.setFromAxisAngle( this.Z, (Math.PI / 180) * angle );
		this.zNegRotation.setFromAxisAngle( this.Z, (Math.PI / 180) * -angle );
		//
		// this.geometry = new THREE.CylinderGeometry(this.lineWidth, this.lineWidth, self.data.lineLength, 3);
		// this.geometry.rotateZ((Math.PI / 180) * 90);
		// this.geometry.translate( -(this.data.segmentLength/2), 0, 0 );
		// for (let face of this.geometry.faces) {
		// 	face.color.setHex(this.colors[colorIndex]);
		// }
		// this.geometry.colorsNeedUpdate = true;

    // TODO: Make currentSegment only an empty shell Object3D, fill with
    // Geometry only when pushing a new segment, then deciding by smybol (F, L, whatever)
    // Bzw. on pushSegment create new THREE.Mesh( chosengeometry ), _copy_ the matrix
    // from currentSegment into it, then merge geometry into according fullModel (eg. branches, leaves) to apply individual materials.
    this.currentSegment = new THREE.Object3D();

		// this.currentSegment.matrixAutoUpdate = false;
    let material = new THREE.MeshLambertMaterial({
      shading: THREE.SmoothShading,
      vertexColors: THREE.FaceColors
    });

		// this.fullModel = new THREE.Mesh(new THREE.Geometry(), material);
    // this.el.setObject3D('mesh', this.fullModel);

		this.LSystem.final();
    // TODO: then add all fullModels (branches, leaves) into object3d
    //
    // for (let fullModel of fullModels) {
    //   this.el.object3D.add(fullModel);
    // }
	},

  onWorkerUpdate: function(e) {
		// Received updated Axiom from worker (which applied productions)
		this.LSystem.setAxiom(e.data.result);
		this.updateTurtleGraphics();
	},

  /**
   * Called when a component is removed (e.g., via removeAttribute).
   * Generally undoes all modifications to the entity.
   */
  remove: function () { },

  /**
   * Called on each scene tick.
   */
  // tick: function (t) { },

  /**
   * Called when entity pauses.
   * Use to stop or remove any dynamic or background behavior such as events.
   */
  pause: function () { },

  /**
   * Called when entity resumes.
   * Use to continue or add any dynamic or background behavior such as events.
   */
  play: function () { },
});
<|endoftext|>


<commit_before>'use strict';

module.exports = plugin;

var path = require('path');
var gutil = require('gulp-util');
var through = require('through2');

var PLUGINNAME = 'gulp-rev-replace';

var REG_HTML = /([\s]href=|[\s]src=)['"][a-zA-Z_\-\d\.\/]+['"]|[\s:]url\(['"]?[a-zA-Z_\-\d\.\/]+['"]?\)/g;
var REG_SUB = /([\s]href=|[\s]src=)|['"]|[\s:]url\(|\)/g;

function plugin(options){
  var cache = [];
  options = options || {};
  return through.obj(function(file, enc, cb){
    if(file.isNull()){
      this.push(file);
      return cb();
    }
    if(file.isStream()){
      this.emit('error', new gutil.PluginError(PLUGINNAME, 'Streaming not supported'));
      return cb();
    }
    cache.push(file);
    //this.push(file);
    cb();
  }, function(cb){
    var stream = this;
    var manifestObj = {};
    var HTML_MATCHED = 0;
    var REPLACED = 0;
    var DOMAIN = '/';

    if(options.manifest){
      options.manifest.on('data', function(file){
        manifestObj = JSON.parse(file.contents.toString());
      });
      options.manifest.on('end', replace);
    }

    if(options.prefix){
      DOMAIN = options.prefix.replace(/\/$/, '') + '/';
    }

    function replace(){

      cache.forEach(function(file){
        var fileContent = file.contents.toString();
        var filePath = file.history[0].replace(/\\[a-zA-Z_\-\d\.]+$/, '');
        var base = file.base;
        console.log('\nchecking file...' + file.history[0]);

        fileContent.replace(REG_HTML, function(matchString){
          HTML_MATCHED++;
          var url = matchString.replace(REG_SUB, '');
          var absoluteUrl = '';
          if(url.indexOf('/') === 0){
            absoluteUrl = url.replace(/^\//, '');
          }else{
            absoluteUrl = path.join(filePath, url).replace(base, '');
          }
          absoluteUrl = absoluteUrl.split(path.sep).join('/');
          var result = '';
          if(manifestObj[absoluteUrl]){
            result = matchString.split(url).join(DOMAIN + manifestObj[absoluteUrl]);
            console.log(url + '  =>  ' + DOMAIN + manifestObj[absoluteUrl]);
            REPLACED++;
          }else{
            console.log('not find in manifest  =>  ' + matchString);
            result = matchString;
          }
          return result;
        });

        file.contents = new Buffer(fileContent);

        stream.push(file);

      });

      console.log('\nHTML_MATCHED:' + HTML_MATCHED + ' REPLACED:' + REPLACED + '\n');

      cb();

    }

  });
}
<commit_msg>[fix] replace invalid<commit_after>'use strict';

module.exports = plugin;

var path = require('path');
var gutil = require('gulp-util');
var through = require('through2');

var PLUGINNAME = 'gulp-rev-replace';

var REG_HTML = /([\s]href=|[\s]src=)['"][a-zA-Z_\-\d\.\/]+['"]|[\s:]url\(['"]?[a-zA-Z_\-\d\.\/]+['"]?\)/g;
var REG_SUB = /([\s]href=|[\s]src=)|['"]|[\s:]url\(|\)/g;

function plugin(options){
  var cache = [];
  options = options || {};
  return through.obj(function(file, enc, cb){
    if(file.isNull()){
      this.push(file);
      return cb();
    }
    if(file.isStream()){
      this.emit('error', new gutil.PluginError(PLUGINNAME, 'Streaming not supported'));
      return cb();
    }
    cache.push(file);
    //this.push(file);
    cb();
  }, function(cb){
    var stream = this;
    var manifestObj = {};
    var HTML_MATCHED = 0;
    var REPLACED = 0;
    var DOMAIN = '/';

    if(options.manifest){
      options.manifest.on('data', function(file){
        manifestObj = JSON.parse(file.contents.toString());
      });
      options.manifest.on('end', replace);
    }

    if(options.prefix){
      DOMAIN = options.prefix.replace(/\/$/, '') + '/';
    }

    function replace(){

      cache.forEach(function(file){
        var fileContent = file.contents.toString();
        var filePath = file.history[0].replace(/\\[a-zA-Z_\-\d\.]+$/, '');
        var base = file.base;
        console.log('\nchecking file...' + file.history[0]);

        fileContent = fileContent.replace(REG_HTML, function(matchString){
          HTML_MATCHED++;
          var url = matchString.replace(REG_SUB, '');
          var absoluteUrl = '';
          var resultUrl = '';
          var result = '';
          if(url.indexOf('/') === 0){
            absoluteUrl = url.replace(/^\//, '');
          }else{
            absoluteUrl = path.join(filePath, url).replace(base, '');
          }
          absoluteUrl = absoluteUrl.split(path.sep).join('/');
          if(manifestObj[absoluteUrl]){
            resultUrl = DOMAIN + manifestObj[absoluteUrl];
            result = matchString.split(url).join(resultUrl);
            console.log(url + '  =>  ' + resultUrl);
            REPLACED++;
          }else{
            console.log('not find in manifest  =>  ' + matchString);
            result = matchString;
          }
          return result;
        });

        file.contents = new Buffer(fileContent);

        stream.push(file);

      });

      console.log('\nHTML_MATCHED:' + HTML_MATCHED + ' REPLACED:' + REPLACED + '\n');

      cb();

    }

  });
}
<|endoftext|>