<commit_before>/**
 * Copyright (c) 2017-present, Wonday (@wonday.org)
 * All rights reserved.
 *
 * This source code is licensed under the MIT-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';
import React,{ Component, PropTypes } from 'react';
import {
    requireNativeComponent,
} from 'react-native';

const AliyunPushNative = require('react-native').NativeModules.AliyunPush;
const { NativeEventEmitter } = require('react-native');
const LocalEventEmitter =  new NativeEventEmitter(AliyunPushNative);

var listeners = {};

var id = 0;
var META = '__listener_id';

function getKey(listener,type){

    if (!listener.hasOwnProperty(META)){
        if (!Object.isExtensible(listener)) {
            return 'F';
        }
        Object.defineProperty(listener, META, {
            value: 'L' + type + ++id,
        });
    }
    return listener[META];

};

export default class AliyunPush {

    static getDeviceId = (cb)=> {
        AliyunPushNative.getDeviceId(function(args) {
            cb(args);
        });
    }

    static addMessageListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushMessage");

    };

    static removeMessageListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushMessage");

    };

    static addNotificationListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotification");

    };

    static removeNotificationListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotification");

    };

    static addNotificationOpenedListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotificationOpened");

    };

    static removeNotificationOpenedListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotificationOpened");

    };

    static addNotificationRemovedListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotificationRemoved");

    };

    static removeNotificationRemovedListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotificationRemoved");

    };

    static addNotificationReceivedInAppListener = (cb) => {

        AliyunPush._addListener(cb,"onNotificationReceivedInApp");

    };

    static removeNotificationReceivedInAppListener = (cb) => {

        AliyunPush._removeListener(cb, "onNotificationReceivedInApp");

    };

    static _addListener = (cb,type) => {

        var key = getKey(cb,type);
        listeners[key] = LocalEventEmitter.addListener(type,
            (e) => {
                cb(e);
            });

    };

    static _removeListener = (cb,type) => {

        var key = getKey(cb,type);
        if (!listeners[key]) {
            return;
        }
        listeners[key].remove();
        listeners[key] = null;

    };
}
<commit_msg>expand NotificationOpened event extra parameters to event<commit_after>/**
 * Copyright (c) 2017-present, Wonday (@wonday.org)
 * All rights reserved.
 *
 * This source code is licensed under the MIT-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';
import React,{ Component, PropTypes } from 'react';
import {
    requireNativeComponent,
} from 'react-native';

const AliyunPushNative = require('react-native').NativeModules.AliyunPush;
const { NativeEventEmitter } = require('react-native');
const LocalEventEmitter =  new NativeEventEmitter(AliyunPushNative);

var listeners = {};

var id = 0;
var META = '__listener_id';

function getKey(listener,type){

    if (!listener.hasOwnProperty(META)){
        if (!Object.isExtensible(listener)) {
            return 'F';
        }
        Object.defineProperty(listener, META, {
            value: 'L' + type + ++id,
        });
    }
    return listener[META];

};

export default class AliyunPush {

    static getDeviceId = (cb)=> {
        AliyunPushNative.getDeviceId(function(args) {
            cb(args);
        });
    }

    static addMessageListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushMessage");

    };

    static removeMessageListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushMessage");

    };

    static addNotificationListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotification");

    };

    static removeNotificationListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotification");

    };

    static addNotificationOpenedListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotificationOpened");

    };

    static removeNotificationOpenedListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotificationOpened");

    };

    static addNotificationRemovedListener = (cb) => {

        AliyunPush._addListener(cb,"onAliyunPushNotificationRemoved");

    };

    static removeNotificationRemovedListener = (cb) => {

        AliyunPush._removeListener(cb, "onAliyunPushNotificationRemoved");

    };

    static addNotificationReceivedInAppListener = (cb) => {

        AliyunPush._addListener(cb,"onNotificationReceivedInApp");

    };

    static removeNotificationReceivedInAppListener = (cb) => {

        AliyunPush._removeListener(cb, "onNotificationReceivedInApp");

    };

    static _addListener = (cb,type) => {

        var key = getKey(cb,type);
        listeners[key] = LocalEventEmitter.addListener(type,
            (e) => {
                if (type==="onAliyunPushNotificationOpened"){
                    if (e.extraMap){
                        let extraMap = JSON.parse(e.extraMap);
                        for (var attrname in extraMap) { e[attrname] = extraMap[attrname]; }
                        delete e.extraMap;
                        cb(e);
                    }
                } else {
                    cb(e);
                }

            });

    };

    static _removeListener = (cb,type) => {

        var key = getKey(cb,type);
        if (!listeners[key]) {
            return;
        }
        listeners[key].remove();
        listeners[key] = null;

    };
}
<|endoftext|>


<commit_before>/*
 * gult-stubby
 * https://github.com/felixzapata/gulp-i18n-pug
 *
 * Copyright (c) 2016 Félix Zapata
 * Licensed under the MIT license.
 */

'use strict';

var pug = require('pug');
var glob = require('glob');
var fs = require('fs');
var gutil = require('gulp-util');
var path = require('path');
var YAML = require('js-yaml');
var through = require('through2');
var PLUGIN_NAME = 'gulp-i18n-pug';

function getExtension(filepath) {
    return path.extname(filepath);
}

function setExtension(ext) {
    if (ext.charAt(0) !== '.') {
        ext = '.' + ext;
    }
    return ext;
}

function readYAML(filepath, options) {
    var src = fs.readFileSync(filepath, options);
    var result;
    try {
        result = YAML.load(src);
        return result;
    } catch (e) {
        throw new gutil.PluginError(PLUGIN_NAME, 'Unable to parse "' + filepath + '" file (' + e.problem + ').', e);
    }
}

function readJSON(filepath, options) {
    var src = fs.readFileSync(filepath, options);
    var result;
    try {
        result = JSON.parse(src);
        return result;
    } catch (e) {
        throw new gutil.PluginError(PLUGIN_NAME, 'Unable to parse "' + filepath + '" file (' + e.message + ').', e);
    }
}

function addLocaleExtensionDest(file, locale, outputExt) {

    var dest, ext;

    locale = locale.toLowerCase();
    ext = getExtension(file);

    function getBaseName() {
        return path.basename(file).split('.')[0];
    }

    if (ext) {
        dest = path.join(path.basename(file, ext) + '.' + locale);
    } else {
        dest = path.join(getBaseName() + '.' + locale);
    }

    dest += setExtension(outputExt);

    return dest;
}

function addLocaleDirnameDest(file, locale, outputExt) {
    var base, dest, ext = getExtension(file);
    if (ext) {
        dest = path.join(path.dirname(file), locale, path.basename(file, ext) + setExtension(ext));
    } else {
        if (/(\/|\*+)$/i.test(file)) {
            base = file.split('/');
            dest = path.join(path.join.apply(null, base.slice(0, -1)), locale, base.slice(-1)[0]);
        } else {
            dest = path.join(file, locale);
        }
    }
    dest = dest.replace(/\.pug$/i, setExtension(outputExt));
    return dest;
}

function readFile(filepath) {
    var data;
    try {
        if (/(\.yaml|\.yml)$/i.test(filepath)) {
            data = readYAML(filepath);
        } else if (/\.js$/i.test(filepath)) {
            data = require(path.resolve(filepath));
        } else {
            data = readJSON(filepath);
        }
    } catch (_error) {
        throw new gutil.PluginError("Cannot parse file '" + filepath + "': " + _error.message, _error);
    }
    return data;
}

function plugI18nPlugin(customOptions) {

    var baseName;
    var compiledFiles = [];
    var dest;
    var localePath;
    var fileExt;
    var locale;

    var defaultI18nOptions = {
        i18n: {
            namespace: '$i18n',
            localeExtension: false,
            defaultExt: '.html'
        }
    }
    var options = Object.assign({}, customOptions);

    if (!options.i18n) {
        options.i18n = {};
    } else {
        options.i18n = Object.assign(defaultI18nOptions.i18n, customOptions.i18n);
    }

    var locales = options.i18n.locales ? glob.sync(options.i18n.locales) : null;

    var bufferContents = function (file, enc, cb) {

        if (file.isNull()) {
            cb(null, file);
            return;
        }

        if (file.isStream()) {
            cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return;
        }

        if (locales && locales.length) {
            for (var i = 0, len = locales.length; i < len; i++) {
                localePath = locales[i];
                fileExt = localePath.split('.').slice(-1)[0];
                locale = path.basename(localePath, '.' + fileExt);
                baseName = path.basename(file.path);
                gutil.log('Loading locale ' + locale);
                gutil.log('Reading translation data: ' + localePath);
                if (!options.data) {
                    options.data = {};
                }
                options.data[options.i18n.namespace] = readFile(localePath);
                options.data['$localeName'] = locale;

                if (options.i18n.localeExtension) {
                    dest = addLocaleExtensionDest(baseName, locale, options.i18n.defaultExt);
                } else {
                    dest = addLocaleDirnameDest(baseName, locale, options.i18n.defaultExt);
                }

                compiledFiles.push(new gutil.File({
                    base: options.i18n.dest,
                    contents: new Buffer(pug.compileFile(file.path, options)(options.data)),
                    path: path.join(options.i18n.dest, dest)
                }));

            }

        } else {
            gutil.log('Locales files not found. Nothing to translate');
            compiledFiles.push(new gutil.File({
                base: __dirname,
                cwd: __dirname,
                contents: new Buffer(pug.compileFile(file.path, options)(options.data)),
                path: path.basename(file.path).split('.')[0] + options.i18n.defaultExt
            }));
        }
        cb();
    }

    var endStream = function (cb) {
        if (!compiledFiles || compiledFiles.length === 0) {
            cb();
            return;
        }
        for (var i = 0, len = compiledFiles.length; i < len; i++) {
            this.push(compiledFiles[i]);
        }
        cb();
    }

    return through.obj(bufferContents, endStream);
}

module.exports = plugI18nPlugin;
<commit_msg>fix #5 issue<commit_after>/*
 * gult-stubby
 * https://github.com/felixzapata/gulp-i18n-pug
 *
 * Copyright (c) 2016 Félix Zapata
 * Licensed under the MIT license.
 */

'use strict';

var pug = require('pug');
var glob = require('glob');
var fs = require('fs');
var gutil = require('gulp-util');
var path = require('path');
var YAML = require('js-yaml');
var through = require('through2');
var PLUGIN_NAME = 'gulp-i18n-pug';

function getExtension(filepath) {
    return path.extname(filepath);
}

function setExtension(ext) {
    if (ext.charAt(0) !== '.') {
        ext = '.' + ext;
    }
    return ext;
}

function readYAML(filepath, options) {
    var src = fs.readFileSync(filepath, options);
    var result;
    try {
        result = YAML.load(src);
        return result;
    } catch (e) {
        throw new gutil.PluginError(PLUGIN_NAME, 'Unable to parse "' + filepath + '" file (' + e.problem + ').', e);
    }
}

function readJSON(filepath, options) {
    var src = fs.readFileSync(filepath, options);
    var result;
    try {
        result = JSON.parse(src);
        return result;
    } catch (e) {
        throw new gutil.PluginError(PLUGIN_NAME, 'Unable to parse "' + filepath + '" file (' + e.message + ').', e);
    }
}

function addLocaleExtensionDest(file, locale, outputExt) {

    var dest, ext;

    locale = locale.toLowerCase();
    ext = getExtension(file);

    function getBaseName() {
        return path.basename(file).split('.')[0];
    }

    if (ext) {
        dest = path.join(path.basename(file, ext) + '.' + locale);
    } else {
        dest = path.join(getBaseName() + '.' + locale);
    }

    dest += setExtension(outputExt);

    return dest;
}

function addLocaleDirnameDest(file, locale, outputExt) {
    var base, dest, ext = getExtension(file);
    if (ext) {
        dest = path.join(locale, path.dirname(file), path.basename(file, ext) + setExtension(ext));
    } else {
        if (/(\/|\*+)$/i.test(file)) {
            base = file.split('/');
            dest = path.join(path.join.apply(null, base.slice(0, -1)), locale, base.slice(-1)[0]);
        } else {
            dest = path.join(file, locale);
        }
    }
    dest = dest.replace(/\.pug$/i, setExtension(outputExt));
    return dest;
}

function readFile(filepath) {
    var data;
    try {
        if (/(\.yaml|\.yml)$/i.test(filepath)) {
            data = readYAML(filepath);
        } else if (/\.js$/i.test(filepath)) {
            data = require(path.resolve(filepath));
        } else {
            data = readJSON(filepath);
        }
    } catch (_error) {
        throw new gutil.PluginError("Cannot parse file '" + filepath + "': " + _error.message, _error);
    }
    return data;
}

function plugI18nPlugin(customOptions) {

    var baseName;
    var compiledFiles = [];
    var dest;
    var localePath;
    var fileExt;
    var locale;

    var defaultI18nOptions = {
        i18n: {
            namespace: '$i18n',
            localeExtension: false,
            defaultExt: '.html'
        }
    }
    var options = Object.assign({}, customOptions);

    if (!options.i18n) {
        options.i18n = {};
    } else {
        options.i18n = Object.assign(defaultI18nOptions.i18n, customOptions.i18n);
    }

    var locales = options.i18n.locales ? glob.sync(options.i18n.locales) : null;

    var bufferContents = function (file, enc, cb) {

        if (file.isNull()) {
            cb(null, file);
            return;
        }

        if (file.isStream()) {
            cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return;
        }

        if (locales && locales.length) {
            for (var i = 0, len = locales.length; i < len; i++) {
                localePath = locales[i];
                fileExt = localePath.split('.').slice(-1)[0];
                locale = path.basename(localePath, '.' + fileExt);
                baseName = path.basename(file.path);
                gutil.log('Loading locale ' + locale);
                gutil.log('Reading translation data: ' + localePath);
                if (!options.data) {
                    options.data = {};
                }
                options.data[options.i18n.namespace] = readFile(localePath);
                options.data['$localeName'] = locale;

                if (options.i18n.localeExtension) {
                    dest = addLocaleExtensionDest(baseName, locale, options.i18n.defaultExt);
                } else {
                    dest = addLocaleDirnameDest(file.relative, locale, options.i18n.defaultExt);
                }

                compiledFiles.push(new gutil.File({
                    base: options.i18n.dest,
                    contents: new Buffer(pug.compileFile(file.path, options)(options.data)),
                    path: path.join(options.i18n.dest, dest)
                }));

            }

        } else {
            gutil.log('Locales files not found. Nothing to translate');
            compiledFiles.push(new gutil.File({
                base: __dirname,
                cwd: __dirname,
                contents: new Buffer(pug.compileFile(file.path, options)(options.data)),
                path: path.basename(file.path).split('.')[0] + options.i18n.defaultExt
            }));
        }
        cb();
    }

    var endStream = function (cb) {
        if (!compiledFiles || compiledFiles.length === 0) {
            cb();
            return;
        }
        for (var i = 0, len = compiledFiles.length; i < len; i++) {
            this.push(compiledFiles[i]);
        }
        cb();
    }

    return through.obj(bufferContents, endStream);
}

module.exports = plugI18nPlugin;
<|endoftext|>


<commit_before>'use strict';

var dgram = require('dgram'),
    util = require('util');

/*
 * Generates an xAP message block, given a messageType and object.
 */
var generateMessage = function (messageType, message) {
    var body = messageType + '\n{\n';
    Object.keys(message).forEach(function(key) {
      body += util.format('%s=%s\n', key, message[key]);
    });
    body += '}\n';
    return body;
};

var XAPBroadcaster = function (options) {
  var self = this;
  options = options || {};

  self.version = options.version || 12;
  self.class = options.class;
  self.source = options.source;
  self.uid = options.uid;
  self.broadcast = options.broadcast || '255.255.255.255';
  self.port = options.port || 3639;

  if (typeof self.class === 'undefined') {
    throw new Error('Must provide a class.');
  }
  if (typeof self.source === 'undefined') {
    throw new Error('Must provide a source.');
  }
  if (typeof self.uid === 'undefined') {
    throw new Error('Must provide a uid.');
  }
}

XAPBroadcaster.prototype.send = function (messageType, message, callback) {
  var self = this,
      client = dgram.createSocket('udp4'),
      header = generateMessage(
        'xap-header',
        {v: self.version, hop: 1, uid: self.uid, class: self.class, source: self.source}),
       message = generateMessage(messageType, message),
       buffer = new Buffer(header + message);

  client.bind(function() {
    client.setBroadcast(true);
    client.send(buffer, 0, buffer.length, self.port, self.broadcast, function(err, bytes) {
      client.close();
      if (typeof callback === 'function') {
        callback(err, bytes);
      }
    });
  });
}

exports.XAPBroadcaster = XAPBroadcaster;
exports.generateMessage = generateMessage;
<commit_msg>Formatting<commit_after>'use strict';

var dgram = require('dgram'),
    util = require('util');

/*
 * Generates an xAP message block, given a messageType and object.
 */
var generateMessage = function (messageType, message) {
    var body = messageType + '\n{\n';
    Object.keys(message).forEach(function(key) {
      body += util.format('%s=%s\n', key, message[key]);
    });
    body += '}\n';
    return body;
};

var XAPBroadcaster = function (options) {
  var self = this;
  options = options || {};

  self.version = options.version || 12;
  self.class = options.class;
  self.source = options.source;
  self.uid = options.uid;
  self.broadcast = options.broadcast || '255.255.255.255';
  self.port = options.port || 3639;

  if (typeof self.class === 'undefined') {
    throw new Error('Must provide a class.');
  }
  if (typeof self.source === 'undefined') {
    throw new Error('Must provide a source.');
  }
  if (typeof self.uid === 'undefined') {
    throw new Error('Must provide a uid.');
  }
}

XAPBroadcaster.prototype.send = function (messageType, message, callback) {
  var self = this,
      client = dgram.createSocket('udp4'),
      header = generateMessage(
        'xap-header',
        {v: self.version, hop: 1, uid: self.uid, class: self.class, source: self.source}),

  message = generateMessage(messageType, message),
  buffer = new Buffer(header + message);

  client.bind(function() {
    client.setBroadcast(true);
    client.send(buffer, 0, buffer.length, self.port, self.broadcast, function(err, bytes) {
      client.close();
      if (typeof callback === 'function') {
        callback(err, bytes);
      }
    });
  });
}

exports.XAPBroadcaster = XAPBroadcaster;
exports.generateMessage = generateMessage;
<|endoftext|>


<commit_before>const upperFirst = require('lodash.upperfirst');
const isEmpty = require('lodash.isempty');

/**
 * a plugin that help schema to build string collection field
 * which is an array containt batch string
 *
 * @param {MongooseSchema} schema - mongoose schema that use this plugin
 * @param {object} [options] - plugin configuration
 * @param {string} [options.fieldName=tags] - the name place in schema
 * @param {boolean} [options.isIndex=false] - whether index in target field
 * @param {boolean} [options.isUnique=true] - whether unique the content in the collection
 * @param {object} [options.elementOptions] - collection element options
 * @param {object} [options.updateOptions] - collection default update options
 *      for add, replace and get methods.
 *      you can also override when using the specified method
 */
const plugin = (schema, options = {}) => {
  const {
    fieldName = 'tags',
    isIndex = false,
    isUnique = true,
  } = options;
  const elementOptions = Object.assign({
    type: String,
    index: isIndex,
  }, options.elementOptions);
  // after mongoose v4 new is an option
  // to get the updated document
  // instead of updating the previous document
  const defautlUpdateOptions = Object.assign({
    new: true,
    upsert: true,
  }, options.updateOptions);

  const upperName = upperFirst(fieldName);
  const pushOperator = isUnique ? '$addToSet' : '$push';
  const pullOperator = '$pullAll';
  const methods = {
    get: `get${upperName}`,
    add: `add${upperName}`,
    remove: `remove${upperName}`,
    replace: `replace${upperName}`,
    batchReplace: `batchReplace${upperName}`,
  };
  /**
   * @namespace model
   */
  const model = {};

  /**
   * sugar method that get target filed as single result
   *
   * @memberof model
   * @param {object} [query={}] - mongoose query that place in this.findOne
   * @return {Promise.<array>} target field
   * @example
   * model.getTags({ _id: 'targetnotexists' }).then(console.log);
   * // undefined
   *
   * model.insert({ _id: 'test', tags: ['test'] });
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['test]
   */
  model.get = function get(query = {}) {
    return this.findOne(query).select(`${fieldName}`).then(document => document && document[fieldName]);
  };

  const updateArguments = (collection, updateOptions) => ({
    updatePatch: {
      $set: {
        [fieldName]: collection,
      },
    },
    operationOpts: Object.assign(defautlUpdateOptions, updateOptions),
  });

  /**
   * remove element array from target field
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will remove from target document
   * @return {Promise.<object>} updated target document
   * @example
   * // { _id: 'test', tags: ['t1', 't2'] }
   * model.removeTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t2'] }
   * model.removeTags({ _id: 'test' }, ['t2']).then(console.log);
   * // { _id: 'test', tags: [] }
   */
  model.remove = function remove(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const updatePatch = {
      [pullOperator]: {
        [fieldName]: collection,
      },
    };
    const { operationOpts } = updateArguments(collection, updateOptions);

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * add string array to target field
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} updated target document
   * @example
   * model.addTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t1'] }
   * model.addTags({ _id: 'test' }, ['t2']).then(console.log);
   * // { _id: 'test', tags: ['t1', 't2'] }
   */
  model.add = function add(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const updatePatch = {
      [pushOperator]: {
        [fieldName]: {
          $each: collection,
        },
      },
    };
    const { operationOpts } = updateArguments(collection, updateOptions);

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * update document's collection filed,
   * which is first document find out by given query.
   * replace collection field with given collection
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} mongoose udpate result
   * @example
   * model.replaceTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t1'] }
   * model.replaceTags({ _id: 'test' }, ['t2', 't3']).then(console.log);
   * // { _id: 'test', tags: ['t2', 't3'] }
   */
  model.replace = function replace(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const { updatePatch, operationOpts } = updateArguments(collection, updateOptions);

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * batch update documents' collection filed
   * by replace it with given collection
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} mongoose udpate result
   * @example
   * model.batchReplaceTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['t1']
   * model.batchReplaceTags({ _id: 'test' }, ['t2', 't3']).then(console.log);
   * // { "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['t2', 't3']
   */
  model.batchReplace = function batchReplace(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const { updatePatch, operationOpts } = updateArguments(collection, updateOptions);

    return this.update(query, updatePatch, operationOpts).exec();
  };

  schema.add({
    [fieldName]: [elementOptions],
  });
  Object.keys(methods).forEach((key) => {
    schema.statics[methods[key]] = model[key];
  }, this);
};

module.exports = plugin;
<commit_msg>feat(batchAdd): add method batchAdd to support new requirements<commit_after>const upperFirst = require('lodash.upperfirst');
const isEmpty = require('lodash.isempty');

/**
 * a plugin that help schema to build string collection field
 * which is an array containt batch string
 *
 * @param {MongooseSchema} schema - mongoose schema that use this plugin
 * @param {object} [options] - plugin configuration
 * @param {string} [options.fieldName=tags] - the name place in schema
 * @param {boolean} [options.isIndex=false] - whether index in target field
 * @param {boolean} [options.isUnique=true] - whether unique the content in the collection
 * @param {object} [options.elementOptions] - collection element options
 * @param {object} [options.updateOptions] - collection default update options
 *      for add, replace and get methods.
 *      you can also override when using the specified method
 */
const plugin = (schema, options = {}) => {
  const { fieldName = 'tags', isIndex = false, isUnique = true } = options;
  const elementOptions = Object.assign(
    {
      type: String,
      index: isIndex,
    },
    options.elementOptions
  );
  // after mongoose v4 new is an option
  // to get the updated document
  // instead of updating the previous document
  const defautlUpdateOptions = Object.assign(
    {
      new: true,
      upsert: true,
    },
    options.updateOptions
  );

  const upperName = upperFirst(fieldName);
  const pushOperator = isUnique ? '$addToSet' : '$push';
  const pullOperator = '$pullAll';
  const methods = {
    get: `get${upperName}`,
    add: `add${upperName}`,
    batchAdd: `batchAdd${upperName}`,
    remove: `remove${upperName}`,
    replace: `replace${upperName}`,
    batchReplace: `batchReplace${upperName}`,
  };
  /**
   * @namespace model
   */
  const model = {};

  /**
   * sugar method that get target filed as single result
   *
   * @memberof model
   * @param {object} [query={}] - mongoose query that place in this.findOne
   * @return {Promise.<array>} target field
   * @example
   * model.getTags({ _id: 'targetnotexists' }).then(console.log);
   * // undefined
   *
   * model.insert({ _id: 'test', tags: ['test'] });
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['test]
   */
  model.get = function get(query = {}) {
    return this.findOne(query)
      .select(`${fieldName}`)
      .then(document => document && document[fieldName]);
  };

  const updateArguments = (collection, updateOptions) => ({
    updatePatch: {
      $set: {
        [fieldName]: collection,
      },
    },
    operationOpts: Object.assign(defautlUpdateOptions, updateOptions),
  });

  /**
   * remove element array from target field
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will remove from target document
   * @return {Promise.<object>} updated target document
   * @example
   * // { _id: 'test', tags: ['t1', 't2'] }
   * model.removeTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t2'] }
   * model.removeTags({ _id: 'test' }, ['t2']).then(console.log);
   * // { _id: 'test', tags: [] }
   */
  model.remove = function remove(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const updatePatch = {
      [pullOperator]: {
        [fieldName]: collection,
      },
    };
    const { operationOpts } = updateArguments(collection, updateOptions);

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * add string array to target field
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} updated target document
   * @example
   * model.addTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t1'] }
   * model.addTags({ _id: 'test' }, ['t2']).then(console.log);
   * // { _id: 'test', tags: ['t1', 't2'] }
   */
  model.add = function add(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const updatePatch = {
      [pushOperator]: {
        [fieldName]: {
          $each: collection,
        },
      },
    };
    const { operationOpts } = updateArguments(collection, updateOptions);

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * batch add element to collection
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} mongoose udpate result
   * @example
   * model.batchAddTags({ _id: { $in: ['id1', 'id2] } }, ['t1', 't2']).then(console.log);
   * // { "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 }
   * model.getTags({ _id: 'id1' }).then(console.log);
   * // ['t1', 't2']
   * model.batchAddTags({ _id: { $in: ['id1', 'id2] } }, ['t2', 't3']).then(console.log);
   * // { "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }
   * model.getTags({ _id: 'id2' }).then(console.log);
   * // ['t1', 't2', 't3']
   */
  model.batchAdd = function batchAdd(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const updatePatch = {
      [pushOperator]: {
        [fieldName]: {
          $each: collection,
        },
      },
    };
    const { operationOpts } = updateArguments(collection, updateOptions);

    return this.update(query, updatePatch, operationOpts).exec();
  };

  /**
   * update document's collection filed,
   * which is first document find out by given query.
   * replace collection field with given collection
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} mongoose udpate result
   * @example
   * model.replaceTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { _id: 'test', tags: ['t1'] }
   * model.replaceTags({ _id: 'test' }, ['t2', 't3']).then(console.log);
   * // { _id: 'test', tags: ['t2', 't3'] }
   */
  model.replace = function replace(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const { updatePatch, operationOpts } = updateArguments(
      collection,
      updateOptions
    );

    return this.findOneAndUpdate(query, updatePatch, operationOpts).exec();
  };

  /**
   * batch update documents' collection filed
   * by replace it with given collection
   *
   * @memberof model
   * @param {object} query - mongoose query to find out update target
   * @param {array} collection - string collection will add to target document
   * @return {Promise.<object>} mongoose udpate result
   * @example
   * model.batchReplaceTags({ _id: 'test' }, ['t1']).then(console.log);
   * // { "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['t1']
   * model.batchReplaceTags({ _id: 'test' }, ['t2', 't3']).then(console.log);
   * // { "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }
   * model.getTags({ _id: 'test' }).then(console.log);
   * // ['t2', 't3']
   */
  model.batchReplace = function batchReplace(query, collection, updateOptions) {
    if (isEmpty(query)) {
      return Promise.reject(new Error('query should not be empty'));
    }

    const { updatePatch, operationOpts } = updateArguments(
      collection,
      updateOptions
    );

    return this.update(query, updatePatch, operationOpts).exec();
  };

  schema.add({
    [fieldName]: [elementOptions],
  });
  Object.keys(methods).forEach((key) => {
    schema.statics[methods[key]] = model[key];
  }, this);
};

module.exports = plugin;
<|endoftext|>


<commit_before>const { EventEmitter } = require('events')
const flowdock = require('flowdock')
const format = require('./format')

const flowPath = flow =>
  `${flow.organization.parameterized_name}/${flow.parameterized_name}`

const findFlow = identifier => flow =>
  flow.id === identifier ||
    flow.parameterized_name === identifier ||
    flowPath(flow) === identifier ||
    flow.name.toLowerCase() === identifier.toLowerCase()

const isFlowSelected = flows => flow =>
  flows.find(identifier => findFlow(identifier)(flow))

const callRes = (session, method, ...args) =>
  new Promise((resolve, reject) =>
    session[method](...args, (err, body, res) =>
      err ? reject(err) : resolve(res)))

const call = (...args) =>
  callRes(...args).then(res => res.body)

const getRawState = session =>
  callRes(session, 'flows')
    .then(res => ({
      userId: res.headers['flowdock-user'],
      flows: res.body
    }))

function Flowdock (config) {
  const emitter = new EventEmitter()
  const session = new flowdock.Session(config.token)
  const onError = err => emitter.emit('error', err)
  const state = {}

  const onEvent = event =>
    event.user !== state.userId && emitter.emit(`raw:${event.event}`, event)

  const onLoad = newState => {
    const flows = newState.flows
      .filter(config.flows ? isFlowSelected(config.flows) : flow => flow.joined)
      .map(flow => flow.id)

    if (state.stream) {
      state.stream.removeAllListeners()
    }

    state.stream = session.stream(flows, config.streamConfig)
      .on('message', onEvent)
      .on('error', onError)
  }

  const onMessage = message =>
    emitter.emit('message', format.message(state)(message))

  const onJoinUser = message =>
    state.usersById[message.user] = format.user(message.content.user)

  const reload = () => {
    getRawState(session)
      .then(format.state)
      .then(state => emitter.emit('load', state))
      .catch(onError)
  }

  emitter.on('load', newState => Object.assign(state, newState))
  emitter.on('load', onLoad)
  emitter.on('raw:message', onMessage)
  emitter.on('raw:backend.join.user', onJoinUser)
  emitter.on('raw:flow-add', reload)
  emitter.on('raw:source-remove', reload)

  emitter.mention = user => `@${user.name}`
  emitter.address = (user, text) => `${emitter.mention(user)}, ${text}`

  emitter.mentions = message =>
    message.raw.tags
      .filter(tag => tag.startsWith(':user:'))
      .map(tag => tag.split(':').pop())
      .map(id => state.usersById[id])

  emitter.isMentioned = (user, message) =>
    message.raw.tags.includes(`:user:${user.id}`)

  const sendFlow = (message, text) =>
    call(session, 'threadMessage', message.raw.flow, message.raw.thread_id, text, [])

  const sendPrivate = (message, text) =>
    call(session, 'privateMessage', message.author.id, text, [])

  emitter.send = (message, text) =>
    (message.raw.to ? sendPrivate : sendFlow)(message, text)
      .then(format.message(state))

  const editFlow = (message, data) => {
    const flowObject = state.flowsById[message.raw.flow]
    const flow = flowObject.parameterized_name
    const org = flowObject.organization.parameterized_name

    return call(session, 'editMessage', flow, org, message.id, data)
      .then(() => call(session, 'get', `/flows/${org}/${flow}/messages/${message.id}`, {}))
      .then(format.message(state))
  }

  const editPrivate = (message, data) =>
    call(session, 'put', `/private/${message.raw.to}/messages/${message.id}`, data)
      .then(() => call(session, 'get', `/private/${message.raw.to}/messages/${message.id}`, {}))
      .then(format.message(state))

  emitter.edit = (message, text) =>
    (message.raw.to ? editPrivate : editFlow)(message, { content: text })

  emitter.tag = (message, tags) =>
    editFlow(message, { tags: message.raw.tags.concat(tags) })

  emitter.messageRoom = (room, text) => {
    const flow = state.flows.find(findFlow(room))

    return call(session, 'message', flow ? flow.id : room, text, [])
      .then(format.message(state))
  }

  emitter.end = () => state.stream && state.stream.end()

  reload()

  return emitter
}

module.exports = Flowdock
<commit_msg>Make sure mentionned user exist<commit_after>const { EventEmitter } = require('events')
const flowdock = require('flowdock')
const format = require('./format')

const flowPath = flow =>
  `${flow.organization.parameterized_name}/${flow.parameterized_name}`

const findFlow = identifier => flow =>
  flow.id === identifier ||
    flow.parameterized_name === identifier ||
    flowPath(flow) === identifier ||
    flow.name.toLowerCase() === identifier.toLowerCase()

const isFlowSelected = flows => flow =>
  flows.find(identifier => findFlow(identifier)(flow))

const callRes = (session, method, ...args) =>
  new Promise((resolve, reject) =>
    session[method](...args, (err, body, res) =>
      err ? reject(err) : resolve(res)))

const call = (...args) =>
  callRes(...args).then(res => res.body)

const getRawState = session =>
  callRes(session, 'flows')
    .then(res => ({
      userId: res.headers['flowdock-user'],
      flows: res.body
    }))

function Flowdock (config) {
  const emitter = new EventEmitter()
  const session = new flowdock.Session(config.token)
  const onError = err => emitter.emit('error', err)
  const state = {}

  const onEvent = event =>
    event.user !== state.userId && emitter.emit(`raw:${event.event}`, event)

  const onLoad = newState => {
    const flows = newState.flows
      .filter(config.flows ? isFlowSelected(config.flows) : flow => flow.joined)
      .map(flow => flow.id)

    if (state.stream) {
      state.stream.removeAllListeners()
    }

    state.stream = session.stream(flows, config.streamConfig)
      .on('message', onEvent)
      .on('error', onError)
  }

  const onMessage = message =>
    emitter.emit('message', format.message(state)(message))

  const onJoinUser = message =>
    state.usersById[message.user] = format.user(message.content.user)

  const reload = () => {
    getRawState(session)
      .then(format.state)
      .then(state => emitter.emit('load', state))
      .catch(onError)
  }

  emitter.on('load', newState => Object.assign(state, newState))
  emitter.on('load', onLoad)
  emitter.on('raw:message', onMessage)
  emitter.on('raw:backend.join.user', onJoinUser)
  emitter.on('raw:flow-add', reload)
  emitter.on('raw:source-remove', reload)

  emitter.mention = user => `@${user.name}`
  emitter.address = (user, text) => `${emitter.mention(user)}, ${text}`

  emitter.mentions = message =>
    message.raw.tags
      .filter(tag => tag.startsWith(':user:'))
      .map(tag => tag.split(':').pop())
      .map(id => state.usersById[id])
      .filter(user => user)

  emitter.isMentioned = (user, message) =>
    message.raw.tags.includes(`:user:${user.id}`)

  const sendFlow = (message, text) =>
    call(session, 'threadMessage', message.raw.flow, message.raw.thread_id, text, [])

  const sendPrivate = (message, text) =>
    call(session, 'privateMessage', message.author.id, text, [])

  emitter.send = (message, text) =>
    (message.raw.to ? sendPrivate : sendFlow)(message, text)
      .then(format.message(state))

  const editFlow = (message, data) => {
    const flowObject = state.flowsById[message.raw.flow]
    const flow = flowObject.parameterized_name
    const org = flowObject.organization.parameterized_name

    return call(session, 'editMessage', flow, org, message.id, data)
      .then(() => call(session, 'get', `/flows/${org}/${flow}/messages/${message.id}`, {}))
      .then(format.message(state))
  }

  const editPrivate = (message, data) =>
    call(session, 'put', `/private/${message.raw.to}/messages/${message.id}`, data)
      .then(() => call(session, 'get', `/private/${message.raw.to}/messages/${message.id}`, {}))
      .then(format.message(state))

  emitter.edit = (message, text) =>
    (message.raw.to ? editPrivate : editFlow)(message, { content: text })

  emitter.tag = (message, tags) =>
    editFlow(message, { tags: message.raw.tags.concat(tags) })

  emitter.messageRoom = (room, text) => {
    const flow = state.flows.find(findFlow(room))

    return call(session, 'message', flow ? flow.id : room, text, [])
      .then(format.message(state))
  }

  emitter.end = () => state.stream && state.stream.end()

  reload()

  return emitter
}

module.exports = Flowdock
<|endoftext|>


<commit_before>var pg;
try {
  pg = require('pg');
} catch (e) {
  try {
    pg = require('pg.js');
  } catch (e2) {
    console.log(e2);
    throw new Error("Could not access pg module. Please install either pg or pg.js.");
  }
}

var domain = require('domain');

var __logFn = null, makeDB;

module.exports = function PostgresGen(con) { return makeDB.call(null, con); };
module.exports.log = function log(fn) {
  if (!!fn && typeof fn === 'function') __logFn = fn;
};

var nextId = (function() {
  var id = 0;
  return function() {
    if (id < Number.MAX_SAFE_INTEGER - 1) { id++; }
    else id = 0;
    return id;
  };
})();

var normalize = module.exports.normalizeQueryArguments = function(args) {
  if (args.length === 1 && args[0].hasOwnProperty('query') && args[0].hasOwnProperty('params')) return args[0];

  var q = args[0];
  var params = [];
  var options = {};
  var questions = q.indexOf('?') >= 0;
  var dollarNums = !questions && /\$[0-9]+/g.test(q);
  var dollarNames = !questions && !dollarNums && /\$[-a-zA-Z0-9_]+/g.test(q);
  var i;

  if (args.length > 1) {
    if (questions || dollarNums) {
      // these require weird param arguments handling
      var paramCount = questions ? (q.match(/\?/g) || []).length : (q.match(/\$[a-zA-Z]/g) || []).length;
      var argLen = args.length;
      if (Array.isArray(args[1])) {
        params = args[1];
        argLen = params.length + args.length - 1;
      } else {
        for (i = 1; i <= paramCount; i++) params.push(args[i]);
      }

      if (questions) {
        i = 1;
        q = q.replace(/\?/g, function() { return '$' + i++; });
      }

      if (argLen > params.length + 1) options = args[args.length - 1];
    } else if (dollarNames) {
      // dollarNames requires conversion to dollarNumbers
      var ps = args[1];
      var idx = 1, arr = [];
      q = q.replace(/(\$[-a-zA-Z0-9_]*)/g, function(m) {
        arr.push(ps[m.slice(1)]);
        return '$' + idx++;
      });
      params = arr;

      if (args.length > 2) options = args[args.length - 1];
    } else {
      options = args[args.length - 1];
    }
  }

  // TODO: option to handle array as literal in addition to looking for a property on the array

  // find any array parameters and replace them and their reference with their contents
  for (i = 0; i < params.length; i++) {
    var p = params[i];
    if (Array.isArray(p)) {
      var arargs = ['$' + (i + 1)];
      for (var j = 1; j < p.length; j++) arargs.push('$' + (j + params.length));
      if (p.literalArray) {
        q = q.replace('$' + (i + 1), 'ARRAY[' + arargs.join(', ') + ']');
      } else {
        q = q.replace('$' + (i + 1), '(' + arargs.join(', ') + ')');
      }
      params.splice(i, 1, p[0]);
      params = params.concat(p.slice(1));
    }
  }

  var res = { query: q, params: params, options: options };
  return res;
};

var root;
root = {
  _connect: function() {
    var me = this;
    return new Promise(function(resolve, reject) {
      if (!me.pool) {
        var client = new pg.Client(me.conStr);
        client.connect(function(err) {
          if (err) reject(err);
          else resolve([client]);
        });
      } else {
        pg.connect(me.conStr, function(err, client, done) {
          if (err) reject(err);
          else resolve([client, done]);
        });
      }
    });
  },
  _query: function(connection, obj) {
    var me = this;
    var con, cleanup = false;
    if (!!connection) {
      con = Promise.resolve([connection]);
    } else if (!root._hasCurrent.call(me)) {
      con = root._connect.call(me);
      cleanup = true;
    } else {
      con = root._currentVal.call(me).begin();
    }

    return con.then(function(c) {
      var start = Date.now();

      return new Promise(function(resolve, reject) {
        c[0].query(obj.query, obj.params, function(err, res) {
          var time = Date.now() - start;

          try {
            var log = me.logFn || __logFn;
            if (log !== null) log({ name: me.name, query: obj.query, params: obj.params, string: me.conStr, time: time, error: err });
          } catch (e) { console.log(e); }

          if (err) reject(err);
          else resolve(res);
          if (cleanup) {
            if (me.pool) c[1]();
            else c[0].end();
          }
        });
      });
    });
  },
  _nonQuery: function(connection, obj) {
    return root._query.call(this, connection, obj).then(function(res) {
      return res.rowCount;
    });
  },
  _queryOne: function(connection, obj) {
    return root._query.call(this, connection, obj).then(function(res) {
      if (res.rows.length > 0) {
        return res.rows[0];
      } else {
        return Promise.reject("No rows were returned where at least one was expected.");
      }
    });
  },
  _hasCurrent: function() {
    return !!root._currentVal.call(this);
  },
  _currentVal: function() {
    var res = domain.active;
    if (!!res) res = res.__pggenContext;
    if (!!res) res = res[this.connectionString()];
    if (!!res) res = res.trans;
    return res;
  },
  _killCurrent: function() {
    var res = domain.active;
    if (!!res) res = res.__pggenContext;
    if (!!res) delete res[this.connectionString()];
  },
  _current: function(fn) {
    var currentDomain = domain.active;
    if (!!!currentDomain) currentDomain = domain.create();
    var ctx = currentDomain.__pggenContext;
    if (!!!ctx) {
      ctx = currentDomain.__pggenContext = {};
    }
    var str = this.connectionString();
    if (!!!ctx[str]) ctx = ctx[str] = {};
    else ctx = ctx[str];

    var trans = ctx.trans || this.newTrans(),
        startTrans = ctx.trans;
    ctx.trans = trans;

    currentDomain.run(function() {
      fn({
        domain: currentDomain,
        trans: trans,
        init: !!!startTrans
      });
    });
  },
  _transact: function(gen) {
    var me = this;
    return new Promise(function(resolve, reject) {
      root._current.call(me, function(ctx) {
        root._transactMiddle.call(me, gen, { resolve: resolve, reject: reject }, ctx);
      });
    });
  },
  _newTransact: function(gen) {
    var currentDomain = domain.create();
    var ctx = currentDomain.__pggenContext = {};
    ctx = ctx[this.connectionString()] = {};
    var me = this;
    ctx.trans = this.newTrans();

    return new Promise(function(resolve, reject) {
      currentDomain.run(function() {
        root._transactMiddle.call(me, gen, { resolve: resolve, reject: reject }, {
          domain: currentDomain,
          trans: ctx.trans,
          init: true
        });
      });
    });
  },
  _transactMiddle: function(gen, deferred, ctx) { // TODO: fix me
    var trans = ctx.trans;
    var g = gen(trans);
    var next, abort;
    var me = this;
    abort = function(e) {
      // rollback if me is the top-level transaction
      if (ctx.init) {
        try {
          trans.rollback().then(function() {
            // closing the transaction, so drop the current trans context
            root._killCurrent.call(me);
            deferred.reject(e);
          }, function(e2) { deferred.reject([e, e2]); });
        } catch (e2) { deferred.reject([e, e2]); }
      } else { deferred.reject(e); }
    };
    next = function(res) {
      if (res.done) {
        if (ctx.init) {
          trans.commit().then(function() {
            // closing the transaction, so drop the current trans context
            root._killCurrent.call(me);
            deferred.resolve(res.value);
          });
        } else deferred.resolve(res.value);
      } else if (res.value && typeof res.value.then === 'function') {
        res.value.then(function(r) {
          try {
            next(g.next(r));
          } catch (e) {
            abort(e);
          }
        }, function(e) {
          abort(e);
        });
      } else {
        try {
          next(g.next(res.value));
        } catch (e) { abort(e); }
      }
    };
    next(g.next());
  }
};

makeDB = (function() {
  var proto = {};
  // transaction prototype
  var tproto = Object.create(proto);

  proto.name = '';
  proto.logFn = null;
  proto.pool = true;

  proto.transaction = function(gen) { return root._transact.call(this, gen); };
  proto.newTransaction = function(gen) { return root._newTransact.call(this, gen); };
  proto.hasTransaction = function() { return root._hasTransaction.call(this); };
  proto.currentTransaction = function() {
    if (this.hasTransaction()) return domain.active.__pggenContext.trans;
    else return undefined;
  };
  proto.query = function() {
    return root._query.call(this, null, normalize(arguments));
  };
  proto.queryOne = function() {
    return root._queryOne.call(this, null, normalize(arguments));
  };
  proto.nonQuery = function() {
    return root._nonQuery.call(this, null, normalize(arguments));
  };
  proto.log = function(fn) {
    if (!!fn && typeof fn === 'function') this.logFn = fn;
  };
  proto.connectionString = function() { return this.conStr; };

  tproto.close = function() {
    if (this.connection && this.closeOnDone) {
      if (this.pool) this.whenDone();
      else this.connection.end();

      this.connection = null;
      this.active = false;
      this.done = true;
    }
    return Promise.resolve(true);
  };

  tproto.begin = function() {
    var t = this;
    if (t.done) {
      return Promise.reject("This transaction is already complete.");
    } else if (!t.active) {
      return root._connect.call(t).then(function(c) {
        t.connection = c[0];
        t.active = true;
        if (t.pool) t.whenDone = c[1];
        return t.nonQuery('begin;').then(function() { return c; });
      });
    } else {
      return Promise.resolve([t.connection]);
    }
  };

  tproto.commit = function() {
    var t = this;
    if (t.done) {
      return Promise.reject("This transaction is already complete.");
    } else if (!t.active && !t.done) {
      t.done = true;
      return Promise.resolve(true);
    } else {
      return t.query('commit;').then(function() {
        t.successful = true;
        return t.close();
      }, function(err) {
        t.successful = false;
        return t.close().then(function() { throw err; });
      });
    }
  };

  tproto.rollback = function() {
    var t = this;
    if (!t.active || t.done) {
      return Promise.reject("This transaction is already complete.");
    } else {
      return t.query('rollback;').then(function() {
        t.successful = false;
        return t.close();
      }, function(err) {
        t.successful = false;
        return t.close().then(function() { throw err; });
      });
    }
  };

  tproto.query = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._query.call(t, t.connection, args); });
    else return root._query.call(t, t.connection, args);
  };

  tproto.queryOne = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._queryOne.call(t, t.connection, args); });
    else return root._queryOne.call(t, t.connection, args);
  };

  tproto.nonQuery = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._nonQuery.call(t, t.connection, args); });
    else return root._nonQuery.call(t, t.connection, args);
  };

  tproto.active = false;
  tproto.done = false;
  tproto.closeOnDone = true;
  tproto.successful = false;
  tproto.whenDone = null;

  return function PostgresGen(arg) {
    if (arg === undefined) throw new Error("You must supply a connection configuration.");
    var mid = Object.create(proto);
    var res = Object.create(mid);

    mid.newTrans = function newTrans() {
      var trans = Object.create(tproto);
      trans.conStr = mid.conStr;
      for (var k in res) trans[k] = res[k];
      trans.id = nextId();
      return trans;
    };

    if (typeof arg === "string") {
      mid.conStr = arg;
    } else if (typeof arg === "object") {
      if (!!arg.string) {
        mid.conStr = arg.string;
      } else {
        mid.conStr = 'postgresql://';
        if (arg.user) {
          mid.conStr += arg.user;
          if (arg.password) mid.conStr += ':' + arg.password;
          mid.conStr += '@';
        }
        if (!!arg.host) mid.conStr += arg.host;
        else mid.conStr += 'localhost';
        if (!!arg.port) mid.conStr += ':' + arg.port;
        if (arg.db) mid.conStr += '/' + arg.db;
      }
      if (arg.hasOwnProperty('pool')) mid.pool = !!arg.pool;
      if (!!arg.log && typeof arg.log === 'function') res.logFn = arg.log;
      if (!!arg.name && typeof arg.name === 'string') res.name = arg.name;
    }

    return res;
  };
}());
<commit_msg>make sure transactions use the same connection if the domain falls over<commit_after>var pg;
try {
  pg = require('pg');
} catch (e) {
  try {
    pg = require('pg.js');
  } catch (e2) {
    console.log(e2);
    throw new Error("Could not access pg module. Please install either pg or pg.js.");
  }
}

var domain = require('domain');

var __logFn = null, makeDB;

module.exports = function PostgresGen(con) { return makeDB.call(null, con); };
module.exports.log = function log(fn) {
  if (!!fn && typeof fn === 'function') __logFn = fn;
};

var nextId = (function() {
  var id = 0;
  return function() {
    if (id < Number.MAX_SAFE_INTEGER - 1) { id++; }
    else id = 0;
    return id;
  };
})();

var normalize = module.exports.normalizeQueryArguments = function(args) {
  if (args.length === 1 && args[0].hasOwnProperty('query') && args[0].hasOwnProperty('params')) return args[0];

  var q = args[0];
  var params = [];
  var options = {};
  var questions = q.indexOf('?') >= 0;
  var dollarNums = !questions && /\$[0-9]+/g.test(q);
  var dollarNames = !questions && !dollarNums && /\$[-a-zA-Z0-9_]+/g.test(q);
  var i;

  if (args.length > 1) {
    if (questions || dollarNums) {
      // these require weird param arguments handling
      var paramCount = questions ? (q.match(/\?/g) || []).length : (q.match(/\$[a-zA-Z]/g) || []).length;
      var argLen = args.length;
      if (Array.isArray(args[1])) {
        params = args[1];
        argLen = params.length + args.length - 1;
      } else {
        for (i = 1; i <= paramCount; i++) params.push(args[i]);
      }

      if (questions) {
        i = 1;
        q = q.replace(/\?/g, function() { return '$' + i++; });
      }

      if (argLen > params.length + 1) options = args[args.length - 1];
    } else if (dollarNames) {
      // dollarNames requires conversion to dollarNumbers
      var ps = args[1];
      var idx = 1, arr = [];
      q = q.replace(/(\$[-a-zA-Z0-9_]*)/g, function(m) {
        arr.push(ps[m.slice(1)]);
        return '$' + idx++;
      });
      params = arr;

      if (args.length > 2) options = args[args.length - 1];
    } else {
      options = args[args.length - 1];
    }
  }

  // TODO: option to handle array as literal in addition to looking for a property on the array

  // find any array parameters and replace them and their reference with their contents
  for (i = 0; i < params.length; i++) {
    var p = params[i];
    if (Array.isArray(p)) {
      var arargs = ['$' + (i + 1)];
      for (var j = 1; j < p.length; j++) arargs.push('$' + (j + params.length));
      if (p.literalArray) {
        q = q.replace('$' + (i + 1), 'ARRAY[' + arargs.join(', ') + ']');
      } else {
        q = q.replace('$' + (i + 1), '(' + arargs.join(', ') + ')');
      }
      params.splice(i, 1, p[0]);
      params = params.concat(p.slice(1));
    }
  }

  var res = { query: q, params: params, options: options };
  return res;
};

var root;
root = {
  _connect: function() {
    var me = this;
    return new Promise(function(resolve, reject) {
      if (!me.pool) {
        var client = new pg.Client(me.conStr);
        client.connect(function(err) {
          if (err) reject(err);
          else resolve([client]);
        });
      } else {
        pg.connect(me.conStr, function(err, client, done) {
          if (err) reject(err);
          else resolve([client, done]);
        });
      }
    });
  },
  _query: function(connection, obj) {
    var me = this;
    var con, cleanup = false;
    if (!!connection) {
      con = Promise.resolve([connection]);
    } else if (!!me.connection) {
      con = Promise.resolve([me.connection]);
    } else if (!root._hasCurrent.call(me)) {
      con = root._connect.call(me);
      cleanup = true;
    } else {
      con = root._currentVal.call(me).begin();
    }

    return con.then(function(c) {
      var start = Date.now();

      return new Promise(function(resolve, reject) {
        c[0].query(obj.query, obj.params, function(err, res) {
          var time = Date.now() - start;

          try {
            var log = me.logFn || __logFn;
            if (log !== null) log({ name: me.name, query: obj.query, params: obj.params, string: me.conStr, time: time, error: err });
          } catch (e) { console.log(e); }

          if (err) reject(err);
          else resolve(res);
          if (cleanup) {
            if (me.pool) c[1]();
            else c[0].end();
          }
        });
      });
    });
  },
  _nonQuery: function(connection, obj) {
    return root._query.call(this, connection, obj).then(function(res) {
      return res.rowCount;
    });
  },
  _queryOne: function(connection, obj) {
    return root._query.call(this, connection, obj).then(function(res) {
      if (res.rows.length > 0) {
        return res.rows[0];
      } else {
        return Promise.reject("No rows were returned where at least one was expected.");
      }
    });
  },
  _hasCurrent: function() {
    return !!root._currentVal.call(this);
  },
  _currentVal: function() {
    var res = domain.active;
    if (!!res) res = res.__pggenContext;
    if (!!res) res = res[this.connectionString()];
    if (!!res) res = res.trans;
    return res;
  },
  _killCurrent: function() {
    var res = domain.active;
    if (!!res) res = res.__pggenContext;
    if (!!res) delete res[this.connectionString()];
  },
  _current: function(fn) {
    var currentDomain = domain.active;
    if (!!!currentDomain) currentDomain = domain.create();
    var ctx = currentDomain.__pggenContext;
    if (!!!ctx) {
      ctx = currentDomain.__pggenContext = {};
    }
    var str = this.connectionString();
    if (!!!ctx[str]) ctx = ctx[str] = {};
    else ctx = ctx[str];

    var trans = ctx.trans || this.newTrans(),
        startTrans = ctx.trans;
    ctx.trans = trans;

    currentDomain.run(function() {
      fn({
        domain: currentDomain,
        trans: trans,
        init: !!!startTrans
      });
    });
  },
  _transact: function(gen) {
    var me = this;
    return new Promise(function(resolve, reject) {
      root._current.call(me, function(ctx) {
        root._transactMiddle.call(me, gen, { resolve: resolve, reject: reject }, ctx);
      });
    });
  },
  _newTransact: function(gen) {
    var currentDomain = domain.create();
    var ctx = currentDomain.__pggenContext = {};
    ctx = ctx[this.connectionString()] = {};
    var me = this;
    ctx.trans = this.newTrans();

    return new Promise(function(resolve, reject) {
      currentDomain.run(function() {
        root._transactMiddle.call(me, gen, { resolve: resolve, reject: reject }, {
          domain: currentDomain,
          trans: ctx.trans,
          init: true
        });
      });
    });
  },
  _transactMiddle: function(gen, deferred, ctx) { // TODO: fix me
    var trans = ctx.trans;
    var g = gen(trans);
    var next, abort;
    var me = this;
    abort = function(e) {
      // rollback if me is the top-level transaction
      if (ctx.init) {
        try {
          trans.rollback().then(function() {
            // closing the transaction, so drop the current trans context
            root._killCurrent.call(me);
            deferred.reject(e);
          }, function(e2) { deferred.reject([e, e2]); });
        } catch (e2) { deferred.reject([e, e2]); }
      } else { deferred.reject(e); }
    };
    next = function(res) {
      if (res.done) {
        if (ctx.init) {
          trans.commit().then(function() {
            // closing the transaction, so drop the current trans context
            root._killCurrent.call(me);
            deferred.resolve(res.value);
          }, abort);
        } else deferred.resolve(res.value);
      } else if (res.value && typeof res.value.then === 'function') {
        res.value.then(function(r) {
          try {
            next(g.next(r));
          } catch (e) {
            abort(e);
          }
        }, function(e) {
          abort(e);
        });
      } else {
        try {
          next(g.next(res.value));
        } catch (e) { abort(e); }
      }
    };
    next(g.next());
  }
};

makeDB = (function() {
  var proto = {};
  // transaction prototype
  var tproto = Object.create(proto);

  proto.name = '';
  proto.logFn = null;
  proto.pool = true;

  proto.transaction = function(gen) { return root._transact.call(this, gen); };
  proto.newTransaction = function(gen) { return root._newTransact.call(this, gen); };
  proto.hasTransaction = function() { return root._hasTransaction.call(this); };
  proto.currentTransaction = function() {
    if (this.hasTransaction()) return domain.active.__pggenContext.trans;
    else return undefined;
  };
  proto.query = function() {
    return root._query.call(this, null, normalize(arguments));
  };
  proto.queryOne = function() {
    return root._queryOne.call(this, null, normalize(arguments));
  };
  proto.nonQuery = function() {
    return root._nonQuery.call(this, null, normalize(arguments));
  };
  proto.log = function(fn) {
    if (!!fn && typeof fn === 'function') this.logFn = fn;
  };
  proto.connectionString = function() { return this.conStr; };

  tproto.close = function() {
    if (this.connection && this.closeOnDone) {
      if (this.pool) this.whenDone();
      else this.connection.end();

      this.connection = null;
      this.active = false;
      this.done = true;
    }
    return Promise.resolve(true);
  };

  tproto.begin = function() {
    var t = this;
    if (t.done) {
      return Promise.reject("This transaction is already complete.");
    } else if (!t.active) {
      return root._connect.call(t).then(function(c) {
        t.connection = c[0];
        t.active = true;
        if (t.pool) t.whenDone = c[1];
        return t.nonQuery('begin;').then(function() { return c; });
      });
    } else {
      return Promise.resolve([t.connection]);
    }
  };

  tproto.commit = function() {
    var t = this;
    if (t.done) {
      return Promise.reject("This transaction is already complete.");
    } else if (!t.active && !t.done) {
      t.done = true;
      return Promise.resolve(true);
    } else {
      return t.query('commit;').then(function() {
        t.successful = true;
        return t.close();
      }, function(err) {
        t.successful = false;
        return t.close().then(function() { throw err; });
      });
    }
  };

  tproto.rollback = function() {
    var t = this;
    if (!t.active || t.done) {
      return Promise.reject("This transaction is already complete.");
    } else {
      return t.query('rollback;').then(function() {
        t.successful = false;
        return t.close();
      }, function(err) {
        t.successful = false;
        return t.close().then(function() { throw err; });
      });
    }
  };

  tproto.query = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._query.call(t, t.connection, args); });
    else return root._query.call(t, t.connection, args);
  };

  tproto.queryOne = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._queryOne.call(t, t.connection, args); });
    else return root._queryOne.call(t, t.connection, args);
  };

  tproto.nonQuery = function() {
    var t = this, args = normalize(arguments);
    if (!t.active) return t.begin().then(function() { return root._nonQuery.call(t, t.connection, args); });
    else return root._nonQuery.call(t, t.connection, args);
  };

  tproto.active = false;
  tproto.done = false;
  tproto.closeOnDone = true;
  tproto.successful = false;
  tproto.whenDone = null;

  return function PostgresGen(arg) {
    if (arg === undefined) throw new Error("You must supply a connection configuration.");
    var mid = Object.create(proto);
    var res = Object.create(mid);

    mid.newTrans = function newTrans() {
      var trans = Object.create(tproto);
      trans.conStr = mid.conStr;
      for (var k in res) trans[k] = res[k];
      trans.id = nextId();
      return trans;
    };

    if (typeof arg === "string") {
      mid.conStr = arg;
    } else if (typeof arg === "object") {
      if (!!arg.string) {
        mid.conStr = arg.string;
      } else {
        mid.conStr = 'postgresql://';
        if (arg.user) {
          mid.conStr += arg.user;
          if (arg.password) mid.conStr += ':' + arg.password;
          mid.conStr += '@';
        }
        if (!!arg.host) mid.conStr += arg.host;
        else mid.conStr += 'localhost';
        if (!!arg.port) mid.conStr += ':' + arg.port;
        if (arg.db) mid.conStr += '/' + arg.db;
      }
      if (arg.hasOwnProperty('pool')) mid.pool = !!arg.pool;
      if (!!arg.log && typeof arg.log === 'function') res.logFn = arg.log;
      if (!!arg.name && typeof arg.name === 'string') res.name = arg.name;
    }

    return res;
  };
}());
<|endoftext|>


<commit_before>var predator = require('predator');

// List of tagNames ordered by their likeliness to be the target of a click event
var clickableWeighting = ['button', 'a', 'label', 'h1', 'h2', 'h3', 'h4', 'i', 'span'];

var types = {
        'button': ['button', 'a'],
        'label': ['label', 'span', 'div'],
        'heading': ['h1', 'h2', 'h3', 'h4'],
        'image': ['img', 'svg'],
        'field': ['input'],
        'all': ['*']
    },
    noelementOfType = 'no elements of type ',
    documentScope = document,
    windowScope = window;

function _pressKey(key, done) {
    var element = documentScope.activeElement;

    element.value += key;

    var keydownEvent = new windowScope.KeyboardEvent('keydown'),
        keyupEvent = new windowScope.KeyboardEvent('keyup'),
        pressKeyEvent = new windowScope.KeyboardEvent('pressKey');

    var method = 'initKeyboardEvent' in keydownEvent ? 'initKeyboardEvent' : 'initKeyEvent';

    keydownEvent[method]('keydown', true, true, windowScope, key, 3, true, false, true, false, false);
    keyupEvent[method]('keyup', true, true, windowScope, key, 3, true, false, true, false, false);
    pressKeyEvent[method]('pressKey', true, true, windowScope, key, 3, true, false, true, false, false);

    element.dispatchEvent(keydownEvent);
    element.dispatchEvent(keyupEvent);
    element.dispatchEvent(pressKeyEvent);

    done(null, element);
}

function _pressKeys(keys, done) {
    keys.split('').forEach(function(key) {
        _pressKey(key, function noop() {});
    });

    done(null, documentScope.activeElement);
}

function findUi(selectors) {
    return documentScope.querySelectorAll(selectors);
}

function _navigate(location, previousElement, done) {
    var callbackTimer;

    function handlewindowScopeError(error) {
        clearTimeout(callbackTimer);

        done(error);
        windowScope.removeEventListener('error', handlewindowScopeError);
    }

    windowScope.addEventListener('error', handlewindowScopeError);
    windowScope.location = location;

    callbackTimer = setTimeout(done, 150);
}

function _getLocation(done) {
    setTimeout(function() {
        done(null, windowScope.location);
    }, 500);
}

function matchElementValue(element, value) {
    return (
            element.textContent.toLowerCase() === value.toLowerCase() ||
            (element.title && element.title.toLowerCase() === value.toLowerCase())
        ) && !predator(element).hidden;
}

function findMatchingElements(value, type, elementsList) {
    return Array.prototype.slice.call(elementsList)
        .filter(function(element) {
            return matchElementValue(element, value);
        });
}

function _findUi(value, type, returnArray, done) {
    if(!done) {
        done = returnArray;
        returnArray = false;
    }

    var elementTypes = types[type];

    if(!elementTypes) {
        return done(new Error(type + ' is not a valid ui type'));
    }

    var elements = findUi(elementTypes);

    if(!elements.length) {
        return done(new Error(noelementOfType + type));
    }

    var result = findMatchingElements(value, type, elements);

    if(!result) {
        return done(new Error(noelementOfType + type + ' with value of ' + value));
    }

    done(null, returnArray ? result : result.shift());
}

function _setValue(value, element, done) {
    element.value = value;

    done(null, element);
}

function _wait(time, done) {
    setTimeout(done, time || 0);
}

function _click(element, done) {
    var rect = element.getBoundingClientRect(),
        clickElement = documentScope.elementFromPoint(rect.left, rect.top),
        elementInClickElement = ~Array.prototype.indexOf.call(clickElement.children, element);
    if
    (!elementInClickElement && (clickElement !== element)) {
        return done(new Error('no clickable element found'));
    }

    element.click();

    done(null, element);
}

function getElementClickWeight(element) {
    var index = clickableWeighting.indexOf(element.tagName.toLowerCase());
    return clickableWeighting.length - (index < 0 ? Infinity : index);
}

function executeClick(value, type, done) {
    _findUi(value, 'all', true, function(error, elements) {
        if(error) {
            return done(error);
        }

        var element = elements.sort(function(a, b) {
            return getElementClickWeight(a) < getElementClickWeight(b);
        }).shift();

        if(!element) {
            return done(new Error('could not find clickable element with value ' + value));
        }

        _click(element, done);
    });
}

function _focus(element, done) {
    element.focus();

    done(null, element);
}

function _blur(done) {
    var element = documentScope.activeElement;
    element.blur();

    done(null, element);
}

function execute(task, value, type, done) {
    _findUi(value, type, function(error, element) {
        if(error) {
            return done(error);
        }

        task(element, done);
    });
}

function runTasks(tasks, callback) {
    if(tasks.length) {
        tasks.shift()(function(error, result) {
            if(error) {
                return callback(error);
            } else {
                if(tasks.length === 0) {
                    callback(null, result);
                } else {
                    runTasks(tasks, callback);
                }
            }
        });
    }
}

function driveUi(settings){
    var runDelay = settings.runDelay || 1500,
        tasks = [],
        driverFunctions = {};

    documentScope = settings.document || documentScope;
    windowScope = settings.window || windowScope;

    function addTask(task){
        tasks.push(task);

        return driverFunctions;
    }

    driverFunctions = {
        navigate: function(location){
            return addTask(_navigate.bind(driverFunctions, location));
        },
        findUi: function(value, type){
            return addTask(_findUi.bind(driverFunctions, value, type));
        },
        getLocation: function() {
            return addTask(_getLocation.bind(driverFunctions));
        },
        focus: function(value, type) {
            return addTask(execute.bind(driverFunctions, _focus, value, type));
        },
        blur: function() {
            return addTask(_blur.bind(driverFunctions));
        },
        click: function(value, type){
            return addTask(executeClick.bind(driverFunctions, value, type));
        },
        pressKey: function(value) {
            return addTask(_pressKey.bind(driverFunctions, value));
        },
        pressKeys: function(value) {
            return addTask(_pressKeys.bind(driverFunctions, value));
        },
        setValue: function(value) {
            return addTask(_setValue.bind(driverFunctions, value));
        },
        wait: function(time) {
            if(!arguments.length) {
                time = runDelay;
            }

            return addTask(_wait.bind(driverFunctions, time));
        },
        do: function(driver){
            return addTask(driver.go);
        },
        go: function(callback) {
            if(tasks.length) {
                tasks.unshift(_wait.bind(driverFunctions, runDelay));
                runTasks(tasks, callback);
            } else {
                callback(new Error('No tasks defined'));
            }
        }
    };

    return driverFunctions;
}

module.exports = driveUi;
<commit_msg>ui init<commit_after>var predator = require('predator');

// List of tagNames ordered by their likeliness to be the target of a click event
var clickableWeighting = ['button', 'a', 'label', 'h1', 'h2', 'h3', 'h4', 'i', 'span'];

var types = {
        'button': ['button', 'a'],
        'label': ['label', 'span', 'div'],
        'heading': ['h1', 'h2', 'h3', 'h4'],
        'image': ['img', 'svg'],
        'field': ['input'],
        'all': ['*']
    },
    noelementOfType = 'no elements of type ',
    documentScope,
    windowScope,
    runDelay,
    initialised;

function _pressKey(key, done) {
    var element = documentScope.activeElement;

    element.value += key;

    var keydownEvent = new windowScope.KeyboardEvent('keydown'),
        keyupEvent = new windowScope.KeyboardEvent('keyup'),
        pressKeyEvent = new windowScope.KeyboardEvent('pressKey');

    var method = 'initKeyboardEvent' in keydownEvent ? 'initKeyboardEvent' : 'initKeyEvent';

    keydownEvent[method]('keydown', true, true, windowScope, key, 3, true, false, true, false, false);
    keyupEvent[method]('keyup', true, true, windowScope, key, 3, true, false, true, false, false);
    pressKeyEvent[method]('pressKey', true, true, windowScope, key, 3, true, false, true, false, false);

    element.dispatchEvent(keydownEvent);
    element.dispatchEvent(keyupEvent);
    element.dispatchEvent(pressKeyEvent);

    done(null, element);
}

function _pressKeys(keys, done) {
    keys.split('').forEach(function(key) {
        _pressKey(key, function noop() {});
    });

    done(null, documentScope.activeElement);
}

function findUi(selectors) {
    return documentScope.querySelectorAll(selectors);
}

function _navigate(location, previousElement, done) {
    var callbackTimer;

    function handlewindowScopeError(error) {
        clearTimeout(callbackTimer);

        done(error);
        windowScope.removeEventListener('error', handlewindowScopeError);
    }

    windowScope.addEventListener('error', handlewindowScopeError);
    windowScope.location = location;

    callbackTimer = setTimeout(done, 150);
}

function _getLocation(done) {
    setTimeout(function() {
        done(null, windowScope.location);
    }, 500);
}

function matchElementValue(element, value) {
    return (
            element.textContent.toLowerCase() === value.toLowerCase() ||
            (element.title && element.title.toLowerCase() === value.toLowerCase())
        ) && !predator(element).hidden;
}

function findMatchingElements(value, type, elementsList) {
    return Array.prototype.slice.call(elementsList)
        .filter(function(element) {
            return matchElementValue(element, value);
        });
}

function _findUi(value, type, returnArray, done) {
    if(!done) {
        done = returnArray;
        returnArray = false;
    }

    var elementTypes = types[type];

    if(!elementTypes) {
        return done(new Error(type + ' is not a valid ui type'));
    }

    var elements = findUi(elementTypes);

    if(!elements.length) {
        return done(new Error(noelementOfType + type));
    }

    var result = findMatchingElements(value, type, elements);

    if(!result) {
        return done(new Error(noelementOfType + type + ' with value of ' + value));
    }

    done(null, returnArray ? result : result.shift());
}

function _setValue(value, element, done) {
    element.value = value;

    done(null, element);
}

function _wait(time, done) {
    setTimeout(done, time || 0);
}

function _click(element, done) {
    var rect = element.getBoundingClientRect(),
        clickElement = documentScope.elementFromPoint(rect.left, rect.top),
        elementInClickElement = ~Array.prototype.indexOf.call(clickElement.children, element);
    if
    (!elementInClickElement && (clickElement !== element)) {
        return done(new Error('no clickable element found'));
    }

    element.click();

    done(null, element);
}

function getElementClickWeight(element) {
    var index = clickableWeighting.indexOf(element.tagName.toLowerCase());
    return clickableWeighting.length - (index < 0 ? Infinity : index);
}

function executeClick(value, type, done) {
    _findUi(value, 'all', true, function(error, elements) {
        if(error) {
            return done(error);
        }

        var element = elements.sort(function(a, b) {
            return getElementClickWeight(a) < getElementClickWeight(b);
        }).shift();

        if(!element) {
            return done(new Error('could not find clickable element with value ' + value));
        }

        _click(element, done);
    });
}

function _focus(element, done) {
    element.focus();

    done(null, element);
}

function _blur(done) {
    var element = documentScope.activeElement;
    element.blur();

    done(null, element);
}

function execute(task, value, type, done) {
    _findUi(value, type, function(error, element) {
        if(error) {
            return done(error);
        }

        task(element, done);
    });
}

function runTasks(tasks, callback) {
    if(tasks.length) {
        tasks.shift()(function(error, result) {
            if(error) {
                return callback(error);
            } else {
                if(tasks.length === 0) {
                    callback(null, result);
                } else {
                    runTasks(tasks, callback);
                }
            }
        });
    }
}

function driveUi(){
    var tasks = [],
        driverFunctions = {};

    function addTask(task){
        tasks.push(task);

        return driverFunctions;
    }

    driverFunctions = {
        navigate: function(location){
            return addTask(_navigate.bind(driverFunctions, location));
        },
        findUi: function(value, type){
            return addTask(_findUi.bind(driverFunctions, value, type));
        },
        getLocation: function() {
            return addTask(_getLocation.bind(driverFunctions));
        },
        focus: function(value, type) {
            return addTask(execute.bind(driverFunctions, _focus, value, type));
        },
        blur: function() {
            return addTask(_blur.bind(driverFunctions));
        },
        click: function(value, type){
            return addTask(executeClick.bind(driverFunctions, value, type));
        },
        pressKey: function(value) {
            return addTask(_pressKey.bind(driverFunctions, value));
        },
        pressKeys: function(value) {
            return addTask(_pressKeys.bind(driverFunctions, value));
        },
        setValue: function(value) {
            return addTask(_setValue.bind(driverFunctions, value));
        },
        wait: function(time) {
            if(!arguments.length) {
                time = runDelay;
            }

            return addTask(_wait.bind(driverFunctions, time));
        },
        do: function(driver){
            return addTask(driver.go);
        },
        go: function(callback) {
            if(!initialised) {
                throw(new Error('init must becalled before calling go'));
            }

            if(tasks.length) {
                tasks.unshift(_wait.bind(driverFunctions, runDelay));
                runTasks(tasks, callback);
            } else {
                callback(new Error('No tasks defined'));
            }
        }
    };

    return driverFunctions;
}

driveUi.init = function(settings) {
    documentScope = settings.document || document;
    windowScope = settings.window || window;
    runDelay = settings.runDelay || 0;

    initialised = true;
};

module.exports = driveUi;
<|endoftext|>


<commit_before>var semver = require('semver')
var chalk = require('chalk')
var path = require('path')
var shell = require('shelljs')
var debug = require('depurar')('lanyon')
var os = require('os')
var fs = require('fs')

var runtime = require('./index').runtime
var yes = chalk.green('✓ ')
var no = chalk.red('✗ ')

debug({runtime: runtime})

function fatalExe (cmd) {
  var opts = { 'silent': true }

  process.stdout.write('--> Executing: ' + cmd + ' ... ')

  var p = shell.exec(cmd, opts)
  if (p.code !== 0) {
    console.log(no)
    console.error('Failed to execute: ' + cmd)
    console.error(p.stdout)
    console.error(p.stderr)
    shell.exit(1)
  }

  console.log(yes)

  return p.stdout.trim()
}

function satisfied (app, cmd, checkOn) {
  var tag = ''
  if (checkOn === undefined) {
    checkOn = app
  } else {
    tag = checkOn + '/'
  }

  process.stdout.write('==> Checking: ' + tag + app + ' \'' + runtime.prerequisites[app].range + '\' ... ')

  if (optSkip.indexOf(checkOn) !== -1) {
    console.log(no + ' (disabled via LANYON_SKIP)')
    return false
  }

  if (!cmd) {
    cmd = app + ' -v'
  }

  var appVersionFull = shell.exec(cmd, { 'silent': false }).stdout.trim()
  var parts = appVersionFull.split(/[,p\s-]+/)
  var appVersion = parts[1]

  if (app === 'node') {
    appVersion = parts[0]
  } else if (app === 'bundler') {
    appVersion = parts[2]
  } else if (app === 'docker') {
    appVersion = parts[2]
  }

  try {
    if (semver.satisfies(appVersion, runtime.prerequisites[app].range)) {
      console.log(yes + appVersion + ' (' + appVersionFull + ')')
      return true
    }
  } catch (e) {
    console.log(no + appVersion + ' (' + appVersionFull + ')' + e)
    return false
  }

  console.log(no + appVersion + ' (' + appVersionFull + ')')
  return false
}

var optOnly = (process.env.LANYON_ONLY || '')
var optSkip = (process.env.LANYON_SKIP || '').split(/\s+/)
var allApps = [ 'system', 'docker', 'rbenv', 'rvm', 'ruby-shim' ]
if (optOnly === 'auto-all') {
  optOnly = ''
}

if (optOnly) {
  optSkip = []
  allApps.forEach(function (app) {
    if (app !== optOnly) {
      optSkip.push(app)
    }
  })
}
// debug({optSkip: optSkip, optOnly: optOnly})
// process.exit(0)

var rubyExe = 'ruby'
var rubyVerify = 'ruby -v'
var rubyExeSuffix = ''
var gemExe = 'gem'
var bundlerExe = 'bundler'
var jekyllExe = 'jekyll'

shell.mkdir('-p', runtime.binDir)

if (!satisfied('node')) {
  shell.exit(1)
}

process.stdout.write('==> Writing: Gemfile ... ')
var buf = 'source \'https://rubygems.org\'\n'
for (var name in runtime.gems) {
  var version = runtime.gems[name]
  buf += 'gem \'' + name + '\', \'' + version + '\'\n'
}
fs.writeFileSync(path.join(runtime.lanyonDir, 'Gemfile'), buf, 'utf-8')
console.log(yes)

if (satisfied('docker')) {
  // ' --interactive',
  // ' --tty',
  var ver = require(runtime.lanyonPackageFile).version

  if (process.env.DOCKER_BUILD === '1') {
    shell.exec('docker build -t kevinvz/lanyon:' + ver + ' .')
    shell.exec('docker push kevinvz/lanyon:' + ver + '')
  }

  rubyExe = [
    'docker run',
    ' --rm',
    ' --workdir ' + runtime.lanyonDir,
    ' --user $(id -u)',
    ' --volume ' + runtime.lanyonDir + ':' + runtime.lanyonDir,
    ' --volume ' + runtime.projectDir + ':' + runtime.projectDir,
    ' kevinvz/lanyon:' + ver + '',
    ' ruby'
  ].join('')

 //  groupadd -f -g $GROUP_ID $DOCKER_GROUP '&&' \
 // useradd -u $USER_ID -g $DOCKER_GROUP $DOCKER_USER '&&' \
 // chown $DOCKER_USER:$DOCKER_GROUP $WORK_DIR '&&' \
 // sudo -u $DOCKER_USER HOME=$HOME_DIR $COMMAND

  jekyllExe = [
    'docker run',
    ' --rm',
    ' --workdir ' + runtime.lanyonDir,
    ' --user $(id -u)',
    ' --volume ' + runtime.lanyonDir + ':' + runtime.lanyonDir,
    ' --volume ' + runtime.projectDir + ':' + runtime.projectDir,
    ' kevinvz/lanyon:' + ver + '',
    ' bundler exec jekyll'
  ].join('')
} else {
  if (satisfied('ruby', 'vendor/bin/ruby -v', 'ruby-shim')) {
    rubyExe = 'vendor/bin/ruby'
    rubyVerify = rubyExe + ' -v' + rubyExeSuffix
  } else if (satisfied('ruby', undefined, 'system')) {
    gemExe = '$(which gem)'
    bundlerExe = '$(which bundler)'
  } else {
    var rubyCfg = runtime.prerequisites.ruby
    // rbenv does not offer installing of rubies by default, it will also require the install plugin:
    if (satisfied('rbenv') && shell.exec('rbenv install --help', { 'silent': true }).code === 0) {
      fatalExe('bash -c "rbenv install --skip-existing \'' + rubyCfg.preferred + '\'"')
      rubyExe = 'bash -c "eval $(rbenv init -) && rbenv shell \'' + rubyCfg.preferred + '\' &&'
      rubyExeSuffix = '"'
      rubyVerify = rubyExe + 'ruby -v' + rubyExeSuffix
    } else if (satisfied('rvm')) {
      fatalExe('bash -c "rvm install \'' + rubyCfg.preferred + '\'"')
      rubyExe = 'bash -c "rvm \'' + rubyCfg.preferred + '\' exec'
      rubyExeSuffix = '"'
      rubyVerify = rubyExe + ' ruby -v' + rubyExeSuffix
    } else {
      console.error('Ruby version not satisfied, and exhausted ruby version installer helpers (rvm, rbenv, brew)')
      process.exit(1)
    }
  }

  if (!satisfied('ruby', rubyVerify, 'verify')) {
    console.error('Ruby should have been installed but still not satisfied')
    process.exit(1)
  }

  bundlerExe = rubyExe + ' ' + bundlerExe
  if (!satisfied('bundler', bundlerExe + ' -v' + rubyExeSuffix)) {
    fatalExe(rubyExe + ' ' + gemExe + ' install bundler -n vendor/bin/ -v \'' + runtime.prerequisites.bundler.preferred + '\'' + rubyExeSuffix)
    bundlerExe = 'vendor/bin/bundler'
  }

  process.stdout.write('==> Configuring: Bundler ... ')
  if (os.platform() === 'darwin' && shell.exec('brew -v', { 'silent': true }).code === 0) {
    fatalExe(bundlerExe + ' config build.nokogiri --use-system-libraries --with-xml2-include=$(brew --prefix libxml2)/include/libxml2' + rubyExeSuffix)
  } else {
    fatalExe(bundlerExe + ' config build.nokogiri --use-system-libraries' + rubyExeSuffix)
  }

  jekyllExe = bundlerExe + ' exec jekyll'

  process.stdout.write('==> Installing: Gems ... ')
  fatalExe(bundlerExe + ' install --path \'vendor/bundler\'' + rubyExeSuffix + ' || ' + bundlerExe + ' update' + rubyExeSuffix)
}

if (rubyExe.indexOf('vendor/bin/ruby') === -1) {
  process.stdout.write('==> Installing: ruby shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'ruby'), rubyExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}

if (bundlerExe.indexOf('vendor/bin/bundler') === -1) {
  process.stdout.write('==> Installing: bundler shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'bundler'), bundlerExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}

if (jekyllExe.indexOf('vendor/bin/jekyll') === -1) {
  process.stdout.write('==> Installing: jekyll shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'jekyll'), jekyllExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}
<commit_msg>Debug rbenv<commit_after>var semver = require('semver')
var chalk = require('chalk')
var path = require('path')
var shell = require('shelljs')
var debug = require('depurar')('lanyon')
var os = require('os')
var fs = require('fs')

var runtime = require('./index').runtime
var yes = chalk.green('✓ ')
var no = chalk.red('✗ ')

debug({runtime: runtime})

function fatalExe (cmd) {
  var opts = { 'silent': true }

  process.stdout.write('--> Executing: ' + cmd + ' ... ')

  var p = shell.exec(cmd, opts)
  if (p.code !== 0) {
    console.log(no)
    console.error('Failed to execute: ' + cmd)
    console.error(p.stdout)
    console.error(p.stderr)
    shell.exit(1)
  }

  console.log(yes)

  return p.stdout.trim()
}

function satisfied (app, cmd, checkOn) {
  var tag = ''
  if (checkOn === undefined) {
    checkOn = app
  } else {
    tag = checkOn + '/'
  }

  process.stdout.write('==> Checking: ' + tag + app + ' \'' + runtime.prerequisites[app].range + '\' ... ')

  if (optSkip.indexOf(checkOn) !== -1) {
    console.log(no + ' (disabled via LANYON_SKIP)')
    return false
  }

  if (!cmd) {
    cmd = app + ' -v'
  }

  var appVersionFull = shell.exec(cmd, { 'silent': false }).stdout.trim()
  var parts = appVersionFull.split(/[,p\s-]+/)
  var appVersion = parts[1]

  if (app === 'node') {
    appVersion = parts[0]
  } else if (app === 'bundler') {
    appVersion = parts[2]
  } else if (app === 'docker') {
    appVersion = parts[2]
  }

  try {
    if (semver.satisfies(appVersion, runtime.prerequisites[app].range)) {
      console.log(yes + appVersion + ' (' + appVersionFull + ')')
      return true
    }
  } catch (e) {
    console.log(no + appVersion + ' (' + appVersionFull + ')' + e)
    return false
  }

  console.log(no + appVersion + ' (' + appVersionFull + ')')
  return false
}

var optOnly = (process.env.LANYON_ONLY || '')
var optSkip = (process.env.LANYON_SKIP || '').split(/\s+/)
var allApps = [ 'system', 'docker', 'rbenv', 'rvm', 'ruby-shim' ]
if (optOnly === 'auto-all') {
  optOnly = ''
}

if (optOnly) {
  optSkip = []
  allApps.forEach(function (app) {
    if (app !== optOnly) {
      optSkip.push(app)
    }
  })
}
// debug({optSkip: optSkip, optOnly: optOnly})
// process.exit(0)

var rubyExe = 'ruby'
var rubyVerify = 'ruby -v'
var rubyExeSuffix = ''
var gemExe = 'gem'
var bundlerExe = 'bundler'
var jekyllExe = 'jekyll'

shell.mkdir('-p', runtime.binDir)

if (!satisfied('node')) {
  shell.exit(1)
}

process.stdout.write('==> Writing: Gemfile ... ')
var buf = 'source \'https://rubygems.org\'\n'
for (var name in runtime.gems) {
  var version = runtime.gems[name]
  buf += 'gem \'' + name + '\', \'' + version + '\'\n'
}
fs.writeFileSync(path.join(runtime.lanyonDir, 'Gemfile'), buf, 'utf-8')
console.log(yes)

if (satisfied('docker')) {
  // ' --interactive',
  // ' --tty',
  var ver = require(runtime.lanyonPackageFile).version

  if (process.env.DOCKER_BUILD === '1') {
    shell.exec('docker build -t kevinvz/lanyon:' + ver + ' .')
    shell.exec('docker push kevinvz/lanyon:' + ver + '')
  }

  rubyExe = [
    'docker run',
    ' --rm',
    ' --workdir ' + runtime.lanyonDir,
    ' --user $(id -u)',
    ' --volume ' + runtime.lanyonDir + ':' + runtime.lanyonDir,
    ' --volume ' + runtime.projectDir + ':' + runtime.projectDir,
    ' kevinvz/lanyon:' + ver + '',
    ' ruby'
  ].join('')

  jekyllExe = [
    'docker run',
    ' --rm',
    ' --workdir ' + runtime.lanyonDir,
    ' --user $(id -u)',
    ' --volume ' + runtime.lanyonDir + ':' + runtime.lanyonDir,
    ' --volume ' + runtime.projectDir + ':' + runtime.projectDir,
    ' kevinvz/lanyon:' + ver + '',
    ' bundler exec jekyll'
  ].join('')
} else {
  if (satisfied('ruby', 'vendor/bin/ruby -v', 'ruby-shim')) {
    rubyExe = 'vendor/bin/ruby'
    rubyVerify = rubyExe + ' -v' + rubyExeSuffix
  } else if (satisfied('ruby', undefined, 'system')) {
    gemExe = '$(which gem)'
    bundlerExe = '$(which bundler)'
  } else {
    var rubyCfg = runtime.prerequisites.ruby
    // rbenv does not offer installing of rubies by default, it will also require the install plugin:
    if (satisfied('rbenv') && shell.exec('rbenv install --help', { 'silent': false }).code === 0) {
      fatalExe('bash -c "rbenv install --skip-existing \'' + rubyCfg.preferred + '\'"')
      rubyExe = 'bash -c "eval $(rbenv init -) && rbenv shell \'' + rubyCfg.preferred + '\' &&'
      rubyExeSuffix = '"'
      rubyVerify = rubyExe + 'ruby -v' + rubyExeSuffix
    } else if (satisfied('rvm')) {
      fatalExe('bash -c "rvm install \'' + rubyCfg.preferred + '\'"')
      rubyExe = 'bash -c "rvm \'' + rubyCfg.preferred + '\' exec'
      rubyExeSuffix = '"'
      rubyVerify = rubyExe + ' ruby -v' + rubyExeSuffix
    } else {
      console.error('Ruby version not satisfied, and exhausted ruby version installer helpers (rvm, rbenv, brew)')
      process.exit(1)
    }
  }

  if (!satisfied('ruby', rubyVerify, 'verify')) {
    console.error('Ruby should have been installed but still not satisfied')
    process.exit(1)
  }

  bundlerExe = rubyExe + ' ' + bundlerExe
  if (!satisfied('bundler', bundlerExe + ' -v' + rubyExeSuffix)) {
    fatalExe(rubyExe + ' ' + gemExe + ' install bundler -n vendor/bin/ -v \'' + runtime.prerequisites.bundler.preferred + '\'' + rubyExeSuffix)
    bundlerExe = 'vendor/bin/bundler'
  }

  process.stdout.write('==> Configuring: Bundler ... ')
  if (os.platform() === 'darwin' && shell.exec('brew -v', { 'silent': true }).code === 0) {
    fatalExe(bundlerExe + ' config build.nokogiri --use-system-libraries --with-xml2-include=$(brew --prefix libxml2)/include/libxml2' + rubyExeSuffix)
  } else {
    fatalExe(bundlerExe + ' config build.nokogiri --use-system-libraries' + rubyExeSuffix)
  }

  jekyllExe = bundlerExe + ' exec jekyll'

  process.stdout.write('==> Installing: Gems ... ')
  fatalExe(bundlerExe + ' install --path \'vendor/bundler\'' + rubyExeSuffix + ' || ' + bundlerExe + ' update' + rubyExeSuffix)
}

if (rubyExe.indexOf('vendor/bin/ruby') === -1) {
  process.stdout.write('==> Installing: ruby shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'ruby'), rubyExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}

if (bundlerExe.indexOf('vendor/bin/bundler') === -1) {
  process.stdout.write('==> Installing: bundler shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'bundler'), bundlerExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}

if (jekyllExe.indexOf('vendor/bin/jekyll') === -1) {
  process.stdout.write('==> Installing: jekyll shim ... ')
  fs.writeFileSync(path.join(runtime.binDir, 'jekyll'), jekyllExe.trim() + ' $*' + rubyExeSuffix + '\n', { 'encoding': 'utf-8', 'mode': '755' })
  console.log(yes)
}
<|endoftext|>