<commit_before>/*
@license https://github.com/t2ym/gulp-i18n-leverage/blob/master/LICENSE.md
Copyright (c) 2016, Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
*/
'use strict';

var path = require('path');
var fs = require('fs');
var JSONstringify = require('json-stringify-safe');
var stripBom = require('strip-bom');
var rfc6902 = require('rfc6902');
var deepcopy = require('deepcopy');
var gutil = require('gulp-util');
var through = require('through2');

/**
 * Gulp plugin to merge text changes in the default locale into a localized bundle.
 *
 * @namespace gulp-i18n-leverage
 */
module.exports = function(options) {
  return through.obj(function (file, enc, callback) {
    var jsonSpace = (options && options.jsonSpace !== undefined) ? options.jsonSpace : 2;
    var srcPath = (options && options.srcPath !== undefined) ? options.srcPath : 'app';
    var distPath = (options && options.distPath !== undefined) ? options.distPath : 'dist';
    var finalize = (options && options.finalize !== undefined) ? options.finalize : false;
    var bundles = options && options.bundles ? options.bundles : {};

    function deepMap(target, source, map) {
      var value;
      for (var prop in source) {
        value = source[prop];
        switch (typeof value) {
        case 'string':
        case 'number':
        case 'boolean':
          target[prop] = map(value, prop);
          break;
        case 'object':
          if (Array.isArray(value)) {
            // TODO: cannot handle deep objects properly
            target[prop] = target[prop] || [];
            deepMap(target[prop], value, map);
          }
          else {
            target[prop] = target[prop] || {};
            deepMap(target[prop], value, map);
          }
          break;
        default:
          target[prop] = value;
          break;
        }
      }
    }

    function getNextFallbackLanguage(lang) {
      var nextFallbackLanguage = null;
      if (lang && lang.length > 0) {
        var parts = lang.split(/[-_]/);
        if (parts.length >= 2) {
          parts.pop();
          nextFallbackLanguage = parts.join('-');
        }
      }
      return nextFallbackLanguage;
    }

    function parsePath() {
      var r = {};
      r.path = path.resolve(file.path);
      r.base = path.resolve(file.base);
      r.base = r.base[r.base.length - 1] === path.sep ? r.base : r.base + path.sep;
      r.srcBase = r.base.substr(0, r.base.length - 1);
      r.distBase = r.srcBase.substr(0, r.srcBase.length - srcPath.length) + distPath;
      r.urlPath = r.path.substr(r.srcBase.length);
      var splitPath = r.urlPath.split(path.sep + 'locales' + path.sep);
      r.componentBase = splitPath[0];
      r.resourcePath = r.componentBase + path.sep + 'locales';
      r.fileName = splitPath[1];
      r.extName = path.extname(r.fileName);
      var baseNameWithLang = r.fileName.substr(0, r.fileName.length - r.extName.length);
      var splitBaseName = baseNameWithLang.split('.');
      r.lang = splitBaseName.pop();
      r.baseName = r.fileName.substr(0, r.fileName.length - r.extName.length - r.lang.length - 1);
      r.defaultPath = r.componentBase + path.sep + r.baseName + r.extName;
      r.ancestorSrcPaths = [];
      var lang = getNextFallbackLanguage(r.lang);
      while (lang) {
        r.ancestorSrcPaths.push(r.srcBase + r.resourcePath + path.sep + r.baseName + '.' + lang + r.extName);
        lang = getNextFallbackLanguage(lang);
      }
      r.srcDefaultPath = r.srcBase + r.defaultPath;
      r.distDefaultPath = r.distBase + r.defaultPath;
      return r;
    }

    /*
    function findPatch(patch, op, path) {
      for (var i in patch) {
        var item = patch[i];
        if (item.op === op && item.path === path) {
          return item;
        }
      }
      return null;
    }
    */

    function findValue(obj, path) {
      var splitPath = path.split('/');
      var value = obj;
      for (var i = 1; i < splitPath.length; i++) {
        value = value[splitPath[i]];
      }
      return value;
    }

    function fragmentObject(obj, path) {
      var fragments = [];
      var op;
      var i;
      switch (typeof obj) {
      case 'object':
        if (Array.isArray(obj)) {
          for (i = 0; i < obj.length; i++) {
            fragments = fragments.concat(fragmentObject(obj[i], path + '/' + i));
          }
        }
        else {
          for (i in obj) {
            fragments = fragments.concat(fragmentObject(obj[i], path + '/' + i));
          }
        }
        break;
      case 'string':
      case 'number':
      case 'boolean':
      case 'function':
      case 'symbol':
      case 'undefined':
      default:
        op = {};
        op[path] = obj;
        fragments = [ op ];
        break;
      }
      return fragments;
    }

    function fragmentOperation(rawOp) {
      var operations = [];
      switch (rawOp.op) {
      case 'add':
      case 'remove':
      case 'replace':
        if (typeof rawOp.value === 'object') {
          if (rawOp.op === 'remove') {
            operations.push(rawOp);
          }
          fragmentObject(rawOp.value, rawOp.path).forEach(function (op) {
            for (var p in op) {
              operations.push({ op: rawOp.op, path: p, value: op[p] });
            }
          });
        }
        else {
          operations.push(rawOp);
        }
        break;
      case 'move':
      case 'copy':
        if (typeof rawOp.value === 'object') {
          fragmentObject(rawOp.value, '').forEach(function (op) {
            for (var p in op) {
              operations.push({ op: rawOp.op, from: rawOp.from + p, path: rawOp.path + p, value: rawOp.value[p.substr(1)] });
            }
          });
        }
        else {
          operations.push(rawOp);
        }
        break;
      case 'test':
      default:
        operations.push(rawOp);
        break;
      }
      return operations;
    }

    function fragmentPatch(patch) {
      var fragmented = [];
      for (var i = 0; i < patch.length; i++) {
        fragmented = fragmented.concat(fragmentOperation(patch[i]));
      }
      return fragmented;
    }

    function minimizePatch(patch, prev, current) {
      var minimized = [];
      var valueOpMap = {};
      var serialized;
      var i;
      var item, item2;
      var removed;
      var arrayLengths = {};

      // adjust patch path for array item addition
      for (i in patch) {
        item = patch[i];
        if (item.op === 'add' &&
            item.path.match(/\/-$/)) {
          item.path = item.path.replace(/\/-$/, '');
          if (arrayLengths[item.path]) {
            arrayLengths[item.path]++;
          }
          else {
            arrayLengths[item.path] = findValue(prev, item.path).length;
          }
          item.path = item.path + '/' + arrayLengths[item.path];
        }
      }

      // construct valueOpMap
      for (i in patch) {
        item = patch[i];
        if (item.op === 'remove') {
          item.value = findValue(prev, item.path);
        }
        serialized = JSONstringify(item.value);
        valueOpMap[serialized] = valueOpMap[serialized] || [];
        valueOpMap[serialized].push(i);
      }

      // convert remove/add pairs to move operations
      for (i in valueOpMap) {
        if (valueOpMap[i].length === 2) {
          item = patch[valueOpMap[i][0]];
          if (item.op === 'add') {
            item2 = item;
            item = patch[valueOpMap[i][1]];
          }
          else {
            item2 = patch[valueOpMap[i][1]];
          }
          if (item.op === 'remove' && item2.op === 'add') {
            // convert them to move operation
            item.op = 'move';
            item.from = item.path;
            item.path = item2.path;
            //delete item.value;
            item2.op = 'noop';
          }
        }
      }

      // construct minimized patch
      for (i in patch) {
        item = patch[i];
        switch (item.op) {
        case 'add':
        case 'remove':
        case 'replace':
        case 'move':
        case 'copy':
          minimized.push(item);
          break;
        case 'test':
        default:
          break;
        }
      }
      return minimized;
    }

    function updateMetaTodo(current, prev)
    {
      var meta = prev.meta || {};
      var prevClone = deepcopy(prev);
      var currentClone = deepcopy(current);
      var patch;
      var i, j;
      delete prevClone.meta;
      delete currentClone.meta;
      current.meta.todo = [];
      patch = rfc6902.createPatch(prevClone, currentClone);
      patch = minimizePatch(patch, prevClone, currentClone);
      //console.log('fragmenting ======================================================================');
      //console.log(JSONstringify(patch, null, 2));
      patch = fragmentPatch(patch);
      //console.log(JSONstringify(patch, null, 2));
      meta.todo = meta.todo || [];
      for (i = 0; i < patch.length; i++) {
        switch (patch[i].op) {
        case 'add':
        case 'replace':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].path) {
              meta.todo[j].op = 'noop';
              break;
            }
          }
          meta.todo.push(patch[i]);
          break;
        case 'remove':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].path) {
              meta.todo[j].op = 'noop';
            }
            else if (meta.todo[j].path.indexOf(patch[i].path + '/') === 0) {
              meta.todo[j].op = 'noop';
            }
          }
          break;
        case 'move':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].from) {
              meta.todo[j].path = patch[i].path;
            }
            else if (meta.todo[j].path.indexOf(patch[i].from + '/') === 0) {
              meta.todo[j].path = patch[i].path + meta.todo[j].path.substr(patch[i].from.length);
            }
          }
          break;
        case 'copy': // TODO: should not happen for now
        case 'test':
        default:
          break;
        }
      }
      for (i = meta.todo.length - 1; i >= 0; i--) {
        if (meta.todo[i].op === 'noop') {
          meta.todo.splice(i, 1);
        }
      }
      current.meta = meta;
      return current;
    }

    function update(paths, contents) {
      var prevDefault;
      var currentDefault;
      var prevLocalized;
      var prevLocalizedOriginal;
      var currentLocalized;
      var prevAncestors = [];
      var patchStatus;
      var i;
      var tmpContents;
      try {
        prevDefault = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.srcDefaultPath, 'utf8')));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || tmpContents) {
          gutil.log(gutil.colors.cyan(paths.srcDefaultPath),
                    gutil.colors.yellow(e.toString()));
        }
        prevDefault = { meta: {} }; // presumably file not found
      }
      try {
        currentDefault = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.distDefaultPath, 'utf8')));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || tmpContents) {
          gutil.log(gutil.colors.cyan(paths.distDefaultPath),
                    gutil.colors.yellow(e.toString()));
        }
        currentDefault = { meta: {} }; // presumably file not found
      }
      for (i = 0; i < paths.ancestorSrcPaths.length; i++) {
        try {
          prevAncestors[i] = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.ancestorSrcPaths[i], 'utf8')));
        }
        catch (e) {
          prevAncestors[i] = null; // presumably file not found
        }
      }

      try {
        prevLocalized = JSON.parse(stripBom(contents));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || contents) {
          gutil.log(gutil.colors.cyan(paths.urlPath),
                    gutil.colors.yellow(e.toString()));
        }
        prevLocalized = { meta: {} };
      }
      prevLocalizedOriginal = deepcopy(prevLocalized);

      var patch = rfc6902.createPatch(prevDefault, currentDefault);
      patch = minimizePatch(patch, prevDefault, currentDefault);

      for (i = 0; i < prevAncestors.length; i++) {
        if (prevAncestors[i]) {
          rfc6902.applyPatch(prevAncestors[i], patch);
        }
      }

      patchStatus = rfc6902.applyPatch(prevLocalized, patch);
      for (i = 0; i < patchStatus.length; i++) {
        if (patchStatus[i]) {
          console.log('warning: ' + patchStatus[i].name +
                                  ' for ' + JSONstringify(patch[i]));
        }
      }

      currentLocalized = deepcopy(currentDefault);

      for (i = 0; i < prevAncestors.length; i++) {
        if (prevAncestors[i]) {
          deepMap(currentLocalized, prevAncestors[i], function (value) { return value; });
        }
      }

      deepMap(currentLocalized, prevLocalized, function (value) { return value; });

      updateMetaTodo(currentLocalized, prevLocalizedOriginal);
      if (finalize) {
        if (currentLocalized.meta &&
            currentLocalized.meta.todo &&
            currentLocalized.meta.todo.length > 0) {
          gutil.log(gutil.colors.cyan(paths.urlPath),
                    gutil.colors.yellow('warning: discarding meta.todo ='),
                    gutil.colors.gray('\n' + JSONstringify(currentLocalized.meta.todo, null, 2)));
        }
        currentLocalized.meta = {};
      }

      //console.log(JSONstringify(patch, null, 2));
      bundles[paths.lang] = bundles[paths.lang] || {};
      bundles[paths.lang][paths.baseName] = deepcopy(currentLocalized);
      bundles[''] = bundles[''] || {};
      bundles[''][paths.baseName] = deepcopy(currentDefault);
      return JSONstringify(currentLocalized, null, jsonSpace);
    }

    function doLeverage() {
      if (file.isNull()) {
        callback(null, file);
        return;
      }

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

      if (file.isBuffer()) {
        var contents = String(file.contents);
        var paths = parsePath();
        //console.log(paths);
        var result = update(paths, contents);
        if (result) {
          file.contents = new Buffer(result);
          return callback(null, file);
        }
      }

      /* istanbul ignore next: non-null, non-stream, non-buffer file should not come */
      callback(null, file);
    }

    doLeverage();
  });
};
<commit_msg>More Fix #14. Support removal on array index adjustment<commit_after>/*
@license https://github.com/t2ym/gulp-i18n-leverage/blob/master/LICENSE.md
Copyright (c) 2016, Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
*/
'use strict';

var path = require('path');
var fs = require('fs');
var JSONstringify = require('json-stringify-safe');
var stripBom = require('strip-bom');
var rfc6902 = require('rfc6902');
var deepcopy = require('deepcopy');
var gutil = require('gulp-util');
var through = require('through2');

/**
 * Gulp plugin to merge text changes in the default locale into a localized bundle.
 *
 * @namespace gulp-i18n-leverage
 */
module.exports = function(options) {
  return through.obj(function (file, enc, callback) {
    var jsonSpace = (options && options.jsonSpace !== undefined) ? options.jsonSpace : 2;
    var srcPath = (options && options.srcPath !== undefined) ? options.srcPath : 'app';
    var distPath = (options && options.distPath !== undefined) ? options.distPath : 'dist';
    var finalize = (options && options.finalize !== undefined) ? options.finalize : false;
    var bundles = options && options.bundles ? options.bundles : {};

    function deepMap(target, source, map) {
      var value;
      for (var prop in source) {
        value = source[prop];
        switch (typeof value) {
        case 'string':
        case 'number':
        case 'boolean':
          target[prop] = map(value, prop);
          break;
        case 'object':
          if (Array.isArray(value)) {
            // TODO: cannot handle deep objects properly
            target[prop] = target[prop] || [];
            deepMap(target[prop], value, map);
          }
          else {
            target[prop] = target[prop] || {};
            deepMap(target[prop], value, map);
          }
          break;
        default:
          target[prop] = value;
          break;
        }
      }
    }

    function getNextFallbackLanguage(lang) {
      var nextFallbackLanguage = null;
      if (lang && lang.length > 0) {
        var parts = lang.split(/[-_]/);
        if (parts.length >= 2) {
          parts.pop();
          nextFallbackLanguage = parts.join('-');
        }
      }
      return nextFallbackLanguage;
    }

    function parsePath() {
      var r = {};
      r.path = path.resolve(file.path);
      r.base = path.resolve(file.base);
      r.base = r.base[r.base.length - 1] === path.sep ? r.base : r.base + path.sep;
      r.srcBase = r.base.substr(0, r.base.length - 1);
      r.distBase = r.srcBase.substr(0, r.srcBase.length - srcPath.length) + distPath;
      r.urlPath = r.path.substr(r.srcBase.length);
      var splitPath = r.urlPath.split(path.sep + 'locales' + path.sep);
      r.componentBase = splitPath[0];
      r.resourcePath = r.componentBase + path.sep + 'locales';
      r.fileName = splitPath[1];
      r.extName = path.extname(r.fileName);
      var baseNameWithLang = r.fileName.substr(0, r.fileName.length - r.extName.length);
      var splitBaseName = baseNameWithLang.split('.');
      r.lang = splitBaseName.pop();
      r.baseName = r.fileName.substr(0, r.fileName.length - r.extName.length - r.lang.length - 1);
      r.defaultPath = r.componentBase + path.sep + r.baseName + r.extName;
      r.ancestorSrcPaths = [];
      var lang = getNextFallbackLanguage(r.lang);
      while (lang) {
        r.ancestorSrcPaths.push(r.srcBase + r.resourcePath + path.sep + r.baseName + '.' + lang + r.extName);
        lang = getNextFallbackLanguage(lang);
      }
      r.srcDefaultPath = r.srcBase + r.defaultPath;
      r.distDefaultPath = r.distBase + r.defaultPath;
      return r;
    }

    /*
    function findPatch(patch, op, path) {
      for (var i in patch) {
        var item = patch[i];
        if (item.op === op && item.path === path) {
          return item;
        }
      }
      return null;
    }
    */

    function findValue(obj, path) {
      var splitPath = path.split('/');
      var value = obj;
      for (var i = 1; i < splitPath.length; i++) {
        value = value[splitPath[i]];
      }
      return value;
    }

    function fragmentObject(obj, path) {
      var fragments = [];
      var op;
      var i;
      switch (typeof obj) {
      case 'object':
        if (Array.isArray(obj)) {
          for (i = 0; i < obj.length; i++) {
            fragments = fragments.concat(fragmentObject(obj[i], path + '/' + i));
          }
        }
        else {
          for (i in obj) {
            fragments = fragments.concat(fragmentObject(obj[i], path + '/' + i));
          }
        }
        break;
      case 'string':
      case 'number':
      case 'boolean':
      case 'function':
      case 'symbol':
      case 'undefined':
      default:
        op = {};
        op[path] = obj;
        fragments = [ op ];
        break;
      }
      return fragments;
    }

    function fragmentOperation(rawOp) {
      var operations = [];
      switch (rawOp.op) {
      case 'add':
      case 'remove':
      case 'replace':
        if (typeof rawOp.value === 'object') {
          if (rawOp.op === 'remove') {
            operations.push(rawOp);
          }
          fragmentObject(rawOp.value, rawOp.path).forEach(function (op) {
            for (var p in op) {
              operations.push({ op: rawOp.op, path: p, value: op[p] });
            }
          });
        }
        else {
          operations.push(rawOp);
        }
        break;
      case 'move':
      case 'copy':
        if (typeof rawOp.value === 'object') {
          fragmentObject(rawOp.value, '').forEach(function (op) {
            for (var p in op) {
              operations.push({ op: rawOp.op, from: rawOp.from + p, path: rawOp.path + p, value: rawOp.value[p.substr(1)] });
            }
          });
        }
        else {
          operations.push(rawOp);
        }
        break;
      case 'test':
      default:
        operations.push(rawOp);
        break;
      }
      return operations;
    }

    function fragmentPatch(patch) {
      var fragmented = [];
      for (var i = 0; i < patch.length; i++) {
        fragmented = fragmented.concat(fragmentOperation(patch[i]));
      }
      return fragmented;
    }

    function minimizePatch(patch, prev, current) {
      var minimized = [];
      var valueOpMap = {};
      var serialized;
      var i;
      var item, item2;
      var removed;
      var arrayLengths = {};
      var arrayPath;

      // adjust patch path for array item addition
      for (i in patch) {
        item = patch[i];
        if (item.op === 'add' &&
            item.path.match(/\/[0-9]{1,}$/)) {
          arrayPath = item.path.replace(/\/[0-9]{1,}$/, '');
          arrayLengths[arrayPath] = arrayLengths[arrayPath]
                                      ? arrayLengths[arrayPath] + 1
                                      : findValue(prev, arrayPath).length;
        }
        else if (item.op === 'remove' &&
                 item.path.match(/\/[0-9]{1,}$/)) {
          arrayPath = item.path.replace(/\/[0-9]{1,}$/, '');
          arrayLengths[arrayPath] = arrayLengths[arrayPath]
                                      ? arrayLengths[arrayPath] - 1
                                      : findValue(prev, arrayPath).length - 1;
        }
        else if (item.op === 'add' &&
                 item.path.match(/\/-$/)) {
          arrayPath = item.path.replace(/\/-$/, '');
          arrayLengths[arrayPath] = arrayLengths[arrayPath]
                                      ? arrayLengths[arrayPath] + 1
                                      : findValue(prev, arrayPath).length;
          item.path = arrayPath + '/' + arrayLengths[arrayPath];
        }
      }

      // construct valueOpMap
      for (i in patch) {
        item = patch[i];
        if (item.op === 'remove') {
          item.value = findValue(prev, item.path);
        }
        serialized = JSONstringify(item.value);
        valueOpMap[serialized] = valueOpMap[serialized] || [];
        valueOpMap[serialized].push(i);
      }

      // convert remove/add pairs to move operations
      for (i in valueOpMap) {
        if (valueOpMap[i].length === 2) {
          item = patch[valueOpMap[i][0]];
          if (item.op === 'add') {
            item2 = item;
            item = patch[valueOpMap[i][1]];
          }
          else {
            item2 = patch[valueOpMap[i][1]];
          }
          if (item.op === 'remove' && item2.op === 'add') {
            // convert them to move operation
            item.op = 'move';
            item.from = item.path;
            item.path = item2.path;
            //delete item.value;
            item2.op = 'noop';
          }
        }
      }

      // construct minimized patch
      for (i in patch) {
        item = patch[i];
        switch (item.op) {
        case 'add':
        case 'remove':
        case 'replace':
        case 'move':
        case 'copy':
          minimized.push(item);
          break;
        case 'test':
        default:
          break;
        }
      }
      return minimized;
    }

    function updateMetaTodo(current, prev)
    {
      var meta = prev.meta || {};
      var prevClone = deepcopy(prev);
      var currentClone = deepcopy(current);
      var patch;
      var i, j;
      delete prevClone.meta;
      delete currentClone.meta;
      current.meta.todo = [];
      patch = rfc6902.createPatch(prevClone, currentClone);
      patch = minimizePatch(patch, prevClone, currentClone);
      //console.log('fragmenting ======================================================================');
      //console.log(JSONstringify(patch, null, 2));
      patch = fragmentPatch(patch);
      //console.log(JSONstringify(patch, null, 2));
      meta.todo = meta.todo || [];
      for (i = 0; i < patch.length; i++) {
        switch (patch[i].op) {
        case 'add':
        case 'replace':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].path) {
              meta.todo[j].op = 'noop';
              break;
            }
          }
          meta.todo.push(patch[i]);
          break;
        case 'remove':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].path) {
              meta.todo[j].op = 'noop';
            }
            else if (meta.todo[j].path.indexOf(patch[i].path + '/') === 0) {
              meta.todo[j].op = 'noop';
            }
          }
          break;
        case 'move':
          for (j = 0; j < meta.todo.length; j++) {
            if (meta.todo[j].path === patch[i].from) {
              meta.todo[j].path = patch[i].path;
            }
            else if (meta.todo[j].path.indexOf(patch[i].from + '/') === 0) {
              meta.todo[j].path = patch[i].path + meta.todo[j].path.substr(patch[i].from.length);
            }
          }
          break;
        case 'copy': // TODO: should not happen for now
        case 'test':
        default:
          break;
        }
      }
      for (i = meta.todo.length - 1; i >= 0; i--) {
        if (meta.todo[i].op === 'noop') {
          meta.todo.splice(i, 1);
        }
      }
      current.meta = meta;
      return current;
    }

    function update(paths, contents) {
      var prevDefault;
      var currentDefault;
      var prevLocalized;
      var prevLocalizedOriginal;
      var currentLocalized;
      var prevAncestors = [];
      var patchStatus;
      var i;
      var tmpContents;
      try {
        prevDefault = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.srcDefaultPath, 'utf8')));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || tmpContents) {
          gutil.log(gutil.colors.cyan(paths.srcDefaultPath),
                    gutil.colors.yellow(e.toString()));
        }
        prevDefault = { meta: {} }; // presumably file not found
      }
      try {
        currentDefault = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.distDefaultPath, 'utf8')));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || tmpContents) {
          gutil.log(gutil.colors.cyan(paths.distDefaultPath),
                    gutil.colors.yellow(e.toString()));
        }
        currentDefault = { meta: {} }; // presumably file not found
      }
      for (i = 0; i < paths.ancestorSrcPaths.length; i++) {
        try {
          prevAncestors[i] = JSON.parse(stripBom(tmpContents = fs.readFileSync(paths.ancestorSrcPaths[i], 'utf8')));
        }
        catch (e) {
          prevAncestors[i] = null; // presumably file not found
        }
      }

      try {
        prevLocalized = JSON.parse(stripBom(contents));
      }
      catch (e) {
        if (!e.toString().match(/SyntaxError/) || contents) {
          gutil.log(gutil.colors.cyan(paths.urlPath),
                    gutil.colors.yellow(e.toString()));
        }
        prevLocalized = { meta: {} };
      }
      prevLocalizedOriginal = deepcopy(prevLocalized);

      var patch = rfc6902.createPatch(prevDefault, currentDefault);
      patch = minimizePatch(patch, prevDefault, currentDefault);

      for (i = 0; i < prevAncestors.length; i++) {
        if (prevAncestors[i]) {
          rfc6902.applyPatch(prevAncestors[i], patch);
        }
      }

      patchStatus = rfc6902.applyPatch(prevLocalized, patch);
      for (i = 0; i < patchStatus.length; i++) {
        if (patchStatus[i]) {
          console.log('warning: ' + patchStatus[i].name +
                                  ' for ' + JSONstringify(patch[i]));
        }
      }

      currentLocalized = deepcopy(currentDefault);

      for (i = 0; i < prevAncestors.length; i++) {
        if (prevAncestors[i]) {
          deepMap(currentLocalized, prevAncestors[i], function (value) { return value; });
        }
      }

      deepMap(currentLocalized, prevLocalized, function (value) { return value; });

      updateMetaTodo(currentLocalized, prevLocalizedOriginal);
      if (finalize) {
        if (currentLocalized.meta &&
            currentLocalized.meta.todo &&
            currentLocalized.meta.todo.length > 0) {
          gutil.log(gutil.colors.cyan(paths.urlPath),
                    gutil.colors.yellow('warning: discarding meta.todo ='),
                    gutil.colors.gray('\n' + JSONstringify(currentLocalized.meta.todo, null, 2)));
        }
        currentLocalized.meta = {};
      }

      //console.log(JSONstringify(patch, null, 2));
      bundles[paths.lang] = bundles[paths.lang] || {};
      bundles[paths.lang][paths.baseName] = deepcopy(currentLocalized);
      bundles[''] = bundles[''] || {};
      bundles[''][paths.baseName] = deepcopy(currentDefault);
      return JSONstringify(currentLocalized, null, jsonSpace);
    }

    function doLeverage() {
      if (file.isNull()) {
        callback(null, file);
        return;
      }

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

      if (file.isBuffer()) {
        var contents = String(file.contents);
        var paths = parsePath();
        //console.log(paths);
        var result = update(paths, contents);
        if (result) {
          file.contents = new Buffer(result);
          return callback(null, file);
        }
      }

      /* istanbul ignore next: non-null, non-stream, non-buffer file should not come */
      callback(null, file);
    }

    doLeverage();
  });
};
<|endoftext|>


<commit_before>var express = require('express'),
    bodyParser = require('body-parser'),
    expressValidator = require('express-validator'),
    request = require('request'),
    mysql = require('mysql'),
    util = require('util'),
    OAuthProvider = require('./lib/OAuthProvider.js'),
    Promise = require("node-promise").Promise,
    pushURL = 'http://52.16.43.57:8080/VectorCloud/rest/v1/stream/push',
    privateMethods;

function log(type, text, force) {
    if (force) {
        console[type](util.inspect(text, {colors: true, depth: null}));
    }
}

function establishDBConnection(dbSettings) {
    if (dbSettings.connection) return dbSettings.connection;

    return mysql.createConnection(dbSettings);
}

var VectorWatchStreamNode = function VectorWatchStreamNode(options) {
    if (typeof options != "object" && Object.prototype.toString.call(options) == '[object Array]') {
        throw new Error('options have to be an object.');
    }
    this.options = options;
    this.debugMode = false;

    if (!options.token) {
        throw new Error('token is required.');
    }
    this.token = options.token;

    if (!options.streamUID) {
        throw new Error('streamUID is required.');
    }
    this.streamUID = options.streamUID;

    if (options.database) {
        var connection = establishDBConnection(options.database);

        var MysqlStorageProvider = require('./lib/StorageProviders/MysqlStorageProvider.js');
        this.authStorage = new MysqlStorageProvider({
            connection: connection,
            table: options.database.authStorageTable || 'Auth'
        });
        this.stateStorage = new MysqlStorageProvider({
            connection: connection,
            table: options.database.stateStorageTable || 'Settings'
        });
    } else {
        var MemoryStorageProvider = require('./lib/StorageProviders/MemoryStorageProvider.js');
        this.authStorage = new MemoryStorageProvider();
        this.stateStorage = new MemoryStorageProvider();
    }

    if (options.auth) {
        var auth = options.auth;
        if (auth.protocol.toLowerCase() != 'oauth') {
            throw new Error('Unsupported auth protocol.');
        }
        this.oauthClient = OAuthProvider.create(this.authStorage, auth);
    }

    privateMethods.setupRouter.call(this);
};

/** This function is called every time a user adds the stream to a watch face(and selects the desired settings, if needed).
 * The DB(if the stream has settings that need to be stored) is automatically updated.
 * When implementing this method the developer must call the 'resolve' function parameter after he retrives/generates the data.
 *      resolve({data:"..."});
 * Called for public streams
 * @param resolve {Function} DB insert success callback
 * @param reject {Function} DB insert fail callback
 * @param settings {Object} User settings
 * @param authTokens {Object}
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.registerSettings = function (resolve, reject, settings, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/** This function is called every time a user removes the stream from a watch face.
 Called for public streams
 * @param settings {Object} User settings
 * @param authTokens {Object}
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.unregisterSettings = function (settings, authTokens) { };

/**
 * @param resolve {Function}
 * @param reject {Function}
 * @param authTokens {Object}
 */
VectorWatchStreamNode.prototype.requestConfig = function(resolve, reject, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/**
 * @param resolve {Function}
 * @param reject {Function}
 * @param settingName {String}
 * @param searchTerm {String}
 * @param state {Object}
 * @param authTokens {Object}
 */
VectorWatchStreamNode.prototype.requestOptions = function(resolve, reject, settingName, searchTerm, state, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/** Sends update request to Vector Cloud, with all the information needed.
 * @param dataArray {Array} The array elements contain the info that will be displayed on the watch and the coresponding settings - [data, settings]
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.sendDeliverRequests = function (dataArray) {
    if (Object.prototype.toString.call(dataArray) != '[object Array]') {
        log('log', "Method expects an array. Example:[{channelLabel: 'value for stream'}]");
    }

    var packets = [], _this = this;
    dataArray.forEach(function(data) {
        for (var channelLabel in data) {
            packets.push({
                type: 3,
                streamUUID: _this.streamUID,
                channelLabel: channelLabel,
                d: data[channelLabel]
            });
        }
    });

    var requestBody = {
        v: 1,
        p: packets
    };
    log('log', "The data is sent to VectorCloud.", true);
    log('log', requestBody, this.debugMode);
    var options = {
        uri: pushURL,
        method: 'POST',
        json: requestBody,
        headers: {"Authorization": this.token}
    };
    request(options, function (err, response, body) {
        if (!err && response.statusCode == 200) {
            log('log', body, _this.debugMode);
        } else {
            log('log', err, _this.debugMode);
        }
    });
};

/** Get all the settings stored in the DB. On success the resolve(settingsArray) method is called, otherwise the reject(error) method.
 * The developer can access the returned array in the resolve(settingsArray) callback, as a parameter.
 Example: sample_stream.retrieveSettings(function (settingsArray) {
                console.log(settingsArray); -> [{"City":"Bucharest", ...},{"City":"New York", ...}, ...]
            });
 * @param resolve {Function} DB select success callback
 * @param reject {Function} DB select fail callback
 * @returns null
 *
 **/
VectorWatchStreamNode.prototype.retrieveSettings = VectorWatchStreamNode.prototype.retrieveState = function (resolve, reject) {
    var _this = this;
    this.stateStorage.retrieveAll(function(err, states) {
        if (err) {
            log('warn', err, _this.debugMode);
            return reject && reject(err);
        }

        for (var channelLabel in states) {
            var state = states[channelLabel];
            state.channelLabel = channelLabel;
        }

        resolve && resolve(states);
    });
};

/**
 * @param state {Object}
 * @param callback {Function}
 */
VectorWatchStreamNode.prototype.getAuthTokensForState = function(state, callback) {
    this.oauthClient.getAccessToken(state.__auth, callback);
};

/** Delete al settings from the DB.
 * @param resolve {Function} DB update/delete success callback
 * @param reject {Function} DB update/delete fail callback
 * @returns null
 *
 **/
VectorWatchStreamNode.prototype.dbCleanUp = function (resolve, reject) {
    var _this = this;
    this.stateStorage.removeAll(function(err) {
        if (err) {
            log('warn', err, _this.debugMode);
            return reject && reject(err);
        }

        log('log', "Deleted all rows", _this.debugMode);
        resolve && resolve();
    });
};

VectorWatchStreamNode.prototype.getMiddleware = function() {
    return this.app;
};

privateMethods = {
    setupRouter: function () {
        var _this = this;
        this.app = express();
        this.app.use(bodyParser.urlencoded({ extended: true })); //support x-www-form-urlencoded
        this.app.use(bodyParser.json());
        this.app.use(expressValidator());
        this.app.use(function (req, res, next) {
            log('log', req.method + '' + req.url, _this.debugMode);
            next();
        });

        this.app.post('/callback', function (req, res, next) {
            req.assert('eventType', 'Event type is required').notEmpty();
            log('log', req.body, _this.debugMode);
            var errors = req.validationErrors();
            if (errors) {
                log('warn', "Vaidation errors encountered", _this.debugMode);
                res.status(400).json(errors);
                return;
            }
            log('log', "Request passed validation", _this.debugMode);
            var eventType = req.body.eventType;

            var state = privateMethods.getStateFromRequest.call(this, req);
            if (eventType == "USR_REG") {
                privateMethods.registerHandler.call(this, state, state.channelLabel, res);
            } else if (eventType == "USR_UNREG") {
                privateMethods.unregisterHandler.call(this, state, res);
            } else if (eventType == "REQ_AUTH") {
                privateMethods.authHandler.call(this, res);
            } else if (eventType == "REQ_CONFIG") {
                privateMethods.configHandler.call(this, state.__auth, res);
            } else if (eventType == "REQ_OPTS") {
                req.assert('settingName', 'Setting name is required').notEmpty();

                var settingName = req.body.settingName;
                var searchTerm = req.body.value || '';
                privateMethods.optionsHandler.call(this, settingName, searchTerm, state, res);
            } else {
                return next("No known event");
            }
        });
    },

    registerHandler: function registerHandler(state, channelLabel, response) {
        var promise = new Promise(), _this = this;
        promise.then(function (streamValue) {
            log('log', "Registration successfull, the response containing " + streamValue + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: [
                    {
                        type: 3,
                        streamUUID: _this.streamUID,
                        channelLabel: channelLabel,
                        d: streamValue[channelLabel] || streamValue
                    }
                ]
            });
        }, function (reason, statusCode) {
            log('log', "Registration unsuccessfull, the response containing the error message is being sent", true);
            response.status(statusCode || 400).json(reason);
        });

        privateMethods.storeSettingsItem.call(
            this, state,
            function() {
                _this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
                    if (err) return promise.reject(err);

                    _this.registerSettings(function (result) {
                        promise.resolve(result);
                    }, function (err) {
                        promise.reject(err);
                    }, state, tokens);
                });
            },
            function (err) {
                promise.reject(err);
            }
        );
    },

    unregisterHandler: function unregisterHandler(state, response) {
        var _this = this;
        privateMethods.deleteSettings.call(
            this, state,
            function() {
                _this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
                    if (err) return response.sendStatus(500);

                    _this.unregisterSettings(state, tokens);
                    response.sendStatus(200);
                });
            },
            function () {
                response.sendStatus(500);
            }
        );
    },

    authHandler: function authHandler(response) {
        if (!this.options.auth) {
            return response.sendStatus(400);
        }

        var promise = new Promise(), _this = this;
        promise.then(function(authMethod) {
            log('log', "Request auth method successfull, the response containing " + authMethod + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: authMethod
            });
        }, function(reason, statusCode) {
            log('log', "Request auth method unsuccessfull, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAuthorizationUrl(function(err, url) {
            if (err) return promise.reject(err);

            promise.resolve({
                protocol: _this.oauthClient.getProtocolName(),
                version: _this.oauthClient.getVersion(),
                loginUrl: url
            });
        });
    },

    configHandler: function configHandler(auth, response) {
        var promise = new Promise(), _this = this;
        promise.then(function(config) {
            log('log', "Request stream config successful, the response containing " + config + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: config
            });
        }, function(reason, statusCode) {
            log('log', "Request config unsuccessful, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAccessToken(auth, function(err, tokens) {
            if (err) return promise.reject(err);

            _this.requestConfig(function(result) {
                promise.resolve(result);
            }, function(err) {
                promise.reject(err);
            }, tokens);
        });
    },

    optionsHandler: function optionsHandler(settingName, searchTerm, state, response) {
        var promise = new Promise(), _this = this;
        promise.then(function(options) {
            log('log', "Request options successful, the response containing " + options + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: options
            });
        }, function (reason, statusCode) {
            log('log', "Request options unsuccessful, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
            _this.requestOptions(function (result) {
                promise.resolve(result);
            }, function (err) {
                promise.reject(err);
            }, settingName, searchTerm, state, tokens);
        });
    },

    getStateFromRequest: function getStateFromRequest(req) {
        var cleanUserSettings = function(userSettings) {
            var cleaned = { };
            for (var setting in userSettings) {
                var settingObject = userSettings[setting];
                var value = settingObject.value;
                var name = settingObject.name;

                cleaned[setting] = value || name || settingObject;
            }
            return cleaned;
        };

        var state = {};
        if (req.body.userSettings) {
            state = cleanUserSettings(req.body.userSettings);
        } else if (req.body.configStreamSettings) {
            var userSettingsMap = req.body.configStreamSettings.userSettingsMap || {};
            for (var channelLabel in userSettingsMap) {
                state = cleanUserSettings(userSettingsMap[channelLabel].userSettings || {});
                state.channelLabel = channelLabel;
                state.__auth = userSettingsMap[channelLabel].auth;
            }
        }
        if (req.body.auth) {
            state.__auth = req.body.auth;
        }

        return state;
    },

    storeSettingsItem: function storeSettingsItem(state, resolve, reject) {
        var _this = this;
        this.stateStorage.store(state.channelLabel, state, function(err) {
            if (err) {
                log('warn', err, _this.debugMode);
                return reject && reject(err);
            }

            log('log', "Setting table updated.", _this.debugMode);
            resolve && resolve();
        });
    },

    deleteSettings: function deleteSettings(state, resolve, reject) {
        var _this = this;
        this.stateStorage.remove(state.channelLabel, function(err) {
            if (err) {
                log('warn', err, _this.debugMode);
                return reject && reject(err);
            }

            log('log', "Setting table updated.", _this.debugMode);
            resolve && resolve();
        });
    }
};

module.exports = {
    /**
     * @param options {Object}
     * @returns {VectorWatchStreamNode}
     */
    createStreamNode: function(options) {
        var node = new VectorWatchStreamNode(options);

        if (options.registerSettings) {
            node.registerSettings = options.registerSettings;
        }
        if (options.unregisterSettings) {
            node.unregisterSettings = options.unregisterSettings;
        }
        if (options.requestConfig) {
            node.requestConfig = options.requestConfig;
        }
        if (options.requestOptions) {
            node.requestOptions = options.requestOptions;
        }

        return node;
    }
};
<commit_msg>Changed this to _this because of different scope.<commit_after>var express = require('express'),
    bodyParser = require('body-parser'),
    expressValidator = require('express-validator'),
    request = require('request'),
    mysql = require('mysql'),
    util = require('util'),
    OAuthProvider = require('./lib/OAuthProvider.js'),
    Promise = require("node-promise").Promise,
    pushURL = 'http://52.16.43.57:8080/VectorCloud/rest/v1/stream/push',
    privateMethods;

function log(type, text, force) {
    if (force) {
        console[type](util.inspect(text, {colors: true, depth: null}));
    }
}

function establishDBConnection(dbSettings) {
    if (dbSettings.connection) return dbSettings.connection;

    return mysql.createConnection(dbSettings);
}

var VectorWatchStreamNode = function VectorWatchStreamNode(options) {
    if (typeof options != "object" && Object.prototype.toString.call(options) == '[object Array]') {
        throw new Error('options have to be an object.');
    }
    this.options = options;
    this.debugMode = false;

    if (!options.token) {
        throw new Error('token is required.');
    }
    this.token = options.token;

    if (!options.streamUID) {
        throw new Error('streamUID is required.');
    }
    this.streamUID = options.streamUID;

    if (options.database) {
        var connection = establishDBConnection(options.database);

        var MysqlStorageProvider = require('./lib/StorageProviders/MysqlStorageProvider.js');
        this.authStorage = new MysqlStorageProvider({
            connection: connection,
            table: options.database.authStorageTable || 'Auth'
        });
        this.stateStorage = new MysqlStorageProvider({
            connection: connection,
            table: options.database.stateStorageTable || 'Settings'
        });
    } else {
        var MemoryStorageProvider = require('./lib/StorageProviders/MemoryStorageProvider.js');
        this.authStorage = new MemoryStorageProvider();
        this.stateStorage = new MemoryStorageProvider();
    }

    if (options.auth) {
        var auth = options.auth;
        if (auth.protocol.toLowerCase() != 'oauth') {
            throw new Error('Unsupported auth protocol.');
        }
        this.oauthClient = OAuthProvider.create(this.authStorage, auth);
    }

    privateMethods.setupRouter.call(this);
};

/** This function is called every time a user adds the stream to a watch face(and selects the desired settings, if needed).
 * The DB(if the stream has settings that need to be stored) is automatically updated.
 * When implementing this method the developer must call the 'resolve' function parameter after he retrives/generates the data.
 *      resolve({data:"..."});
 * Called for public streams
 * @param resolve {Function} DB insert success callback
 * @param reject {Function} DB insert fail callback
 * @param settings {Object} User settings
 * @param authTokens {Object}
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.registerSettings = function (resolve, reject, settings, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/** This function is called every time a user removes the stream from a watch face.
 Called for public streams
 * @param settings {Object} User settings
 * @param authTokens {Object}
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.unregisterSettings = function (settings, authTokens) { };

/**
 * @param resolve {Function}
 * @param reject {Function}
 * @param authTokens {Object}
 */
VectorWatchStreamNode.prototype.requestConfig = function(resolve, reject, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/**
 * @param resolve {Function}
 * @param reject {Function}
 * @param settingName {String}
 * @param searchTerm {String}
 * @param state {Object}
 * @param authTokens {Object}
 */
VectorWatchStreamNode.prototype.requestOptions = function(resolve, reject, settingName, searchTerm, state, authTokens) {
    reject && reject(new Error('Not implemented.'));
};

/** Sends update request to Vector Cloud, with all the information needed.
 * @param dataArray {Array} The array elements contain the info that will be displayed on the watch and the coresponding settings - [data, settings]
 * @returns {null}
 * */
VectorWatchStreamNode.prototype.sendDeliverRequests = function (dataArray) {
    if (Object.prototype.toString.call(dataArray) != '[object Array]') {
        log('log', "Method expects an array. Example:[{channelLabel: 'value for stream'}]");
    }

    var packets = [], _this = this;
    dataArray.forEach(function(data) {
        for (var channelLabel in data) {
            packets.push({
                type: 3,
                streamUUID: _this.streamUID,
                channelLabel: channelLabel,
                d: data[channelLabel]
            });
        }
    });

    var requestBody = {
        v: 1,
        p: packets
    };
    log('log', "The data is sent to VectorCloud.", true);
    log('log', requestBody, this.debugMode);
    var options = {
        uri: pushURL,
        method: 'POST',
        json: requestBody,
        headers: {"Authorization": this.token}
    };
    request(options, function (err, response, body) {
        if (!err && response.statusCode == 200) {
            log('log', body, _this.debugMode);
        } else {
            log('log', err, _this.debugMode);
        }
    });
};

/** Get all the settings stored in the DB. On success the resolve(settingsArray) method is called, otherwise the reject(error) method.
 * The developer can access the returned array in the resolve(settingsArray) callback, as a parameter.
 Example: sample_stream.retrieveSettings(function (settingsArray) {
                console.log(settingsArray); -> [{"City":"Bucharest", ...},{"City":"New York", ...}, ...]
            });
 * @param resolve {Function} DB select success callback
 * @param reject {Function} DB select fail callback
 * @returns null
 *
 **/
VectorWatchStreamNode.prototype.retrieveSettings = VectorWatchStreamNode.prototype.retrieveState = function (resolve, reject) {
    var _this = this;
    this.stateStorage.retrieveAll(function(err, states) {
        if (err) {
            log('warn', err, _this.debugMode);
            return reject && reject(err);
        }

        for (var channelLabel in states) {
            var state = states[channelLabel];
            state.channelLabel = channelLabel;
        }

        resolve && resolve(states);
    });
};

/**
 * @param state {Object}
 * @param callback {Function}
 */
VectorWatchStreamNode.prototype.getAuthTokensForState = function(state, callback) {
    this.oauthClient.getAccessToken(state.__auth, callback);
};

/** Delete al settings from the DB.
 * @param resolve {Function} DB update/delete success callback
 * @param reject {Function} DB update/delete fail callback
 * @returns null
 *
 **/
VectorWatchStreamNode.prototype.dbCleanUp = function (resolve, reject) {
    var _this = this;
    this.stateStorage.removeAll(function(err) {
        if (err) {
            log('warn', err, _this.debugMode);
            return reject && reject(err);
        }

        log('log', "Deleted all rows", _this.debugMode);
        resolve && resolve();
    });
};

VectorWatchStreamNode.prototype.getMiddleware = function() {
    return this.app;
};

privateMethods = {
    setupRouter: function () {
        var _this = this;
        this.app = express();
        this.app.use(bodyParser.urlencoded({ extended: true })); //support x-www-form-urlencoded
        this.app.use(bodyParser.json());
        this.app.use(expressValidator());
        this.app.use(function (req, res, next) {
            log('log', req.method + '' + req.url, _this.debugMode);
            next();
        });

        this.app.post('/callback', function (req, res, next) {
            req.assert('eventType', 'Event type is required').notEmpty();
            log('log', req.body, _this.debugMode);
            var errors = req.validationErrors();
            if (errors) {
                log('warn', "Vaidation errors encountered", _this.debugMode);
                res.status(400).json(errors);
                return;
            }
            log('log', "Request passed validation", _this.debugMode);
            var eventType = req.body.eventType;

            var state = privateMethods.getStateFromRequest.call(_this, req);
            if (eventType == "USR_REG") {
                privateMethods.registerHandler.call(_this, state, state.channelLabel, res);
            } else if (eventType == "USR_UNREG") {
                privateMethods.unregisterHandler.call(_this, state, res);
            } else if (eventType == "REQ_AUTH") {
                privateMethods.authHandler.call(_this, res);
            } else if (eventType == "REQ_CONFIG") {
                privateMethods.configHandler.call(_this, state.__auth, res);
            } else if (eventType == "REQ_OPTS") {
                req.assert('settingName', 'Setting name is required').notEmpty();

                var settingName = req.body.settingName;
                var searchTerm = req.body.value || '';
                privateMethods.optionsHandler.call(_this, settingName, searchTerm, state, res);
            } else {
                return next("No known event");
            }
        });
    },

    registerHandler: function registerHandler(state, channelLabel, response) {
        var promise = new Promise(), _this = this;
        promise.then(function (streamValue) {
            log('log', "Registration successfull, the response containing " + streamValue + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: [
                    {
                        type: 3,
                        streamUUID: _this.streamUID,
                        channelLabel: channelLabel,
                        d: streamValue[channelLabel] || streamValue
                    }
                ]
            });
        }, function (reason, statusCode) {
            log('log', "Registration unsuccessfull, the response containing the error message is being sent", true);
            response.status(statusCode || 400).json(reason);
        });

        privateMethods.storeSettingsItem.call(
            this, state,
            function() {
                _this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
                    if (err) return promise.reject(err);

                    _this.registerSettings(function (result) {
                        promise.resolve(result);
                    }, function (err) {
                        promise.reject(err);
                    }, state, tokens);
                });
            },
            function (err) {
                promise.reject(err);
            }
        );
    },

    unregisterHandler: function unregisterHandler(state, response) {
        var _this = this;
        privateMethods.deleteSettings.call(
            this, state,
            function() {
                _this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
                    if (err) return response.sendStatus(500);

                    _this.unregisterSettings(state, tokens);
                    response.sendStatus(200);
                });
            },
            function () {
                response.sendStatus(500);
            }
        );
    },

    authHandler: function authHandler(response) {
        if (!this.options.auth) {
            return response.sendStatus(400);
        }

        var promise = new Promise(), _this = this;
        promise.then(function(authMethod) {
            log('log', "Request auth method successfull, the response containing " + authMethod + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: authMethod
            });
        }, function(reason, statusCode) {
            log('log', "Request auth method unsuccessfull, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAuthorizationUrl(function(err, url) {
            if (err) return promise.reject(err);

            promise.resolve({
                protocol: _this.oauthClient.getProtocolName(),
                version: _this.oauthClient.getVersion(),
                loginUrl: url
            });
        });
    },

    configHandler: function configHandler(auth, response) {
        var promise = new Promise(), _this = this;
        promise.then(function(config) {
            log('log', "Request stream config successful, the response containing " + config + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: config
            });
        }, function(reason, statusCode) {
            log('log', "Request config unsuccessful, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAccessToken(auth, function(err, tokens) {
            if (err) return promise.reject(err);

            _this.requestConfig(function(result) {
                promise.resolve(result);
            }, function(err) {
                promise.reject(err);
            }, tokens);
        });
    },

    optionsHandler: function optionsHandler(settingName, searchTerm, state, response) {
        var promise = new Promise(), _this = this;
        promise.then(function(options) {
            log('log', "Request options successful, the response containing " + options + " is being sent", true);

            response.status(200).json({
                v: 1,
                p: options
            });
        }, function (reason, statusCode) {
            log('log', "Request options unsuccessful, the response containing the error message is being sent.", true);
            response.status(statusCode || 400).json(reason);
        });

        this.oauthClient.getAccessToken(state.__auth, function(err, tokens) {
            _this.requestOptions(function (result) {
                promise.resolve(result);
            }, function (err) {
                promise.reject(err);
            }, settingName, searchTerm, state, tokens);
        });
    },

    getStateFromRequest: function getStateFromRequest(req) {
        var cleanUserSettings = function(userSettings) {
            var cleaned = { };
            for (var setting in userSettings) {
                var settingObject = userSettings[setting];
                var value = settingObject.value;
                var name = settingObject.name;

                cleaned[setting] = value || name || settingObject;
            }
            return cleaned;
        };

        var state = {};
        if (req.body.userSettings) {
            state = cleanUserSettings(req.body.userSettings);
        } else if (req.body.configStreamSettings) {
            var userSettingsMap = req.body.configStreamSettings.userSettingsMap || {};
            for (var channelLabel in userSettingsMap) {
                state = cleanUserSettings(userSettingsMap[channelLabel].userSettings || {});
                state.channelLabel = channelLabel;
                state.__auth = userSettingsMap[channelLabel].auth;
            }
        }
        if (req.body.auth) {
            state.__auth = req.body.auth;
        }

        return state;
    },

    storeSettingsItem: function storeSettingsItem(state, resolve, reject) {
        var _this = this;
        this.stateStorage.store(state.channelLabel, state, function(err) {
            if (err) {
                log('warn', err, _this.debugMode);
                return reject && reject(err);
            }

            log('log', "Setting table updated.", _this.debugMode);
            resolve && resolve();
        });
    },

    deleteSettings: function deleteSettings(state, resolve, reject) {
        var _this = this;
        this.stateStorage.remove(state.channelLabel, function(err) {
            if (err) {
                log('warn', err, _this.debugMode);
                return reject && reject(err);
            }

            log('log', "Setting table updated.", _this.debugMode);
            resolve && resolve();
        });
    }
};

module.exports = {
    /**
     * @param options {Object}
     * @returns {VectorWatchStreamNode}
     */
    createStreamNode: function(options) {
        var node = new VectorWatchStreamNode(options);

        if (options.registerSettings) {
            node.registerSettings = options.registerSettings;
        }
        if (options.unregisterSettings) {
            node.unregisterSettings = options.unregisterSettings;
        }
        if (options.requestConfig) {
            node.requestConfig = options.requestConfig;
        }
        if (options.requestOptions) {
            node.requestOptions = options.requestOptions;
        }

        return node;
    }
};
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var YML = require('js-yaml');
var osHomedir = require('os-homedir');
var deepExtend = require('deep-extend');
var deepFreeze = require('deep-freeze');
var NODE_ENV = process.env.NODE_ENV;

/**
 * findFile
 * returns the first filename found in directory
 * @arg { string } dirPath
 * @arg { string[] } filenames
 * @return { string? } filename
 */
function findFile (dirPath, filenames) {
    try {
        var ls = fs.readdirSync(dirPath);
        for (var i = 0, listLen = ls.length; i < listLen; i++) {
            for (var ii = 0, fileLen = filenames.length; ii < fileLen; ii++) {
                if (ls[i] === filenames[ii]) return filenames[ii];
            }
        }
    } catch (_) { /* ignore error */ }
    // return undefined;
}

/**
 * findClosestFile
 * returns the first filename found in any parent directory until "/" (root)
 * @arg { string } dirPath
 * @arg { string[] } filenames
 * @return { string? } filename
 */
function findClosestFile (dirPath, filenames) {
    var found;
    var currentDir = path.resolve(dirPath);

    for (;;) {
        found = findFile(currentDir, filenames);

        if (found || currentDir === '/') {
            break;
        }

        currentDir = path.resolve(currentDir, '..');
    }

    return found;
}

/**
 * loadFile
 * read and parses a yml files content (nice side effect: JSON is valid yaml syntax)
 *
 * @arg { string } filePath
 * @return { Object } config file contents
 */
function loadFile (filePath) {
    try {
        var data = fs.readFileSync(filePath, 'utf8');
        return YML.safeLoad(data);
    }
    catch (err) {
        throw new Error(
            'apprc could not parse file "' + filePath + '":' + err.message
        );
    }
}

/**
 * getDefaultLocations
 * returns a list of lookup paths
 *
 * @arg { string } appName
 * @return { string[] } lookup paths
 */
function getDefaultLocations (appName) {
    return [
        findClosestFile(process.cwd(), [ '.' + appName + 'rc', '.' + appName + 'rc.yml', '.' + appName + 'rc.json' ]),
        path.join(osHomedir(), '.' + appName + 'rc'),
        path.join(osHomedir(), appName, '/config'),
        path.join(osHomedir(), '.' + appName, '/config'),
        path.join(osHomedir(), '/.config/', appName),
        path.join(osHomedir(), '/.config/', appName, '/config'),
        path.join('/etc/', appName + 'rc'),
        path.join('/etc/', appName, '/config')
    ];
}

/**
 * apprc
 * @arg { Object? } defaults
 * @arg { string? } envKey name of environment-variable switch
 * @arg { string? } appName name of app
 * @arg { string? | string[]? } locations overwrite lookup-paths
 */
module.exports = function apprc (_extraVars, _envKey, _appName, _locations) {
    // parse arguments
    var pkg = loadFile(findClosestFile(process.cwd(), ['package.json']));

    var defaults = _extraVars || {};
    var envKey = _envKey != null ? _envKey : NODE_ENV || 'development';
    var appName = _appName || pkg.name;

    if (!appName) {
        throw new Error(
            'apprc could not detect "appName". ' +
            'Please make sure there is a package.json with a name property ' +
            'or provide it manually.'
        );
    }

    // list of all locations that may contain config files
    var locations;

    if (typeof _locations === 'string') {
        locations = _locations.split(';');
    }
    else if (Array.isArray(_locations)) {
        locations = _locations;
    }

    if (!locations) {
        locations = getDefaultLocations(appName);
    }

    // validate and dedupe list
    locations = locations.reduce(function (results, nextLoc) {
        if (!nextLoc) return results; // findClosestFile() may return undefined

        // find existing files
        var found = findFile(path.dirname(nextLoc), [ nextLoc, nextLoc + '.yml', nextLoc + '.json' ]);
        if (found && !results.includes(found)) results.push(nextLoc);

        return results;
    }, []);

    // load all and deep-merge
    var merged = deepExtend.apply(null, locations.map(loadFile));

    // compose returned config
    var finalConfig = deepExtend(
        defaults,
        envKey
            ? deepExtend(merged.defaults || {}, merged[envKey] || {})
            : merged
        ,
        {
            appName: appName,
            configs: locations
        }
    );

    // freeze it!
    return deepFreeze(finalConfig);
};
<commit_msg>fix node v4 test<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var YML = require('js-yaml');
var osHomedir = require('os-homedir');
var deepExtend = require('deep-extend');
var deepFreeze = require('deep-freeze');
var NODE_ENV = process.env.NODE_ENV;

/**
 * legacy isArray
 * @arg { any }
 * @returns { bool }
 */

var isArray = Array.isArray || function (arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';
};

/**
 * findFile
 * returns the first filename found in directory
 * @arg { string } dirPath
 * @arg { string[] } filenames
 * @return { string? } filename
 */
function findFile (dirPath, filenames) {
    try {
        var ls = fs.readdirSync(dirPath);
        for (var i = 0, listLen = ls.length; i < listLen; i++) {
            for (var ii = 0, fileLen = filenames.length; ii < fileLen; ii++) {
                if (ls[i] === filenames[ii]) return filenames[ii];
            }
        }
    } catch (_) { /* ignore error */ }
    // return undefined;
}

/**
 * findClosestFile
 * returns the first filename found in any parent directory until "/" (root)
 * @arg { string } dirPath
 * @arg { string[] } filenames
 * @return { string? } filename
 */
function findClosestFile (dirPath, filenames) {
    var found;
    var currentDir = path.resolve(dirPath);

    for (;;) {
        found = findFile(currentDir, filenames);

        if (found || currentDir === '/') {
            break;
        }

        currentDir = path.resolve(currentDir, '..');
    }

    return found;
}

/**
 * loadFile
 * read and parses a yml files content (nice side effect: JSON is valid yaml syntax)
 *
 * @arg { string } filePath
 * @return { Object } config file contents
 */
function loadFile (filePath) {
    try {
        var data = fs.readFileSync(filePath, 'utf8');
        return YML.safeLoad(data);
    }
    catch (err) {
        throw new Error(
            'apprc could not parse file "' + filePath + '":' + err.message
        );
    }
}

/**
 * getDefaultLocations
 * returns a list of lookup paths
 *
 * @arg { string } appName
 * @return { string[] } lookup paths
 */
function getDefaultLocations (appName) {
    return [
        findClosestFile(process.cwd(), [ '.' + appName + 'rc', '.' + appName + 'rc.yml', '.' + appName + 'rc.json' ]),
        path.join(osHomedir(), '.' + appName + 'rc'),
        path.join(osHomedir(), appName, '/config'),
        path.join(osHomedir(), '.' + appName, '/config'),
        path.join(osHomedir(), '/.config/', appName),
        path.join(osHomedir(), '/.config/', appName, '/config'),
        path.join('/etc/', appName + 'rc'),
        path.join('/etc/', appName, '/config')
    ];
}

/**
 * apprc
 * @arg { Object? } defaults
 * @arg { string? } envKey name of environment-variable switch
 * @arg { string? } appName name of app
 * @arg { string? | string[]? } locations overwrite lookup-paths
 */
module.exports = function apprc (_extraVars, _envKey, _appName, _locations) {
    // parse arguments
    var pkg = loadFile(findClosestFile(process.cwd(), ['package.json']));

    var defaults = _extraVars || {};
    var envKey = _envKey != null ? _envKey : NODE_ENV || 'development';
    var appName = _appName || pkg.name;

    if (!appName) {
        throw new Error(
            'apprc could not detect "appName". ' +
            'Please make sure there is a package.json with a name property ' +
            'or provide it manually.'
        );
    }

    // list of all locations that may contain config files
    var locations;

    if (typeof _locations === 'string') {
        locations = _locations.split(';');
    }
    else if (isArray(_locations)) {
        locations = _locations;
    }

    if (!locations) {
        locations = getDefaultLocations(appName);
    }

    // validate and dedupe list
    locations = locations.reduce(function (results, nextLoc) {
        if (!nextLoc) return results; // findClosestFile() may return undefined

        // find existing files
        var found = findFile(path.dirname(nextLoc), [ nextLoc, nextLoc + '.yml', nextLoc + '.json' ]);
        if (found && results.indexOf(found) === -1) results.push(nextLoc);

        return results;
    }, []);

    // load all and deep-merge
    var merged = deepExtend.apply(null, locations.map(loadFile));

    // compose returned config
    var finalConfig = deepExtend(
        defaults,
        envKey
            ? deepExtend(merged.defaults || {}, merged[envKey] || {})
            : merged
        ,
        {
            appName: appName,
            configs: locations
        }
    );

    // freeze it!
    return deepFreeze(finalConfig);
};
<|endoftext|>


<commit_before>'use strict';

const aws = require('aws-sdk');
const express = require('express');
const knex = require('knex');
const morgan = require('morgan');
const rawBody = require('raw-body');
const raygun = require('raygun');
const uuid = require('uuid');

// Configuration

const ENV = process.env.NODE_ENV || 'development';
const DB_CONFIG = require('./knexfile')[ENV];
console.log(DB_CONFIG);
// AWS_ACCESS_KEY_ID
// AWS_SECRET_ACCESS_KEY
const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
const S3_BUCKET = process.env.S3_BUCKET;
const RAYGUN_API_KEY = process.env.RAYGUN_APIKEY;
const PORT = process.env.PORT || 8888;

// Database

const db = knex(DB_CONFIG);

// AWS

aws.config.region = AWS_REGION;
const s3 = new aws.S3({ params: { Bucket: S3_BUCKET } });

// Helpers

const getRawBody = (request, _response, next) => {
  rawBody(request, {
    length: request.headers['Content-Length'],
    limit: '10mb'
  }, (error, data) => {
    if (error) {
      return next(error);
    }
    request.rawBody = data;
    next();
  });
};

// API v1

const apiV1 = express.Router();

apiV1.use((request, response, next) => {
  if (request.accepts('json')) {
    next();
  } else {
    response.status(406).json(null);
  }
});

apiV1.get('/recordings', (request, response) => {
  db('recordings')
    .where('deleted_at', null)
    .orderBy('created_at', 'desc')
    .then((recordings) => {
      response.json(recordings);
    });
});

apiV1.post('/recordings', getRawBody, (request, response) => {
  const guid = uuid.v4();
  s3.upload({ Body: request.rawBody, Key: guid }, (error, _data) => {
    if (error) { throw error; }
    db('recordings')
      .insert({
        guid: guid,
        latitude: request.query.latitude,
        longitude: request.query.longitude
      })
      .then(() => {
        console.log(arguments);
        response.status(201).location(guid).json(null);
      })
      .catch((_error) => {
        response.status(400).json(null);
      });
  });
});

apiV1.get(/\/recordings\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/, (request, response) => {
  db('recordings')
    .where('guid', request.params[0])
    .where('deleted_at', null)
    .then((recordings) => {
      if (recordings.length !== 1) {
        return response.status(404).json(null);
      }
      response.json(recordings[0]);
    });
});

apiV1.delete(/\/recordings\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/, (request, response) => {
  db('recordings')
    .where('guid', request.params[0])
    .where('deleted_at', null)
    .update('deleted_at', new Date())
    .then((count) => {
      if (count !== 1) {
        return response.status(422).json(null);
      }
      response.status(204).json(null);
    });
});

apiV1.use((error, _request, response, _next) => {
  console.error(error);
  const client = new raygun.Client().init({ apiKey: RAYGUN_API_KEY });
  client.send(error);
  response.status(500).json(null);
});

// API

const api = express.Router();
api.use('/v1', apiV1);

// Application

const app = express();
app.use(morgan('combined'));
app.use('/api', api);

// Server

const server = app.listen(PORT, () => {
  console.log('Listening on port ' + server.address().port + '...');
});
<commit_msg>Remove database config logging<commit_after>'use strict';

const aws = require('aws-sdk');
const express = require('express');
const knex = require('knex');
const morgan = require('morgan');
const rawBody = require('raw-body');
const raygun = require('raygun');
const uuid = require('uuid');

// Configuration

const ENV = process.env.NODE_ENV || 'development';
const DB_CONFIG = require('./knexfile')[ENV];
// AWS_ACCESS_KEY_ID
// AWS_SECRET_ACCESS_KEY
const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
const S3_BUCKET = process.env.S3_BUCKET;
const RAYGUN_API_KEY = process.env.RAYGUN_APIKEY;
const PORT = process.env.PORT || 8888;

// Database

const db = knex(DB_CONFIG);

// AWS

aws.config.region = AWS_REGION;
const s3 = new aws.S3({ params: { Bucket: S3_BUCKET } });

// Helpers

const getRawBody = (request, _response, next) => {
  rawBody(request, {
    length: request.headers['Content-Length'],
    limit: '10mb'
  }, (error, data) => {
    if (error) {
      return next(error);
    }
    request.rawBody = data;
    next();
  });
};

// API v1

const apiV1 = express.Router();

apiV1.use((request, response, next) => {
  if (request.accepts('json')) {
    next();
  } else {
    response.status(406).json(null);
  }
});

apiV1.get('/recordings', (request, response) => {
  db('recordings')
    .where('deleted_at', null)
    .orderBy('created_at', 'desc')
    .then((recordings) => {
      response.json(recordings);
    });
});

apiV1.post('/recordings', getRawBody, (request, response) => {
  const guid = uuid.v4();
  s3.upload({ Body: request.rawBody, Key: guid }, (error, _data) => {
    if (error) { throw error; }
    db('recordings')
      .insert({
        guid: guid,
        latitude: request.query.latitude,
        longitude: request.query.longitude
      })
      .then(() => {
        console.log(arguments);
        response.status(201).location(guid).json(null);
      })
      .catch((_error) => {
        response.status(400).json(null);
      });
  });
});

apiV1.get(/\/recordings\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/, (request, response) => {
  db('recordings')
    .where('guid', request.params[0])
    .where('deleted_at', null)
    .then((recordings) => {
      if (recordings.length !== 1) {
        return response.status(404).json(null);
      }
      response.json(recordings[0]);
    });
});

apiV1.delete(/\/recordings\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/, (request, response) => {
  db('recordings')
    .where('guid', request.params[0])
    .where('deleted_at', null)
    .update('deleted_at', new Date())
    .then((count) => {
      if (count !== 1) {
        return response.status(422).json(null);
      }
      response.status(204).json(null);
    });
});

apiV1.use((error, _request, response, _next) => {
  console.error(error);
  const client = new raygun.Client().init({ apiKey: RAYGUN_API_KEY });
  client.send(error);
  response.status(500).json(null);
});

// API

const api = express.Router();
api.use('/v1', apiV1);

// Application

const app = express();
app.use(morgan('combined'));
app.use('/api', api);

// Server

const server = app.listen(PORT, () => {
  console.log('Listening on port ' + server.address().port + '...');
});
<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var osc = require('osc');

var inport = 3333;
var outport = 7400;

var MESSAGE_SCHEMA = {
  type: 'object',
  properties: {
    adress: {
      type: 'string',
      required: true
    },
    args: {
      type: 'string',
      required: true
    }
  }
};

var OPTIONS_SCHEMA = {
  type: 'object',
  properties: {
    firstExampleOption: {
      type: 'string',
      required: true
    }
  }
};

function Plugin(){
  this.options = {};
  this.messageSchema = MESSAGE_SCHEMA;
  this.optionsSchema = OPTIONS_SCHEMA;
  return this;
}
util.inherits(Plugin, EventEmitter);

 var udpPort = new osc.UDPPort({
    localAddress: "0.0.0.0",
    localPort: 7400
});

udpPort.open();

Plugin.prototype.onMessage = function(message){
  var payload = message.payload;
  if(payload.port){ inport = payload.port;}
  if(payload.bundle){
  udpPort.send(payload.bundle, "127.0.0.1", inport);
}else{

  udpPort.send(payload, "127.0.0.1", inport);
}

  };

Plugin.prototype.onConfig = function(device){
  var self = this;
  this.setOptions(device.options||{});

// Listen for incoming OSC bundles.
udpPort.on("bundle", function (oscBundle) {
    console.log("An OSC bundle just arrived!", oscBundle);

      self.emit("message", {devices: ['*'], "payload": oscBundle
             });

});

// Open the socket.

};

Plugin.prototype.setOptions = function(options){
  this.options = options;
};

module.exports = {
  messageSchema: MESSAGE_SCHEMA,
  optionsSchema: OPTIONS_SCHEMA,
  Plugin: Plugin
};
<commit_msg>fixed something<commit_after>'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var osc = require('osc');

var inport = 3333;
var outport = 7400;

var MESSAGE_SCHEMA = {
  type: 'object',
  properties: {
    address: {
      type: 'string',
      required: true
    },
    args: {
      type: 'string',
      required: true
    }
  }
};

var OPTIONS_SCHEMA = {
  type: 'object',
  properties: {
    firstExampleOption: {
      type: 'string',
      required: true
    }
  }
};

function Plugin(){
  this.options = {};
  this.messageSchema = MESSAGE_SCHEMA;
  this.optionsSchema = OPTIONS_SCHEMA;
  return this;
}
util.inherits(Plugin, EventEmitter);

 var udpPort = new osc.UDPPort({
    localAddress: "0.0.0.0",
    localPort: 7400
});

udpPort.open();

Plugin.prototype.onMessage = function(message){
  var payload = message.payload;
  if(payload.port){ inport = payload.port;}
  if(payload.bundle){
  udpPort.send(payload.bundle, "127.0.0.1", inport);
}else{

  udpPort.send(payload, "127.0.0.1", inport);
}

  };

Plugin.prototype.onConfig = function(device){
  var self = this;
  this.setOptions(device.options||{});

// Listen for incoming OSC bundles.
udpPort.on("bundle", function (oscBundle) {
    console.log("An OSC bundle just arrived!", oscBundle);

      self.emit("message", {devices: ['*'], "payload": oscBundle
             });

});

// Open the socket.

};

Plugin.prototype.setOptions = function(options){
  this.options = options;
};

module.exports = {
  messageSchema: MESSAGE_SCHEMA,
  optionsSchema: OPTIONS_SCHEMA,
  Plugin: Plugin
};
<|endoftext|>


<commit_before>// cop - emit value for particular key

module.exports = cop

var Stream = require('stream').Stream
  , key = require('./lib/key.js')

function cop () {
  var args = Array.prototype.slice.call(arguments)
    , first = args[0]
    , isFunction = typeof first === 'function'
    , fun = isFunction ? args.shift() : key

  var stream = new Stream()

  stream.readable = true
  stream.writable = true

  stream.write = function (obj) {
    var value = fun.apply(null, [obj].concat(args))
    if (value) stream.emit('data', value)
  }

  // fstream
  stream.add = stream.write

  stream.end = function () {
    stream.emit('end')
  }

  return stream
}
<commit_msg>Return true from write<commit_after>// cop - emit value for particular key

module.exports = cop

var Stream = require('stream').Stream
  , key = require('./lib/key.js')

function cop () {
  var args = Array.prototype.slice.call(arguments)
    , first = args[0]
    , isFunction = typeof first === 'function'
    , fun = isFunction ? args.shift() : key

  var stream = new Stream()

  stream.readable = true
  stream.writable = true

  stream.write = function (obj) {
    var value = fun.apply(null, [obj].concat(args))
    if (value) stream.emit('data', value)
    return true
  }

  // fstream
  stream.add = stream.write

  stream.end = function () {
    stream.emit('end')
  }

  return stream
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, ListView, Dimensions, ViewPropTypes } from 'react-native';
import { chunkArray } from './utils';

class SuperGrid extends Component {
  constructor(props) {
    super(props);
    this.renderRow = this.renderRow.bind(this);
    this.onLayout = this.onLayout.bind(this);
    this.getDimensions = this.getDimensions.bind(this);
    this.state = this.getDimensions();
  }

  onLayout(e) {
    const {staticDimension, horizontal} = this.props;
    if (!staticDimension) {
      const { width, height } = e.nativeEvent.layout || {};

      this.setState({
        ...this.getDimensions(horizontal ? height : width),
      });
    }

  }

  getDimensions(lvDimension) {
    const { itemDimension, spacing, fixed, staticDimension, horizontal } = this.props;
    const dimension = horizontal ? 'height' : 'width';
    const totalDimension = lvDimension || staticDimension || Dimensions.get('window')[dimension];
    const itemTotalDimension = itemDimension + spacing;
    const availableDimension = totalDimension - spacing; // One spacing extra
    const itemsPerRow = Math.floor(availableDimension / itemTotalDimension);
    const containerDimension = availableDimension / itemsPerRow;

    return {
      itemDimension,
      spacing,
      itemsPerRow,
      containerDimension,
      fixed,
    };
  }

  renderVerticalRow(data, sectionId, rowId) {
    const { itemDimension, spacing, containerDimension, fixed } = this.state;
    const columnStyle = {
      flexDirection: 'column',
      justifyContent: 'flex-start',
      paddingRight: spacing,
      height: containerDimension,
    };
    let itemStyle = {};
    if (fixed) {
      itemStyle = {
        height: itemDimension,
        alignSelf: 'center',
      };
    }

    return (
      <View style={columnStyle}>
        {(data || []).map((item, i) => (
          <View key={`${rowId}_${i}`} style={itemStyle}>
            {this.props.renderItem(item, i)}
          </View>
        ))}
      </View>
    );
  }

  renderHorizontalRow(data, sectionId, rowId) {
    const { itemDimension, containerDimension, spacing, fixed } = this.state;
    const rowStyle = {
      flexDirection: 'row',
      paddingLeft: spacing,
      paddingBottom: spacing,
    };
    const columnStyle = {
      flexDirection: 'column',
      justifyContent: 'center',
      width: containerDimension,
      paddingRight: spacing,
    };
    let itemStyle = {};
    if (fixed) {
      itemStyle = {
        width: itemDimension,
        alignSelf: 'center',
      };
    }

    return (
      <View style={rowStyle}>
        {(data || []).map((item, i) => (
          <View key={`${rowId}_${i}`} style={columnStyle}>
            <View style={itemStyle}>
              {this.props.renderItem(item, i)}
            </View>
          </View>
        ))}
      </View>
    );
  }

  renderRow(data, sectionId, rowId) {
    const { horizontal } = this.props;
    if (horizontal) {
      return this.renderVerticalRow(data, sectionId, rowId);
    }
    return this.renderHorizontalRow(data, sectionId, rowId);
  }

  render() {
    const { items, style, renderItem, spacing, fixed, itemDimension, horizontal, ...props } = this.props;
    const { itemsPerRow } = this.state;

    const rows = chunkArray(items, itemsPerRow);
    const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });

    return (
      <ListView
        style={[{ paddingTop: spacing }, style]}
        onLayout={this.onLayout}
        dataSource={ds.cloneWithRows(rows)}
        renderRow={this.renderRow}
        {...props}
        horizontal={horizontal}
      />
    );
  }
}

SuperGrid.propTypes = {
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.arrayOf(PropTypes.any).isRequired,
  itemDimension: PropTypes.number,
  fixed: PropTypes.bool,
  spacing: PropTypes.number,
  style: ViewPropTypes.style,
  staticDimension: PropTypes.number,
  horizontal: PropTypes.bool
};

SuperGrid.defaultProps = {
  fixed: false,
  itemDimension: 120,
  spacing: 10,
  style: {},
  staticDimension: undefined,
  horizontal: false
};

export default SuperGrid;
<commit_msg>fixes on code<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, ListView, Dimensions, ViewPropTypes } from 'react-native';
import { chunkArray } from './utils';

class SuperGrid extends Component {
  constructor(props) {
    super(props);
    this.renderRow = this.renderRow.bind(this);
    this.onLayout = this.onLayout.bind(this);
    this.getDimensions = this.getDimensions.bind(this);
    this.state = this.getDimensions();
  }

  onLayout(e) {
    const {staticDimension, horizontal} = this.props;
    if (!staticDimension) {
      const { width, height } = e.nativeEvent.layout || {};

      this.setState({
        ...this.getDimensions(horizontal ? height : width),
      });
    }

  }

  getDimensions(lvDimension) {
    const { itemDimension, spacing, fixed, staticDimension, horizontal } = this.props;
    const dimension = horizontal ? 'height' : 'width';
    const totalDimension = lvDimension || staticDimension || Dimensions.get('window')[dimension];
    const itemTotalDimension = itemDimension + spacing;
    const availableDimension = totalDimension - spacing; // One spacing extra
    const itemsPerRow = Math.floor(availableDimension / itemTotalDimension);
    const containerDimension = availableDimension / itemsPerRow;

    return {
      itemDimension,
      spacing,
      itemsPerRow,
      containerDimension,
      fixed,
    };
  }

  renderVerticalRow(data, sectionId, rowId) {
    const { itemDimension, spacing, containerDimension, fixed } = this.state;
    const columnStyle = {
      flexDirection: 'column',
      paddingLeft: spacing,
    };
    const rowStyle = {
      height: containerDimension,
      paddingBottom: spacing,
    };
    let itemStyle = {};
    if (fixed) {
      itemStyle = {
        height: itemDimension,
        alignSelf: 'center',
        paddingBottom: spacing,
      };
    }

    return (
      <View style={columnStyle}>
        {(data || []).map((item, i) => (
          <View key={`${rowId}_${i}`} style={rowStyle}>
            <View style={itemStyle}>
              {this.props.renderItem(item, i)}
            </View>
          </View>
        ))}
      </View>
    );
  }

  renderHorizontalRow(data, sectionId, rowId) {
    const { itemDimension, containerDimension, spacing, fixed } = this.state;
    const rowStyle = {
      flexDirection: 'row',
      paddingLeft: spacing,
      paddingBottom: spacing,
    };
    const columnStyle = {
      flexDirection: 'column',
      justifyContent: 'center',
      width: containerDimension,
      paddingRight: spacing,
    };
    let itemStyle = {};
    if (fixed) {
      itemStyle = {
        width: itemDimension,
        alignSelf: 'center',
      };
    }

    return (
      <View style={rowStyle}>
        {(data || []).map((item, i) => (
          <View key={`${rowId}_${i}`} style={columnStyle}>
            <View style={itemStyle}>
              {this.props.renderItem(item, i)}
            </View>
          </View>
        ))}
      </View>
    );
  }

  renderRow(data, sectionId, rowId) {
    const { horizontal } = this.props;
    if (horizontal) {
      return this.renderVerticalRow(data, sectionId, rowId);
    }
    return this.renderHorizontalRow(data, sectionId, rowId);
  }

  render() {
    const { items, style, renderItem, spacing, fixed, itemDimension, horizontal, ...props } = this.props;
    const { itemsPerRow } = this.state;

    const rows = chunkArray(items, itemsPerRow);
    const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });

    return (
      <ListView
        style={[{ paddingTop: spacing }, style]}
        onLayout={this.onLayout}
        dataSource={ds.cloneWithRows(rows)}
        renderRow={this.renderRow}
        {...props}
        horizontal={horizontal}
      />
    );
  }
}

SuperGrid.propTypes = {
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.arrayOf(PropTypes.any).isRequired,
  itemDimension: PropTypes.number,
  fixed: PropTypes.bool,
  spacing: PropTypes.number,
  style: ViewPropTypes.style,
  staticDimension: PropTypes.number,
  horizontal: PropTypes.bool
};

SuperGrid.defaultProps = {
  fixed: false,
  itemDimension: 120,
  spacing: 10,
  style: {},
  staticDimension: undefined,
  horizontal: false
};

export default SuperGrid;
<|endoftext|>