<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 : {};

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

    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') {
          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 });
            }
          });
        }
        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;

      // 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);
        }
      }

      callback(null, file);
    }

    doLeverage();
  });
};
<commit_msg>Fix #8. Remove redundant null file check<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') {
          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 });
            }
          });
        }
        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;

      // 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);
        }
      }

      callback(null, file);
    }

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


<commit_before>// jshint strict:true, node:true, camelcase:true, curly:true, maxcomplexity:15, newcap:true
"use strict";

var fs = require('fs');
var paths = require('path');
var colors = require('colors');
var pillarsPackage = require('./package');

// Splash screen...
console.log(("\n\n"+
"      ###########################################################\n"+
"      ##·······················································##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##·······················································##  "+pillarsPackage.description+"\n"+
"      ###########################################################  v"+pillarsPackage.version+"\n\n"
).replace(/·/g,' '.bgRed).replace(/#/g,' '.bgWhite));

// Pillars base export (for cyclical requires)
var pillars = module.exports = {
  debug: true
};

// Pillars version
Object.defineProperty(pillars,"version",{
  enumerable : true,
  get : function(){return pillarsPackage.version;}
});

// Path & resolve
pillars.path = __dirname;
pillars.resolve = function(path){
  return paths.resolve(pillars.path,path);
};

// Configuration propierties & config method
pillars.config = {
  cors: false,
  maxUploadSize: 5*1024*1024,
  maxCacheFileSize : 5*1024*1024,
  cacheMaxSamples : 100,
  cacheMaxSize : 250*1024*1024,
  cacheMaxItems : 5000,
  fileMaxAge : 7*24*60*60,
  renderReload: false
};

pillars.configure = function(config){
  for(var i=0,k=Object.keys(config),l=k.length;i<l;i++){
    pillars.config[k[i]]=config[k[i]];
  }
  return pillars;
};

// Dependencies, globals...
global.templated = require('templated');
global.crier = require('crier');
var crier = global.crier.addGroup('pillars');
crier.constructor.console.language = 'en';
var i18n = global.textualization = require('textualization');
i18n.load('pillars',paths.join(__dirname,'./languages'));
i18n.languages = ['en'];
var Procedure = global.Procedure = require('procedure');
var ObjectArray = global.ObjectArray = require('objectarray');
var Scheduled = global.Scheduled = require('scheduled');
require('date.format');
require('string.format');
require('json.decycled');

// Pillars components
var Gangway = global.Gangway = require('./lib/Gangway');
var Route = global.Route = require('./lib/Route');
var Plugin = global.Plugin = require('./lib/Plugin');
var HttpService = global.HttpService = require('./lib/HttpService');

// Plugins & Routes & Services
pillars.plugins = new ObjectArray();
pillars.routes = new ObjectArray();
pillars.services = new ObjectArray();

// Setup log file
function logFileLoader(){
  if(pillars.logFile){
    pillars.logFile.end();
  }
  pillars.logFile = fs.createWriteStream('./logs/'+(new Date()).format('{YYYY}{MM}{DD}')+'.log',{flags: 'a'})
    .on('open',function(fd){
      crier.log('logfile.ok');
    })
    .on('error',function(error){
      crier.warn('logfile.error',{error:error});
    })
  ;
}
logFileLoader();

crier.rules.add({
  id:'logFile',
  rule:function(stores,location,lvl,msg,meta){
    if(['log','alert','error','warn'].indexOf(lvl)>=0){
      stores.push('logFile');
    }
  }
});
crier.stores.add({
  id:'logFile',
  handler: function(location,lvl,msg,meta,done){
    var line = (new Date()).format('{YYYY}/{MM}/{DD} {hh}:{mm}:{ss}:{mss}',true)+'\t'+lvl.toUpperCase()+'\t'+location.join('.')+'\t'+JSON.decycled(msg)+'\t'+JSON.decycled(meta)+'\n';
    pillars.logFile.write(line);
    done.result(line);
  }
});

pillars.logCleaner = new Scheduled({
  id: 'logCleaner',
  pattern: '0 0 *',
  task: logFileLoader
}).start();

// Load builtin Plugins
var plugins = [
  require('./plugins/langPath.js'),
  require('./plugins/encoding.js'),
  require('./plugins/router.js'),
  require('./plugins/maxUploadSize.js'),
  require('./plugins/CORS.js'),
  require('./plugins/OPTIONS.js'),
  //require('./plugins/sessions.js'),
  require('./plugins/directory.js'),
  require('./plugins/bodyReader.js')
];

for(var i=0,l=plugins.length;i<l;i++){
  pillars.plugins.insert(plugins[i]);
}
crier.info('plugins.loaded',{list:pillars.plugins.keys()});

// Pillars handler
pillars.handler = function pillarsHandler(req,res){
  var gw = new Gangway(req,res);
  var pluginHandling = new Procedure();
  for(var i=0,l=pillars.plugins.length;i<l;i++){
    var plugin = pillars.plugins[i];
    pluginHandling.add(plugin.id,plugin.handler,gw);
  }
  pluginHandling.launch(function(errors){
    if(errors){
      gw.error(500,errors[0]);
    } else if(gw.routing && gw.routing.handlers && gw.routing.handlers.length>0){
      var routeHandling = new Procedure();
      var routeNames = ObjectArray.prototype.keys.call(gw.routing.routes).join('.');
      for(var i=0,l=gw.routing.handlers.length;i<l;i++){
        var handler = gw.routing.handlers[i];
        var handlerName = routeNames+".";
        handlerName += handler.name?handler.name:i+1===l?"handler":"middleware["+i+"]";
        routeHandling.add(handlerName,handler,gw);
      }
      routeHandling.launch(function(errors){
        if(errors){
          gw.error(500,errors[0]);
        }
      });
    } else {
      gw.error(404);
    }
  });
};

// Default http service
pillars.services.insert(new HttpService({id:'http'}));

// Shutdown control
var shuttingdown = false;
var shuttingdownCounter = 0;
process.on('SIGINT', function() {
  if(!shuttingdown){
    shuttingdown = true;
    var procedure = new Procedure();
    var i,l;
    for(i=0,l=pillars.services.length;i<l;i++){
      if(typeof pillars.services[i].stop === 'function'){
        procedure.add(pillars.services[i].stop);
      }
    }
    Scheduled.close();
    procedure.race().launch(function(errors){
      if(errors){
        crier.error('shutdown.errors',{errors:errors},processExit);
      } else {
        crier.info('shutdown.ok',processExit);
      }
    });
  } else {
    shuttingdownCounter++;
    crier.info('shuttingdown');
    if(shuttingdownCounter>=3){
      crier.info('forced-shuttingdown');
      processExit();
    }
  }
});

function processExit(){
  setTimeout(process.exit,500);
}

<commit_msg>shutdown control fix<commit_after>// jshint strict:true, node:true, camelcase:true, curly:true, maxcomplexity:15, newcap:true
"use strict";

var fs = require('fs');
var paths = require('path');
var colors = require('colors');
var pillarsPackage = require('./package');

// Splash screen...
console.log(("\n\n"+
"      ###########################################################\n"+
"      ##·······················································##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##·······················································##  "+pillarsPackage.description+"\n"+
"      ###########################################################  v"+pillarsPackage.version+"\n\n"
).replace(/·/g,' '.bgRed).replace(/#/g,' '.bgWhite));

// Pillars base export (for cyclical requires)
var pillars = module.exports = {
  debug: true
};

// Pillars version
Object.defineProperty(pillars,"version",{
  enumerable : true,
  get : function(){return pillarsPackage.version;}
});

// Path & resolve
pillars.path = __dirname;
pillars.resolve = function(path){
  return paths.resolve(pillars.path,path);
};

// Configuration propierties & config method
pillars.config = {
  cors: false,
  maxUploadSize: 5*1024*1024,
  maxCacheFileSize : 5*1024*1024,
  cacheMaxSamples : 100,
  cacheMaxSize : 250*1024*1024,
  cacheMaxItems : 5000,
  fileMaxAge : 7*24*60*60,
  renderReload: false
};

pillars.configure = function(config){
  for(var i=0,k=Object.keys(config),l=k.length;i<l;i++){
    pillars.config[k[i]]=config[k[i]];
  }
  return pillars;
};

// Dependencies, globals...
global.templated = require('templated');
global.crier = require('crier');
var crier = global.crier.addGroup('pillars');
crier.constructor.console.language = 'en';
var i18n = global.textualization = require('textualization');
i18n.load('pillars',paths.join(__dirname,'./languages'));
i18n.languages = ['en'];
var Procedure = global.Procedure = require('procedure');
var ObjectArray = global.ObjectArray = require('objectarray');
var Scheduled = global.Scheduled = require('scheduled');
require('date.format');
require('string.format');
require('json.decycled');

// Pillars components
var Gangway = global.Gangway = require('./lib/Gangway');
var Route = global.Route = require('./lib/Route');
var Plugin = global.Plugin = require('./lib/Plugin');
var HttpService = global.HttpService = require('./lib/HttpService');

// Plugins & Routes & Services
pillars.plugins = new ObjectArray();
pillars.routes = new ObjectArray();
pillars.services = new ObjectArray();

// Setup log file
function logFileLoader(){
  if(pillars.logFile){
    pillars.logFile.end();
  }
  pillars.logFile = fs.createWriteStream('./logs/'+(new Date()).format('{YYYY}{MM}{DD}')+'.log',{flags: 'a'})
    .on('open',function(fd){
      crier.log('logfile.ok');
    })
    .on('error',function(error){
      crier.warn('logfile.error',{error:error});
    })
  ;
}
logFileLoader();

crier.rules.add({
  id:'logFile',
  rule:function(stores,location,lvl,msg,meta){
    if(['log','alert','error','warn'].indexOf(lvl)>=0){
      stores.push('logFile');
    }
  }
});
crier.stores.add({
  id:'logFile',
  handler: function(location,lvl,msg,meta,done){
    var line = (new Date()).format('{YYYY}/{MM}/{DD} {hh}:{mm}:{ss}:{mss}',true)+'\t'+lvl.toUpperCase()+'\t'+location.join('.')+'\t'+JSON.decycled(msg)+'\t'+JSON.decycled(meta)+'\n';
    pillars.logFile.write(line);
    done.result(line);
  }
});

pillars.logCleaner = new Scheduled({
  id: 'logCleaner',
  pattern: '0 0 *',
  task: logFileLoader
}).start();

// Load builtin Plugins
var plugins = [
  require('./plugins/langPath.js'),
  require('./plugins/encoding.js'),
  require('./plugins/router.js'),
  require('./plugins/maxUploadSize.js'),
  require('./plugins/CORS.js'),
  require('./plugins/OPTIONS.js'),
  //require('./plugins/sessions.js'),
  require('./plugins/directory.js'),
  require('./plugins/bodyReader.js')
];

for(var i=0,l=plugins.length;i<l;i++){
  pillars.plugins.insert(plugins[i]);
}
crier.info('plugins.loaded',{list:pillars.plugins.keys()});

// Pillars handler
pillars.handler = function pillarsHandler(req,res){
  var gw = new Gangway(req,res);
  var pluginHandling = new Procedure();
  for(var i=0,l=pillars.plugins.length;i<l;i++){
    var plugin = pillars.plugins[i];
    pluginHandling.add(plugin.id,plugin.handler,gw);
  }
  pluginHandling.launch(function(errors){
    if(errors){
      gw.error(500,errors[0]);
    } else if(gw.routing && gw.routing.handlers && gw.routing.handlers.length>0){
      var routeHandling = new Procedure();
      var routeNames = ObjectArray.prototype.keys.call(gw.routing.routes).join('.');
      for(var i=0,l=gw.routing.handlers.length;i<l;i++){
        var handler = gw.routing.handlers[i];
        var handlerName = routeNames+".";
        handlerName += handler.name?handler.name:i+1===l?"handler":"middleware["+i+"]";
        routeHandling.add(handlerName,handler,gw);
      }
      routeHandling.launch(function(errors){
        if(errors){
          gw.error(500,errors[0]);
        }
      });
    } else {
      gw.error(404);
    }
  });
};

// Default http service
pillars.services.insert(new HttpService({id:'http'}));

// Shutdown control
var shuttingdown = false;
var shuttingdownCounter = 0;
process.on('SIGINT', function() {
  if(!shuttingdown){
    shuttingdown = true;
    var procedure = new Procedure();
    var i,l;
    for(i=0,l=pillars.services.length;i<l;i++){
      if(typeof pillars.services[i].stop === 'function'){
        procedure.add(pillars.services[i].stop);
      }
    }
    Scheduled.close();
    procedure.race().launch(function(errors){
      if(errors){
        crier.error('shutdown.errors',{errors:errors},processExit);
      } else {
        crier.info('shutdown.ok',{},processExit);
      }
    });
  } else {
    shuttingdownCounter++;
    crier.info('shuttingdown');
    if(shuttingdownCounter>=3){
      crier.info('forced-shuttingdown',{},processExit);
    }
  }
});

function processExit(){
  setTimeout(process.exit,500);
}

<|endoftext|>


<commit_before>var config = require('./config.json');

var io = require('socket.io-client');
var socket = io.connect(config.serverUrl);

 socket.on('connect', function(){
 	console.log("Connecting Device");
 	socket.emit('deviceInfo', config.device);
    socket.on('disconnect', function(){
    	console.log("Disconnected");
    });
 });
<commit_msg>getting task from server<commit_after>var config = require('./config.json');

var io = require('socket.io-client');
var socket = io.connect(config.serverUrl);

 socket.on('connect', function(){
 	console.log("Connecting Device");
 	socket.emit('deviceInfo', config.device);
 	socket.on("execute",function(data){
 		console.log(data.runitem)
 		console.log("Running tests.......");
 		socket.emit("status",{runitem: data.runitem,status: 'Running'})
 	});
    socket.on('disconnect', function(){
    	console.log("Disconnected");
    });
 });
<|endoftext|>


<commit_before>/**
 * @module Panopticon
 * @author Mark S. Everitt
 */
var EventEmitter   = require('events').EventEmitter;
var cluster        = require('cluster');
var util           = require('util');

// Panopticon data types.
var SetLog         = require('./lib/Set');
var IncLog         = require('./lib/Inc');
var SampleLog      = require('./lib/Sample');
var TimedSampleLog = require('./lib/TimedSample');

// id gets incremented with each new panopticon. This allows us to have multiple panoptica running
// in parallel without master panoptica getting messages from multiple panoptica on each worker.
var instanceCount = 0;

// Load the private methods.
var augment       = require('./private/augment');
var genericSetup  = require('./private/genericSetup');
var setupDelivery = require('./private/setupDelivery');
var masterSetup   = require('./private/masterSetup');
var workerSetup   = require('./private/workerSetup');

/**
 * The constructor for Panopticon. Handles the differences between master and worker processes.
 * Please refer to the README for more information.
 *
 * @param {Number} startTime Time in milliseconds elapsed since 1 January 1970 00:00:00 UTC.
 * @param {String} name The name of the panopticon being constructed.
 * @param {Number} interval Interval time in milliseconds.
 * @param {Number} [scaleFactor] 1 -> kHz, 1000 -> Hz. If no positive finite number is given, defaults to 1.
 * @param {Boolean} [persist] Keep a logger once initialised. Each interval reset it.
 * @param {Function} [transformer] A custom function to transform data before merging with the aggregate.
 * @constructor
 * @extends EventEmitter
 * @alias module:Panopticon
 */

function Panopticon(startTime, name, interval, scaleFactor, persist, transformer) {
	EventEmitter.call(this);
	this.id = instanceCount;
	instanceCount += 1;

	// First we sort out the methods and data which handle are local to this process.
	genericSetup(this, name, startTime, interval, scaleFactor, persist, transformer);

	// If the process is a worker, we only need to send the master results then return. If the
	// process is not a worker, it is either the master or stand alone. The master also handles
	// the delivery of aggregated data.
	if (cluster.isWorker) {
		workerSetup(this);
	} else {
		masterSetup(this);
		setupDelivery(this);
	}
}

util.inherits(Panopticon, EventEmitter);

/**
 * Class method to allow new loggers to be registered.
 *
 * @param {String}   name        Name of the logger method.
 * @param {Function} loggerClass A constructor function that conforms to the panopticon logger API.
 * @param {Function} [validator] A function that screens datapoints. It must return true for valid.
 */

Panopticon.registerMethod = function (name, loggerClass, validator) {
	if (Panopticon.hasOwnProperty(name)) {
		throw new Error('Method ' + name + ' is already registered.');
	}

	Panopticon.prototype[name] = function (path, id, dataPoint) {
		if (validator && !validator(dataPoint)) {
			return;
		}

		augment(this, loggerClass, path, id, dataPoint);
	};
};

// Register build in logger methods.
Panopticon.registerMethod('sample', SampleLog, Number.isFinite);
Panopticon.registerMethod('timedSample', TimedSampleLog, Array.isArray);
Panopticon.registerMethod('inc', IncLog);
Panopticon.registerMethod('set', SetLog);

/**
 * Clears the interval and the timeout. Removes listeners on a panopticon.
 */

Panopticon.prototype.stop = function () {
	this.emit('stopping'); // This is used to remove the cluster.fork listener on master.

	this.removeAllListeners();

	clearTimeout(this.halfInterval);
	this.halfInterval = null;

	clearInterval(this.aggregationInterval);
	this.aggregationInterval = null;

	clearTimeout(this.timer);
	this.timer = null;
};

/**
 * Static method returns the number of panoptica instances.
 *
 * @return {Number}
 */

Panopticon.count = function () {
	return instanceCount;
};

/**
 * Used for unit testing to reset the panopticon count. DO NOT USE.
 *
 * @private
 */

Panopticon._resetCount = function () {
	instanceCount = 0;
};

// This is a constructor-module, so the only thing on module.exports is the constructor itself.
module.exports = Panopticon;
<commit_msg>Users cannot overwrite the stop method now.<commit_after>/**
 * @module Panopticon
 * @author Mark S. Everitt
 */
var EventEmitter   = require('events').EventEmitter;
var cluster        = require('cluster');
var util           = require('util');

// Panopticon data types.
var SetLog         = require('./lib/Set');
var IncLog         = require('./lib/Inc');
var SampleLog      = require('./lib/Sample');
var TimedSampleLog = require('./lib/TimedSample');

// id gets incremented with each new panopticon. This allows us to have multiple panoptica running
// in parallel without master panoptica getting messages from multiple panoptica on each worker.
var instanceCount = 0;

// Load the private methods.
var augment       = require('./private/augment');
var genericSetup  = require('./private/genericSetup');
var setupDelivery = require('./private/setupDelivery');
var masterSetup   = require('./private/masterSetup');
var workerSetup   = require('./private/workerSetup');

/**
 * The constructor for Panopticon. Handles the differences between master and worker processes.
 * Please refer to the README for more information.
 *
 * @param {Number} startTime Time in milliseconds elapsed since 1 January 1970 00:00:00 UTC.
 * @param {String} name The name of the panopticon being constructed.
 * @param {Number} interval Interval time in milliseconds.
 * @param {Number} [scaleFactor] 1 -> kHz, 1000 -> Hz. If no positive finite number is given, defaults to 1.
 * @param {Boolean} [persist] Keep a logger once initialised. Each interval reset it.
 * @param {Function} [transformer] A custom function to transform data before merging with the aggregate.
 * @constructor
 * @extends EventEmitter
 * @alias module:Panopticon
 */

function Panopticon(startTime, name, interval, scaleFactor, persist, transformer) {
	EventEmitter.call(this);
	this.id = instanceCount;
	instanceCount += 1;

	// First we sort out the methods and data which handle are local to this process.
	genericSetup(this, name, startTime, interval, scaleFactor, persist, transformer);

	// If the process is a worker, we only need to send the master results then return. If the
	// process is not a worker, it is either the master or stand alone. The master also handles
	// the delivery of aggregated data.
	if (cluster.isWorker) {
		workerSetup(this);
	} else {
		masterSetup(this);
		setupDelivery(this);
	}
}

util.inherits(Panopticon, EventEmitter);

/**
 * Clears the interval and the timeout. Removes listeners on a panopticon.
 */

Panopticon.prototype.stop = function () {
	this.emit('stopping'); // This is used to remove the cluster.fork listener on master.

	this.removeAllListeners();

	clearTimeout(this.halfInterval);
	this.halfInterval = null;

	clearInterval(this.aggregationInterval);
	this.aggregationInterval = null;

	clearTimeout(this.timer);
	this.timer = null;
};

/**
 * Class method to allow new loggers to be registered.
 *
 * @param {String}   name        Name of the logger method.
 * @param {Function} loggerClass A constructor function that conforms to the panopticon logger API.
 * @param {Function} [validator] A function that screens datapoints. It must return true for valid.
 */

Panopticon.registerMethod = function (name, loggerClass, validator) {
	if (Panopticon.prototype.hasOwnProperty(name)) {
		throw new Error('Method ' + name + ' is already registered.');
	}

	Panopticon.prototype[name] = function (path, id, dataPoint) {
		if (validator && !validator(dataPoint)) {
			return;
		}

		augment(this, loggerClass, path, id, dataPoint);
	};
};

// Register build in logger methods.
Panopticon.registerMethod('sample', SampleLog, Number.isFinite);
Panopticon.registerMethod('timedSample', TimedSampleLog, Array.isArray);
Panopticon.registerMethod('inc', IncLog);
Panopticon.registerMethod('set', SetLog);

/**
 * Static method returns the number of panoptica instances.
 *
 * @return {Number}
 */

Panopticon.count = function () {
	return instanceCount;
};

/**
 * Used for unit testing to reset the panopticon count. DO NOT USE.
 *
 * @private
 */

Panopticon._resetCount = function () {
	instanceCount = 0;
};

// This is a constructor-module, so the only thing on module.exports is the constructor itself.
module.exports = Panopticon;
<|endoftext|>


<commit_before>/*!
 * set-value <https://github.com/jonschlinkert/set-value>
 *
 * Copyright (c) 2014-2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

'use strict';

var isObject = require('isobject');
var nc = require('noncharacters');

module.exports = function setValue(obj, path, val) {
  if (path == null) {
    return obj;
  }
  path = escape(path);
  var seg = (/^(.+)\.(.+)$/).exec(path);
  if (seg !== null) {
    create(obj, seg[1], seg[2], val);
    return obj;
  }
  obj[unescape(path)] = val;
  return obj;
};

function create(obj, path, child, val) {
  if (!path) return obj;
  var arr = path.split('.');
  var len = arr.length, i = 0;
  while (len--) {
    var key = unescape(arr[i++]);
    if (!obj[key] || !isObject(obj[key])) {
      obj[key] = {};
    }
    obj = obj[key];
  }
  if (typeof child === 'string') {
    child = unescape(child);
  }
  return (obj[child] = val);
}

/**
 * Escape => `\\.`
 */
function escape(str) {
  return str.split('\\.').join(nc[1]);
}

/**
 * Unescaped dots
 */
function unescape(str) {
  return str.split(nc[1]).join('.');
}

<commit_msg>refactor<commit_after>/*!
 * set-value <https://github.com/jonschlinkert/set-value>
 *
 * Copyright (c) 2014-2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

'use strict';

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

module.exports = function (obj, path, val) {
  if (typeof obj !== 'object') return obj;

  if (Array.isArray(path)) {
    path = utils.toPath(path);
  }

  if (typeof path !== 'string') {
    return obj;
  }

  var segs = path.split('.');
  var len = segs.length, i = -1;
  var res = obj;
  var last;

  while (++i < len) {
    var key = segs[i];

    while (key[key.length - 1] === '\\') {
      key = key.slice(0, -1) + '.' + segs[++i];
    }

    if (i === len - 1) {
      last = key;
      break;
    }

    if (typeof obj[key] !== 'object') {
      obj[key] = {};
    }
    obj = obj[key];
  }

  if (obj.hasOwnProperty(last) && typeof obj[last] === 'object') {
    utils.extend(obj[last], val);
  } else {
    obj[last] = val;
  }
  return res;
};

function arrayify(val) {
  return Array.isArray(val) ? val : [val];
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const autoprefixer = require('gulp-autoprefixer');
const browserSync = require('browser-sync');
const buildConfig = require('./build.conf.js');
const Builder = require('systemjs-builder');
const concat = require('gulp-concat');
const cssnano = require('gulp-cssnano');
const debounce = require('debounce');
const del = require('del');
const esLint = require('gulp-eslint');
const filter = require('gulp-filter');
const gulpif = require('gulp-if');
const htmlmin = require('gulp-htmlmin');
const imagemin = require('gulp-imagemin');
const insertLines = require('gulp-insert-lines');
const ngAnnotate = require('gulp-ng-annotate');
const pkg = require('./package.json');
const plumber = require('gulp-plumber');
const rev = require('gulp-rev');
const sass = require('gulp-sass');
const sassLint = require('gulp-sass-lint');
const Server = require('karma').Server;
const sourceMaps = require('gulp-sourcemaps');
const templateCache = require('gulp-angular-templatecache');
const uglify = require('gulp-uglify');
const usemin = require('gulp-usemin');
const getRelativeModuleFolderPath = require('./utils.js').getRelativeModuleFolderPath;

function buildTasks(customConfig, localGulp) {
  const cfg = buildConfig(customConfig);
  const gulp = localGulp || require('gulp');
  const bsServer = browserSync.create();

  function createLintFunction(src, lintConfig) {
    return function lintScripts() {
      return gulp
        .src(src)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(esLint(lintConfig))
        .pipe(esLint.format())
        .pipe(esLint.failAfterError());
    };
  }

  function clean() {
    return del([cfg.distDir, cfg.coverageDir]);
  }

  function styles() {
    return gulp
      .src(cfg.src.indexStyles)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(sassLint(cfg.sassLintConfig))
      .pipe(sassLint.format())
      .pipe(sassLint.failOnError())
      .pipe(sourceMaps.init())
      .pipe(sass({
        outputStyle: 'expanded',
      }))
      .pipe(autoprefixer({
        browsers: cfg.supportedBrowsers,
      }))
      .pipe(sourceMaps.write('./'))
      .pipe(gulp.dest(cfg.dist.styles))
      .pipe(filter('**/*.css'))
      .pipe(bsServer.stream());
  }

  function images() {
    return gulp
      .src(cfg.src.images)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(imagemin())
      .pipe(gulp.dest(cfg.dist.images))
      .pipe(bsServer.stream());
  }

  function fonts() {
    return gulp
      .src(cfg.src.fonts)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.dist.fonts))
      .pipe(bsServer.stream());
  }

  function copyFiles(done) {
    cfg.copyFiles.forEach((copySpec) => {
      gulp.src(copySpec.src)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(gulp.dest(copySpec.dest))
        .pipe(bsServer.stream());
    });
    done();
  }

  function symlinkDependencies() {
    return gulp
      .src(`${cfg.bowerDir}/**/*`)
      .pipe(gulp.dest(cfg.distDir + cfg.bowerDir));
  }

  function unlinkDependencies() {
    return del([
      cfg.targetBowerDir,
      cfg.targetNpmDir,
    ]);
  }

  function scripts(done) {
    function transpile() {
      const systemjs = new Builder();
      systemjs.config(cfg.systemjsOptions);
      return systemjs.buildStatic(cfg.src.indexScript, cfg.dist.indexScript, {
        sourceMaps: true,
      });
    }

    function annotate() {
      return gulp
        .src(cfg.dist.indexScript)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(sourceMaps.init({
          loadMaps: true,
        }))
        .pipe(ngAnnotate())
        .pipe(sourceMaps.write('./'))
        .pipe(gulp.dest(cfg.dist.scripts));
    }

    function addPolyfills() {
      const babelCorePath = getRelativeModuleFolderPath('babel-core');
      const babelPolyfill = `${babelCorePath}/browser-polyfill.js`;

      return gulp
        .src([
          babelPolyfill,
          cfg.dist.indexScript,
        ])
        .pipe(plumber(cfg.plumberOptions))
        .pipe(sourceMaps.init({
          loadMaps: true,
        }))
        .pipe(concat(`${cfg.projectName}.js`))
        .pipe(sourceMaps.write('./'))
        .pipe(gulp.dest(cfg.dist.scripts));
    }

    function html2js() {
      return gulp
        .src([
          cfg.dist.indexScript,
          cfg.src.templates,
        ])
        .pipe(plumber(cfg.plumberOptions))
        .pipe(gulpif('*.html', templateCache({
          transformUrl: function transformUrl(url) {
            return url.replace(/.*angularjs(?:\\|\/)/gi, '');
          },

          module: `${cfg.projectName}-templates`,
          standalone: true,
        })))
        .pipe(concat(`${cfg.projectName}.js`))
        .pipe(gulp.dest(cfg.dist.scripts))
        .pipe(bsServer.stream());
    }

    gulp.series(
      createLintFunction(cfg.src.scripts, cfg.esLintConfig),
      transpile,
      annotate,
      addPolyfills,
      html2js
    )(done);
  }

  function test(done) {
    function runKarma(karmaDone) {
      new Server({
        configFile: cfg.karmaConfig,
      }, karmaDone).start();
    }

    gulp.series(
      createLintFunction([cfg.src.unitTests, cfg.src.scripts], cfg.esLintTestConfig),
      runKarma
    )(done);
  }

  function testDebug(done) {
    function runKarma(karmaDone) {
      new Server({
        configFile: cfg.karmaConfig,
        browsers: ['Chrome'],
        autoWatch: true,
        singleRun: false,
      }, karmaDone).start();
    }

    gulp.series(
      createLintFunction([cfg.src.unitTests, cfg.src.scripts], cfg.esLintTestConfig),
      runKarma
    )(done);
  }

  function i18n() {
    return gulp
      .src(cfg.src.i18n)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.dist.i18n));
  }

  function dev() {
    return gulp
      .src(cfg.src.indexHtml)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function dist() {
    return gulp
      .src(cfg.dist.indexHtml)
      .pipe(usemin({
        html: [
          htmlmin(),
        ],
        css: [
          sourceMaps.init(),
          cssnano({
            safe: true,
          }),
          rev(),
          sourceMaps.write('./'),
        ],
        js: [
          sourceMaps.init(),
          uglify(),
          rev(),
          sourceMaps.write('./'),
        ],
      }))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function bsServerSync() {
    if (cfg.env.maven) {
      bsServer.init();
    } else {
      bsServer.init({
        ui: {
          port: (cfg.serverPort + 1),
        },
        server: {
          baseDir: [cfg.distDir, './'],
          middleware: cfg.serverMiddlewares,
        },
        port: cfg.serverPort,
        watchOptions: {
          ignored: '*.map',
        },
      });
    }
  }

  function bsServerSyncDist() {
    if (cfg.env.maven) {
      bsServer.init();
    } else {
      bsServer.init({
        ui: {
          port: (cfg.serverPort + 1),
        },
        server: {
          baseDir: [cfg.distDir],
          middleware: cfg.serverMiddlewares,
        },
        port: cfg.serverPort,
      });
    }
  }

  function bsInject() {
    const browserSyncVersion = pkg.dependencies['browser-sync'];
    const bsScriptPrefix = '//localhost:3000/browser-sync/browser-sync-client';
    const bsScriptPath = `${bsScriptPrefix}.${browserSyncVersion}.js`;
    const bsScriptTag = `<script src="${bsScriptPath}"></script>`;

    return gulp
      .src(cfg.src.indexHtml)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(insertLines({
        before: /<\/body>$/,
        lineBefore: bsScriptTag,
      }))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function serve(done) {
    if (cfg.env.maven) {
      gulp.series('build', 'bsInject', gulp.parallel('bsServerSync', 'watch'))(done);
    } else {
      gulp.series('build', gulp.parallel('bsServerSync', 'watch'))(done);
    }
  }

  function serveDist(done) {
    gulp.series('buildDist', 'bsServerSyncDist')(done);
  }

  function build(done) {
    if (cfg.env.maven) {
      gulp.series(
        'clean',
        gulp.parallel(
          'scripts',
          'styles',
          'images',
          'copyFiles',
          'i18n',
          'dev',
          'symlinkDependencies'
        )
      )(done);
    } else {
      gulp.series(
        'clean',
        gulp.parallel('scripts',
          'styles',
          'images',
          'copyFiles',
          'i18n',
          'dev'
        )
      )(done);
    }
  }

  function buildDist(done) {
    if (cfg.env.maven) {
      gulp.series('build', 'dist', 'unlinkDependencies')(done);
    } else {
      gulp.series('build', 'dist')(done);
    }
  }

  function watch() {
    gulp.watch(cfg.src.styles, gulp.series('styles'));
    gulp.watch(cfg.src.images, gulp.series('images'));
    gulp.watch(cfg.copyFiles.map((copySpec) => copySpec.src), gulp.series('copyFiles'));
    gulp.watch(cfg.src.fonts, gulp.series('fonts'));
    gulp.watch([
      cfg.src.scripts,
      cfg.src.templates,
      cfg.src.unitTests,
    ], debounce(gulp.series('scripts', 'test'), 200));
    gulp.watch(cfg.src.fixtures.pattern, gulp.series('test'));
    gulp.watch(cfg.src.i18n, gulp.series('i18n'));

    gulp.watch(cfg.src.bowerLinks, gulp.series('build'));

    if (cfg.env.maven) {
      gulp.watch(cfg.src.indexHtml, gulp.series('bsInject'));
    } else {
      gulp.watch(cfg.src.indexHtml, gulp.series('dev'));
    }
  }

  gulp.task(bsInject);
  gulp.task(bsServerSync);
  gulp.task(bsServerSyncDist);
  gulp.task(build);
  gulp.task(buildDist);
  gulp.task(clean);
  gulp.task(copyFiles);
  gulp.task(dev);
  gulp.task(dist);
  gulp.task(fonts);
  gulp.task(i18n);
  gulp.task(images);
  gulp.task(scripts);
  gulp.task(serve);
  gulp.task(serveDist);
  gulp.task(styles);
  gulp.task(symlinkDependencies);
  gulp.task(test);
  gulp.task(testDebug);
  gulp.task(unlinkDependencies);
  gulp.task(watch);
}

module.exports.buildTasks = buildTasks;
module.exports.buildConfig = buildConfig;
<commit_msg>Do not do coverage instrumentation for testDebug<commit_after>/*
 * Copyright 2015-2016 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const autoprefixer = require('gulp-autoprefixer');
const browserSync = require('browser-sync');
const buildConfig = require('./build.conf.js');
const Builder = require('systemjs-builder');
const concat = require('gulp-concat');
const cssnano = require('gulp-cssnano');
const debounce = require('debounce');
const del = require('del');
const esLint = require('gulp-eslint');
const filter = require('gulp-filter');
const gulpif = require('gulp-if');
const htmlmin = require('gulp-htmlmin');
const imagemin = require('gulp-imagemin');
const insertLines = require('gulp-insert-lines');
const ngAnnotate = require('gulp-ng-annotate');
const pkg = require('./package.json');
const plumber = require('gulp-plumber');
const rev = require('gulp-rev');
const sass = require('gulp-sass');
const sassLint = require('gulp-sass-lint');
const Server = require('karma').Server;
const sourceMaps = require('gulp-sourcemaps');
const templateCache = require('gulp-angular-templatecache');
const uglify = require('gulp-uglify');
const usemin = require('gulp-usemin');
const getRelativeModuleFolderPath = require('./utils.js').getRelativeModuleFolderPath;

function buildTasks(customConfig, localGulp) {
  const cfg = buildConfig(customConfig);
  const gulp = localGulp || require('gulp');
  const bsServer = browserSync.create();

  function createLintFunction(src, lintConfig) {
    return function lintScripts() {
      return gulp
        .src(src)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(esLint(lintConfig))
        .pipe(esLint.format())
        .pipe(esLint.failAfterError());
    };
  }

  function clean() {
    return del([cfg.distDir, cfg.coverageDir]);
  }

  function styles() {
    return gulp
      .src(cfg.src.indexStyles)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(sassLint(cfg.sassLintConfig))
      .pipe(sassLint.format())
      .pipe(sassLint.failOnError())
      .pipe(sourceMaps.init())
      .pipe(sass({
        outputStyle: 'expanded',
      }))
      .pipe(autoprefixer({
        browsers: cfg.supportedBrowsers,
      }))
      .pipe(sourceMaps.write('./'))
      .pipe(gulp.dest(cfg.dist.styles))
      .pipe(filter('**/*.css'))
      .pipe(bsServer.stream());
  }

  function images() {
    return gulp
      .src(cfg.src.images)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(imagemin())
      .pipe(gulp.dest(cfg.dist.images))
      .pipe(bsServer.stream());
  }

  function fonts() {
    return gulp
      .src(cfg.src.fonts)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.dist.fonts))
      .pipe(bsServer.stream());
  }

  function copyFiles(done) {
    cfg.copyFiles.forEach((copySpec) => {
      gulp.src(copySpec.src)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(gulp.dest(copySpec.dest))
        .pipe(bsServer.stream());
    });
    done();
  }

  function symlinkDependencies() {
    return gulp
      .src(`${cfg.bowerDir}/**/*`)
      .pipe(gulp.dest(cfg.distDir + cfg.bowerDir));
  }

  function unlinkDependencies() {
    return del([
      cfg.targetBowerDir,
      cfg.targetNpmDir,
    ]);
  }

  function scripts(done) {
    function transpile() {
      const systemjs = new Builder();
      systemjs.config(cfg.systemjsOptions);
      return systemjs.buildStatic(cfg.src.indexScript, cfg.dist.indexScript, {
        sourceMaps: true,
      });
    }

    function annotate() {
      return gulp
        .src(cfg.dist.indexScript)
        .pipe(plumber(cfg.plumberOptions))
        .pipe(sourceMaps.init({
          loadMaps: true,
        }))
        .pipe(ngAnnotate())
        .pipe(sourceMaps.write('./'))
        .pipe(gulp.dest(cfg.dist.scripts));
    }

    function addPolyfills() {
      const babelCorePath = getRelativeModuleFolderPath('babel-core');
      const babelPolyfill = `${babelCorePath}/browser-polyfill.js`;

      return gulp
        .src([
          babelPolyfill,
          cfg.dist.indexScript,
        ])
        .pipe(plumber(cfg.plumberOptions))
        .pipe(sourceMaps.init({
          loadMaps: true,
        }))
        .pipe(concat(`${cfg.projectName}.js`))
        .pipe(sourceMaps.write('./'))
        .pipe(gulp.dest(cfg.dist.scripts));
    }

    function html2js() {
      return gulp
        .src([
          cfg.dist.indexScript,
          cfg.src.templates,
        ])
        .pipe(plumber(cfg.plumberOptions))
        .pipe(gulpif('*.html', templateCache({
          transformUrl: function transformUrl(url) {
            return url.replace(/.*angularjs(?:\\|\/)/gi, '');
          },

          module: `${cfg.projectName}-templates`,
          standalone: true,
        })))
        .pipe(concat(`${cfg.projectName}.js`))
        .pipe(gulp.dest(cfg.dist.scripts))
        .pipe(bsServer.stream());
    }

    gulp.series(
      createLintFunction(cfg.src.scripts, cfg.esLintConfig),
      transpile,
      annotate,
      addPolyfills,
      html2js
    )(done);
  }

  function test(done) {
    function runKarma(karmaDone) {
      new Server({
        configFile: cfg.karmaConfig,
      }, karmaDone).start();
    }

    gulp.series(
      createLintFunction([cfg.src.unitTests, cfg.src.scripts], cfg.esLintTestConfig),
      runKarma
    )(done);
  }

  function testDebug(done) {
    function runKarma(karmaDone) {
      new Server({
        configFile: cfg.karmaConfig,
        browsers: ['Chrome'],
        reporters: ['progress'],
        autoWatch: true,
        singleRun: false,
      }, karmaDone).start();
    }

    gulp.series(
      createLintFunction([cfg.src.unitTests, cfg.src.scripts], cfg.esLintTestConfig),
      runKarma
    )(done);
  }

  function i18n() {
    return gulp
      .src(cfg.src.i18n)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.dist.i18n));
  }

  function dev() {
    return gulp
      .src(cfg.src.indexHtml)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function dist() {
    return gulp
      .src(cfg.dist.indexHtml)
      .pipe(usemin({
        html: [
          htmlmin(),
        ],
        css: [
          sourceMaps.init(),
          cssnano({
            safe: true,
          }),
          rev(),
          sourceMaps.write('./'),
        ],
        js: [
          sourceMaps.init(),
          uglify(),
          rev(),
          sourceMaps.write('./'),
        ],
      }))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function bsServerSync() {
    if (cfg.env.maven) {
      bsServer.init();
    } else {
      bsServer.init({
        ui: {
          port: (cfg.serverPort + 1),
        },
        server: {
          baseDir: [cfg.distDir, './'],
          middleware: cfg.serverMiddlewares,
        },
        port: cfg.serverPort,
        watchOptions: {
          ignored: '*.map',
        },
      });
    }
  }

  function bsServerSyncDist() {
    if (cfg.env.maven) {
      bsServer.init();
    } else {
      bsServer.init({
        ui: {
          port: (cfg.serverPort + 1),
        },
        server: {
          baseDir: [cfg.distDir],
          middleware: cfg.serverMiddlewares,
        },
        port: cfg.serverPort,
      });
    }
  }

  function bsInject() {
    const browserSyncVersion = pkg.dependencies['browser-sync'];
    const bsScriptPrefix = '//localhost:3000/browser-sync/browser-sync-client';
    const bsScriptPath = `${bsScriptPrefix}.${browserSyncVersion}.js`;
    const bsScriptTag = `<script src="${bsScriptPath}"></script>`;

    return gulp
      .src(cfg.src.indexHtml)
      .pipe(plumber(cfg.plumberOptions))
      .pipe(insertLines({
        before: /<\/body>$/,
        lineBefore: bsScriptTag,
      }))
      .pipe(gulp.dest(cfg.distDir))
      .pipe(bsServer.stream());
  }

  function serve(done) {
    if (cfg.env.maven) {
      gulp.series('build', 'bsInject', gulp.parallel('bsServerSync', 'watch'))(done);
    } else {
      gulp.series('build', gulp.parallel('bsServerSync', 'watch'))(done);
    }
  }

  function serveDist(done) {
    gulp.series('buildDist', 'bsServerSyncDist')(done);
  }

  function build(done) {
    if (cfg.env.maven) {
      gulp.series(
        'clean',
        gulp.parallel(
          'scripts',
          'styles',
          'images',
          'copyFiles',
          'i18n',
          'dev',
          'symlinkDependencies'
        )
      )(done);
    } else {
      gulp.series(
        'clean',
        gulp.parallel('scripts',
          'styles',
          'images',
          'copyFiles',
          'i18n',
          'dev'
        )
      )(done);
    }
  }

  function buildDist(done) {
    if (cfg.env.maven) {
      gulp.series('build', 'dist', 'unlinkDependencies')(done);
    } else {
      gulp.series('build', 'dist')(done);
    }
  }

  function watch() {
    gulp.watch(cfg.src.styles, gulp.series('styles'));
    gulp.watch(cfg.src.images, gulp.series('images'));
    gulp.watch(cfg.copyFiles.map((copySpec) => copySpec.src), gulp.series('copyFiles'));
    gulp.watch(cfg.src.fonts, gulp.series('fonts'));
    gulp.watch([
      cfg.src.scripts,
      cfg.src.templates,
      cfg.src.unitTests,
    ], debounce(gulp.series('scripts', 'test'), 200));
    gulp.watch(cfg.src.fixtures.pattern, gulp.series('test'));
    gulp.watch(cfg.src.i18n, gulp.series('i18n'));

    gulp.watch(cfg.src.bowerLinks, gulp.series('build'));

    if (cfg.env.maven) {
      gulp.watch(cfg.src.indexHtml, gulp.series('bsInject'));
    } else {
      gulp.watch(cfg.src.indexHtml, gulp.series('dev'));
    }
  }

  gulp.task(bsInject);
  gulp.task(bsServerSync);
  gulp.task(bsServerSyncDist);
  gulp.task(build);
  gulp.task(buildDist);
  gulp.task(clean);
  gulp.task(copyFiles);
  gulp.task(dev);
  gulp.task(dist);
  gulp.task(fonts);
  gulp.task(i18n);
  gulp.task(images);
  gulp.task(scripts);
  gulp.task(serve);
  gulp.task(serveDist);
  gulp.task(styles);
  gulp.task(symlinkDependencies);
  gulp.task(test);
  gulp.task(testDebug);
  gulp.task(unlinkDependencies);
  gulp.task(watch);
}

module.exports.buildTasks = buildTasks;
module.exports.buildConfig = buildConfig;
<|endoftext|>


<commit_before>require('coffee-script/register')
const express = require('express')
const newrelic = require('artsy-newrelic')
const artsyXapp = require('artsy-xapp')
const artsyPassport = require('artsy-passport')
const mobile = require('./mobile')
const desktop = require('./desktop')
const mobileMiddleware = require('./desktop/lib/middleware/redirect_mobile.coffee')
const cache = require('./lib/cache')
const DesktopUser = require('./desktop/models/current_user.coffee')
const MobileUser = require('./mobile/models/current_user.coffee')
const glob = require('glob')

const app = express()
const { API_URL, CLIENT_ID, CLIENT_SECRET, PORT } = process.env

// Combine user models from desktop and mobile
const MergedUser = DesktopUser.extend(MobileUser)
artsyPassport.options.CurrentUser = MergedUser

// Middleware to direct to mobile or desktop apps
const isResponsive = (url) => {
  const stack = mobileMiddleware.stack.slice(0, -1)
  return stack.filter((route) => route.regexp.test(url)).length > 0
}

const determineDevice = (req, res, next) => {
  const ua = req.get('user-agent')
  const isPhone = Boolean(
    (ua.match(/iPhone/i) && !ua.match(/iPad/i)) ||
    (ua.match(/Android/i) && ua.match(/Mobile/i)) ||
    (ua.match(/Windows Phone/i)) ||
    (ua.match(/BB10/i)) ||
    (ua.match(/BlackBerry/i))
  )
  req.isMobile = isPhone && !isResponsive(req.url)
  next()
}

const routeApp = (req, res, next) => {
  req.isMobile ? mobile(req, res, next) : desktop(req, res, next)
}

const routeErr = (err, req, res, next) => {
  req.isMobile ? mobile(err, req, res, next) : desktop(err, req, res, next)
}

// Mount static assets first so responsive pages don't get confused
glob.sync('desktop/**/public/')
  .concat(glob.sync('mobile/**/public/'))
  .forEach((fld) => app.use(express.static(fld)))

// Mount root-level middleware
app.use(newrelic)
app.use(determineDevice)
app.use(routeApp)
app.use(routeErr)

// Attempt to connect to Redis. If it fails, no worries, the app will move on
// without caching.
cache.setup(() => {
  // Get an xapp token
  artsyXapp.init({ url: API_URL, id: CLIENT_ID, secret: CLIENT_SECRET }, () => {
    // Start server
    app.listen(PORT, () => {
      console.log(`Force listening on port ${PORT}`)
    })
  })
})
<commit_msg>clean up imports<commit_after>require('coffee-script/register')
const express = require('express')
const newrelic = require('artsy-newrelic')
const artsyXapp = require('artsy-xapp')
const artsyPassport = require('artsy-passport')
const glob = require('glob')
const desktop = require('./desktop')
const mobile = require('./mobile')
const mobileMiddleware = require('./desktop/lib/middleware/redirect_mobile.coffee')
const cache = require('./lib/cache')
const DesktopUser = require('./desktop/models/current_user.coffee')
const MobileUser = require('./mobile/models/current_user.coffee')

const app = express()
const { API_URL, CLIENT_ID, CLIENT_SECRET, PORT } = process.env

// Combine user models from desktop and mobile
const MergedUser = DesktopUser.extend(MobileUser)
artsyPassport.options.CurrentUser = MergedUser

// Middleware to direct to mobile or desktop apps
const isResponsive = (url) => {
  const stack = mobileMiddleware.stack.slice(0, -1)
  return stack.filter((route) => route.regexp.test(url)).length > 0
}

const determineDevice = (req, res, next) => {
  const ua = req.get('user-agent')
  const isPhone = Boolean(
    (ua.match(/iPhone/i) && !ua.match(/iPad/i)) ||
    (ua.match(/Android/i) && ua.match(/Mobile/i)) ||
    (ua.match(/Windows Phone/i)) ||
    (ua.match(/BB10/i)) ||
    (ua.match(/BlackBerry/i))
  )
  req.isMobile = isPhone && !isResponsive(req.url)
  next()
}

const routeApp = (req, res, next) => {
  req.isMobile ? mobile(req, res, next) : desktop(req, res, next)
}

const routeErr = (err, req, res, next) => {
  req.isMobile ? mobile(err, req, res, next) : desktop(err, req, res, next)
}

// Mount static assets first so responsive pages don't get confused
glob.sync('desktop/**/public/')
  .concat(glob.sync('mobile/**/public/'))
  .forEach((fld) => app.use(express.static(fld)))

// Mount root-level middleware
app.use(newrelic)
app.use(determineDevice)
app.use(routeApp)
app.use(routeErr)

// Attempt to connect to Redis. If it fails, no worries, the app will move on
// without caching.
cache.setup(() => {
  // Get an xapp token
  artsyXapp.init({ url: API_URL, id: CLIENT_ID, secret: CLIENT_SECRET }, () => {
    // Start server
    app.listen(PORT, () => {
      console.log(`Force listening on port ${PORT}`)
    })
  })
})
<|endoftext|>


<commit_before>/* eslint react/no-array-index-key: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, Dimensions, ViewPropTypes, FlatList } from 'react-native';
import { chunkArray } from './utils';
import SuperGridSectionList from './SuperGridSectionList'

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();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.itemDimension !== this.props.itemDimension) {
      this.setState({
        ...this.getDimensions(this.state.totalDimension, nextProps.itemDimension),
      });
    }
  }

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

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

  getDimensions(lvDimension, itemDim) {
    const { itemWidth, spacing, fixed, staticDimension, horizontal } = this.props;
    let itemDimension = itemDim || this.props.itemDimension;
    if (itemWidth) {
      itemDimension = itemWidth;
      console.warn('React Native Super Grid - property "itemWidth" is depreciated. Use "itemDimension" instead.');
    }

    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 {
      totalDimension,
      itemDimension,
      spacing,
      itemsPerRow,
      containerDimension,
      fixed,
    };
  }

  renderVerticalRow(data) {
    const { itemDimension, spacing, containerDimension, fixed, itemsPerRow } = this.state;
    const rowStyle = {
      flexDirection: 'column',
      paddingTop: spacing,
      paddingRight: spacing,
    };
    if (data.isLast) {
      rowStyle.marginRight = spacing;
    }
    const itemContainerStyle = {
      justifyContent: 'center',
      height: containerDimension,
      paddingBottom: spacing,
    };
    let itemStyle = { };
    if (fixed) {
      itemStyle = {
        height: itemDimension,
        justifyContent: 'center',
      };
      delete itemContainerStyle.paddingBottom;
    }

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

  renderHorizontalRow(data) {
    const { itemDimension, containerDimension, spacing, fixe, itemsPerRow } = this.state;
    const rowStyle = {
      flexDirection: 'row',
      paddingLeft: spacing,
      paddingBottom: spacing,
    };
    if (data.isLast) {
      rowStyle.marginBottom = spacing;
    }
    const itemContainerStyle = {
      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={`${data.key}_${i}`} style={itemContainerStyle}>
            <View style={itemStyle}>
              {this.props.renderItem(item, i + (data.rowNumber * itemsPerRow))}
            </View>
          </View>
        ))}
      </View>
    );
  }

  renderRow({ item }) { // item is array of items which go in one row
    const { horizontal } = this.props;
    if (horizontal) {
      return this.renderVerticalRow(item);
    }
    return this.renderHorizontalRow(item);
  }

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

    const chunked = chunkArray(items, itemsPerRow); //Splitting the data into rows

    //Adding metadata to these rows
    const rows = chunked.map((r, i) => {
      const keydRow = [...r];
      keydRow.key = `row_${i}`;
      keydRow.rowNumber = i; //Assigning a row number to each row to allow proper indexing later
      keydRow.isLast = (chunked.length - 1 === i);
      return keydRow;
    });

    return (
      <FlatList
        data={rows}
        renderItem={this.renderRow}
        style={[
          { ...horizontal ? { paddingLeft: spacing } : { paddingTop: spacing } },
          style,
        ]}
        onLayout={this.onLayout}
        {...props}
        horizontal={horizontal}
      />
    );
  }
}

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

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

export default SuperGrid;
export {SuperGridSectionList};
<commit_msg>Fixed a minor typo<commit_after>/* eslint react/no-array-index-key: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, Dimensions, ViewPropTypes, FlatList } from 'react-native';
import { chunkArray } from './utils';
import SuperGridSectionList from './SuperGridSectionList'

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();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.itemDimension !== this.props.itemDimension) {
      this.setState({
        ...this.getDimensions(this.state.totalDimension, nextProps.itemDimension),
      });
    }
  }

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

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

  getDimensions(lvDimension, itemDim) {
    const { itemWidth, spacing, fixed, staticDimension, horizontal } = this.props;
    let itemDimension = itemDim || this.props.itemDimension;
    if (itemWidth) {
      itemDimension = itemWidth;
      console.warn('React Native Super Grid - property "itemWidth" is depreciated. Use "itemDimension" instead.');
    }

    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 {
      totalDimension,
      itemDimension,
      spacing,
      itemsPerRow,
      containerDimension,
      fixed,
    };
  }

  renderVerticalRow(data) {
    const { itemDimension, spacing, containerDimension, fixed, itemsPerRow } = this.state;
    const rowStyle = {
      flexDirection: 'column',
      paddingTop: spacing,
      paddingRight: spacing,
    };
    if (data.isLast) {
      rowStyle.marginRight = spacing;
    }
    const itemContainerStyle = {
      justifyContent: 'center',
      height: containerDimension,
      paddingBottom: spacing,
    };
    let itemStyle = { };
    if (fixed) {
      itemStyle = {
        height: itemDimension,
        justifyContent: 'center',
      };
      delete itemContainerStyle.paddingBottom;
    }

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

  renderHorizontalRow(data) {
    const { itemDimension, containerDimension, spacing, fixed, itemsPerRow } = this.state;
    const rowStyle = {
      flexDirection: 'row',
      paddingLeft: spacing,
      paddingBottom: spacing,
    };
    if (data.isLast) {
      rowStyle.marginBottom = spacing;
    }
    const itemContainerStyle = {
      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={`${data.key}_${i}`} style={itemContainerStyle}>
            <View style={itemStyle}>
              {this.props.renderItem(item, i + (data.rowNumber * itemsPerRow))}
            </View>
          </View>
        ))}
      </View>
    );
  }

  renderRow({ item }) { // item is array of items which go in one row
    const { horizontal } = this.props;
    if (horizontal) {
      return this.renderVerticalRow(item);
    }
    return this.renderHorizontalRow(item);
  }

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

    const chunked = chunkArray(items, itemsPerRow); //Splitting the data into rows

    //Adding metadata to these rows
    const rows = chunked.map((r, i) => {
      const keydRow = [...r];
      keydRow.key = `row_${i}`;
      keydRow.rowNumber = i; //Assigning a row number to each row to allow proper indexing later
      keydRow.isLast = (chunked.length - 1 === i);
      return keydRow;
    });

    return (
      <FlatList
        data={rows}
        renderItem={this.renderRow}
        style={[
          { ...horizontal ? { paddingLeft: spacing } : { paddingTop: spacing } },
          style,
        ]}
        onLayout={this.onLayout}
        {...props}
        horizontal={horizontal}
      />
    );
  }
}

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

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

export default SuperGrid;
export {SuperGridSectionList};
<|endoftext|>