<commit_before>//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) {

    function trim(str, chr) {
        var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
        return str.replace(rgxtrim, '');
    }

    $scope.renderModel = [];

    var dialogOptions = {
        multiPicker: false,
        entityType: "Media",
        section: "media",
        treeAlias: "media",
        idType: "int"
    };

    //combine the dialogOptions with any values returned from the server
    if ($scope.model.config) {
        angular.extend(dialogOptions, $scope.model.config);
    }

    $scope.openContentPicker = function() {
      $scope.contentPickerOverlay = dialogOptions;
      $scope.contentPickerOverlay.view = "treePicker";
      $scope.contentPickerOverlay.show = true;

      $scope.contentPickerOverlay.submit = function(model) {

         if ($scope.contentPickerOverlay.multiPicker) {
             _.each(model.selection, function (item, i) {
                 $scope.add(item);
             });
         }
         else {
             $scope.clear();
             $scope.add(model.selection[0]);
         }

         $scope.contentPickerOverlay.show = false;
         $scope.contentPickerOverlay = null;
      };

      $scope.contentPickerOverlay.close = function(oldModel) {
         $scope.contentPickerOverlay.show = false;
         $scope.contentPickerOverlay = null;
      };
    }

    $scope.remove =function(index, event){
        event.preventDefault();
        $scope.renderModel.splice(index, 1);
    };

    $scope.clear = function() {
        $scope.renderModel = [];
    };

    $scope.add = function (item) {

        var itemId = dialogOptions.idType === "udi" ? item.udi : item.id;

        var currIds = _.map($scope.renderModel, function (i) {
            return dialogOptions.idType === "udi" ? i.udi : i.id;
        });
        if (currIds.indexOf(itemId) < 0) {
            item.icon = iconHelper.convertFromLegacyIcon(item.icon);
            $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, udi: item.udi });
        }
    };

    var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
        var currIds = _.map($scope.renderModel, function (i) {
            return dialogOptions.idType === "udi" ? i.udi : i.id;
        });
        $scope.model.value = trim(currIds.join(), ",");
    });

    //when the scope is destroyed we need to unsubscribe
    $scope.$on('$destroy', function () {
        unsubscribe();
    });

    //load media data
    var modelIds = $scope.model.value ? $scope.model.value.split(',') : [];
    entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) {
        _.each(data, function (item, i) {
            item.icon = iconHelper.convertFromLegacyIcon(item.icon);
            $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, udi: item.udi });
        });
    });

}

angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController);<commit_msg>updates mediapicker.controller pre value to not make an uneeded request<commit_after>//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) {

    function trim(str, chr) {
        var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
        return str.replace(rgxtrim, '');
    }

    $scope.renderModel = [];

    var dialogOptions = {
        multiPicker: false,
        entityType: "Media",
        section: "media",
        treeAlias: "media",
        idType: "int"
    };

    //combine the dialogOptions with any values returned from the server
    if ($scope.model.config) {
        angular.extend(dialogOptions, $scope.model.config);
    }

    $scope.openContentPicker = function() {
      $scope.contentPickerOverlay = dialogOptions;
      $scope.contentPickerOverlay.view = "treePicker";
      $scope.contentPickerOverlay.show = true;

      $scope.contentPickerOverlay.submit = function(model) {

         if ($scope.contentPickerOverlay.multiPicker) {
             _.each(model.selection, function (item, i) {
                 $scope.add(item);
             });
         }
         else {
             $scope.clear();
             $scope.add(model.selection[0]);
         }

         $scope.contentPickerOverlay.show = false;
         $scope.contentPickerOverlay = null;
      };

      $scope.contentPickerOverlay.close = function(oldModel) {
         $scope.contentPickerOverlay.show = false;
         $scope.contentPickerOverlay = null;
      };
    }

    $scope.remove =function(index, event){
        event.preventDefault();
        $scope.renderModel.splice(index, 1);
    };

    $scope.clear = function() {
        $scope.renderModel = [];
    };

    $scope.add = function (item) {

        var itemId = dialogOptions.idType === "udi" ? item.udi : item.id;

        var currIds = _.map($scope.renderModel, function (i) {
            return dialogOptions.idType === "udi" ? i.udi : i.id;
        });
        if (currIds.indexOf(itemId) < 0) {
            item.icon = iconHelper.convertFromLegacyIcon(item.icon);
            $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, udi: item.udi });
        }
    };

    var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
        var currIds = _.map($scope.renderModel, function (i) {
            return dialogOptions.idType === "udi" ? i.udi : i.id;
        });
        $scope.model.value = trim(currIds.join(), ",");
    });

    //when the scope is destroyed we need to unsubscribe
    $scope.$on('$destroy', function () {
        unsubscribe();
    });

    //load media data
    var modelIds = $scope.model.value ? $scope.model.value.split(',') : [];
    if (modelIds.length > 0) {
        entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) {
            _.each(data, function (item, i) {
                item.icon = iconHelper.convertFromLegacyIcon(item.icon);
                $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, udi: item.udi });
            });
        });
    }

}

angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController);<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/* global chrome, __DEBUG__ */

var _ = require('lodash');
var async = require('async');
var getIn = require('./getIn');

var serialDevice = require('../serialDevice');
var hidDevice = require('../hidDevice');
var driverManager = require('../driverManager');
var builder = require('../objectBuilder')();

var dexcomDriver = require('../drivers/dexcomDriver');
var oneTouchMiniDriver = require('../drivers/oneTouchMiniDriver');
var abbottPrecisionXtra = require('../drivers/abbottPrecisionXtra');
var tandemDriver = require('../drivers/tandemDriver');
var insuletOmniPod = require('../drivers/insuletDriver');
var oneTouchUltra2 = require('../drivers/oneTouchUltra2');
var oneTouchVerioIQ = require('../drivers/oneTouchVerioIQ');
var abbottFreeStyleLite = require('../drivers/abbottFreeStyleLite');
var bayerContourNext = require('../drivers/bayerContourNext');

var device = {
  log: require('../bows')('Device')
};

device._deviceDrivers = {
  'Dexcom': dexcomDriver,
  'OneTouchMini': oneTouchMiniDriver,
  'AbbottPrecisionXtra': abbottPrecisionXtra,
  'InsuletOmniPod': insuletOmniPod,
  'Tandem': tandemDriver,
  'OneTouchUltra2': oneTouchUltra2,
  'OneTouchVerioIQ': oneTouchVerioIQ,
  'AbbottFreeStyleLite': abbottFreeStyleLite,
  'AbbottFreeStyleFreedomLite': abbottFreeStyleLite,
  'BayerContourNext': bayerContourNext,
  'BayerContourNextUsb': bayerContourNext,
  'BayerContourUsb': bayerContourNext,
  'BayerContourNextLink': bayerContourNext
};

device._deviceComms = {
  'Dexcom': serialDevice,
  'OneTouchMini': serialDevice,
  'AbbottPrecisionXtra': serialDevice,
  'OneTouchUltra2': serialDevice,
  'OneTouchVerioIQ': serialDevice,
  'AbbottFreeStyleLite': serialDevice,
  'AbbottFreeStyleFreedomLite': serialDevice,
  'Tandem': serialDevice,
  'BayerContourNext': hidDevice,
  'BayerContourNextUsb': hidDevice,
  'BayerContourUsb': hidDevice,
  'BayerContourNextLink': hidDevice
};

device._silentComms = {};
_.forEach(_.keys(device._deviceComms), function(driverId) {

  var comm = device._deviceComms[driverId];
  device._silentComms[driverId] = comm({silent: true});
  device._deviceComms[driverId] = comm();
});

// this is a cache for device information
// we need it so that what we learn in detect()
// can be used by process().
device._deviceInfoCache = {};

device.init = function(options, cb) {
  var self=this;
  self._defaultTimezone = options.defaultTimezone;
  self._driverManifests = self._getAllDriverManifests();
  self._api = options.api;
  self._version = options.version;
  self._groupId = options.targetId;

  chrome.runtime.getPlatformInfo(function (platformInfo) {
    self._os = platformInfo.os;
    if (self._os == 'win') {
      self._portpattern = 'COM[0-9]+';
    } else {
      self._portpattern = '/dev/cu\\.usb.+';
    }
    cb();
  });

};

device._getAllDriverManifests = function() {
  var manifest = chrome.runtime.getManifest();

  var usbDevicesPermission = _.find(manifest.permissions, function(permission) {
    return permission.usbDevices;
  });
  return _.reduce(usbDevicesPermission.usbDevices, function(acc, usbDevice) {
    acc[usbDevice.driverId] = usbDevice;
    return acc;
  }, {});
};

device.getDriverManifests = function() {
  return _.cloneDeep(this._driverManifests);
};

device.getDriverIds = function() {
  return _.keys(this._driverManifests);
};

device.getDriverManifest = function(driverId) {
  var driverManifest = this._driverManifests[driverId];
  if (!driverManifest) {
    throw new Error('Could not find driver manifest for "' + driverId + '"');
  }
  return driverManifest;
};

device.detectHelper = function(driverId, options, cb) {
  // Detect can run on a loop, so don't pollute the console with logging
  options.silent = true;
  var dm = this._createDriverManager(driverId, options);
  dm.detect(driverId, cb);
};

device._createDriverManager = function(driverId, options) {
  var drivers = {};
  drivers[driverId] = this._deviceDrivers[driverId];
  var configs = {};
  configs[driverId] = this._createDriverConfig(driverId, options);
  configs.debug = __DEBUG__ || false;

  return driverManager(drivers, configs);
};

device._createDriverConfig = function(driverId, options) {
  options = options || {};
  var timezone = options.timezone || this._defaultTimezone;
  var comms = options.silent ? this._silentComms : this._deviceComms;
  var theVersion = options.version || this._version;
  var uploadGroup = options.targetId || this._groupId;

  // handle config for block-mode devices, which includes the file name and data
  if (options.filename != null) {
    return {
      filename: options.filename,
      filedata: options.filedata,
      deviceInfo: this._deviceInfoCache[driverId],
      timezone: timezone,
      groupId: uploadGroup,
      api: this._api,
      version: options.version,
      builder: builder,
      progress: options.progress,
      silent: Boolean(options.silent)
    };
  }

  return {
    deviceInfo: this._deviceInfoCache[driverId],
    deviceComms: comms[driverId],
    timezone: timezone,
    groupId: uploadGroup,
    api: this._api,
    version: options.version,
    builder: builder,
    progress: options.progress,
    silent: Boolean(options.silent)
  };
};

device.detectHid = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };
  chrome.hid.getDevices({vendorId:driverManifest.vendorId}, function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        deviceId: result.deviceId,
        vendorId: result.vendorId,
        productId: result.productId
      };

      if (self._os == 'win') {
        if (driverManifest.winPortPattern) {
          retval.portPattern = driverManifest.winPortPattern;
        }
      } else {
        if (driverManifest.macPortPattern) {
          retval.portPattern = driverManifest.macPortPattern;
        }
      }

      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }

    return cb(null, devdata);
  });
};

device.detectUsb = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };

  var getDevice = function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        vendorId: driverManifest.vendorId,
        productId: driverManifest.productId,
        portPattern: self._portpattern,
        usbDevice: result.device,
        path: result.path
      };
      if (self._os == 'win') {
        if (driverManifest.winPortPattern) {
          retval.portPattern = driverManifest.winPortPattern;
        }
      } else {
        if (driverManifest.macPortPattern) {
          retval.portPattern = driverManifest.macPortPattern;
        }
      }
      if (!!driverManifest.bitrate) {
        retval.bitrate = driverManifest.bitrate;
      }
      if (!!driverManifest.sendTimeout) {
        retval.sendTimeout = driverManifest.sendTimeout;
      }
      if(!!driverManifest.receiveTimeout) {
        retval.receiveTimeout = driverManifest.receiveTimeout;
      }
      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }
    return cb(null, devdata);
  };

  if (self._os === 'win') {
    var chromeVersion = /Chrome\/([0-9]+)/.exec(navigator.userAgent)[1];
    self.log('Chrome version detected as', chromeVersion);

    chrome.serial.getDevices(function(results) {
      if (chromeVersion >= 50) {
        // As of Chrome 50, product ID and vendor ID is returned
        // on Windows (only) for serial.getDevices
        results = _.filter(results, function(p) {
          return (p.vendorId === driverManifest.vendorId) &&
          (p.productId === driverManifest.productId);
        });
      }
      else{
        // With Chrome 49 and earlier, there may be
        // multiple connected devices
        self.log('Connected device(s): ');
        for(var i in results) {
            self.log(results[i].path);
        }
      }
      getDevice(results);
    });
  }
  else {
    chrome.usb.getDevices(identification, function(results) {
      // this is the way it *should* work, with filtering done by
      // usb.getDevices, but it is not working on Windows
      getDevice(results);
    });
  }
};

device.detect = function(driverId, options, cb) {
  var self = this;
  if (_.isFunction(options)) {
    cb = options;
    options = { version: self._version };
  }
  var driverManifest = this.getDriverManifest(driverId);

  if(driverManifest.mode === 'HID'){
      this.detectHid(driverId, function(err, devdata) {

        if (err) {
          return cb(err);
        }

        if (!devdata) {
          return cb();
        }

        self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
        self.detectHelper(driverId, options, function(err, ftdiDevice) {

          if (err) {
            return cb(err);
          }
          device = _.assign(devdata, ftdiDevice);
          return cb(null, devdata);
        });
      });

  }else{

    this.detectUsb(driverId, function(err, devdata) {

      if (err) {
        return cb(err);
      }

      if (!devdata) {
        return cb();
      }

      self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
      self.detectHelper(driverId, options, function(err, ftdiDevice) {
        if (err) {
          return cb(err);
        }
        device = _.assign(devdata, ftdiDevice);
        return cb(null, devdata);
      });
    });
  }
};

device.detectAll = function(cb) {
  async.map(this.getDriverIds(), this.detect.bind(this), function(err, results) {
    if (err) {
      return cb(err);
    }
    // Filter out any nulls
    results = _.filter(results);
    cb(null, results);
  });
};

device.upload = function(driverId, options, cb) {
  var dm = this._createDriverManager(driverId, options);
  dm.process(driverId, function(err, result) {
    if (err) {
      return cb(err);
    }
    return cb(null, result.post_records);
  });
};

module.exports = device;
<commit_msg>add link to Chromium bug report<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/* global chrome, __DEBUG__ */

var _ = require('lodash');
var async = require('async');
var getIn = require('./getIn');

var serialDevice = require('../serialDevice');
var hidDevice = require('../hidDevice');
var driverManager = require('../driverManager');
var builder = require('../objectBuilder')();

var dexcomDriver = require('../drivers/dexcomDriver');
var oneTouchMiniDriver = require('../drivers/oneTouchMiniDriver');
var abbottPrecisionXtra = require('../drivers/abbottPrecisionXtra');
var tandemDriver = require('../drivers/tandemDriver');
var insuletOmniPod = require('../drivers/insuletDriver');
var oneTouchUltra2 = require('../drivers/oneTouchUltra2');
var oneTouchVerioIQ = require('../drivers/oneTouchVerioIQ');
var abbottFreeStyleLite = require('../drivers/abbottFreeStyleLite');
var bayerContourNext = require('../drivers/bayerContourNext');

var device = {
  log: require('../bows')('Device')
};

device._deviceDrivers = {
  'Dexcom': dexcomDriver,
  'OneTouchMini': oneTouchMiniDriver,
  'AbbottPrecisionXtra': abbottPrecisionXtra,
  'InsuletOmniPod': insuletOmniPod,
  'Tandem': tandemDriver,
  'OneTouchUltra2': oneTouchUltra2,
  'OneTouchVerioIQ': oneTouchVerioIQ,
  'AbbottFreeStyleLite': abbottFreeStyleLite,
  'AbbottFreeStyleFreedomLite': abbottFreeStyleLite,
  'BayerContourNext': bayerContourNext,
  'BayerContourNextUsb': bayerContourNext,
  'BayerContourUsb': bayerContourNext,
  'BayerContourNextLink': bayerContourNext
};

device._deviceComms = {
  'Dexcom': serialDevice,
  'OneTouchMini': serialDevice,
  'AbbottPrecisionXtra': serialDevice,
  'OneTouchUltra2': serialDevice,
  'OneTouchVerioIQ': serialDevice,
  'AbbottFreeStyleLite': serialDevice,
  'AbbottFreeStyleFreedomLite': serialDevice,
  'Tandem': serialDevice,
  'BayerContourNext': hidDevice,
  'BayerContourNextUsb': hidDevice,
  'BayerContourUsb': hidDevice,
  'BayerContourNextLink': hidDevice
};

device._silentComms = {};
_.forEach(_.keys(device._deviceComms), function(driverId) {

  var comm = device._deviceComms[driverId];
  device._silentComms[driverId] = comm({silent: true});
  device._deviceComms[driverId] = comm();
});

// this is a cache for device information
// we need it so that what we learn in detect()
// can be used by process().
device._deviceInfoCache = {};

device.init = function(options, cb) {
  var self=this;
  self._defaultTimezone = options.defaultTimezone;
  self._driverManifests = self._getAllDriverManifests();
  self._api = options.api;
  self._version = options.version;
  self._groupId = options.targetId;

  chrome.runtime.getPlatformInfo(function (platformInfo) {
    self._os = platformInfo.os;
    if (self._os == 'win') {
      self._portpattern = 'COM[0-9]+';
    } else {
      self._portpattern = '/dev/cu\\.usb.+';
    }
    cb();
  });

};

device._getAllDriverManifests = function() {
  var manifest = chrome.runtime.getManifest();

  var usbDevicesPermission = _.find(manifest.permissions, function(permission) {
    return permission.usbDevices;
  });
  return _.reduce(usbDevicesPermission.usbDevices, function(acc, usbDevice) {
    acc[usbDevice.driverId] = usbDevice;
    return acc;
  }, {});
};

device.getDriverManifests = function() {
  return _.cloneDeep(this._driverManifests);
};

device.getDriverIds = function() {
  return _.keys(this._driverManifests);
};

device.getDriverManifest = function(driverId) {
  var driverManifest = this._driverManifests[driverId];
  if (!driverManifest) {
    throw new Error('Could not find driver manifest for "' + driverId + '"');
  }
  return driverManifest;
};

device.detectHelper = function(driverId, options, cb) {
  // Detect can run on a loop, so don't pollute the console with logging
  options.silent = true;
  var dm = this._createDriverManager(driverId, options);
  dm.detect(driverId, cb);
};

device._createDriverManager = function(driverId, options) {
  var drivers = {};
  drivers[driverId] = this._deviceDrivers[driverId];
  var configs = {};
  configs[driverId] = this._createDriverConfig(driverId, options);
  configs.debug = __DEBUG__ || false;

  return driverManager(drivers, configs);
};

device._createDriverConfig = function(driverId, options) {
  options = options || {};
  var timezone = options.timezone || this._defaultTimezone;
  var comms = options.silent ? this._silentComms : this._deviceComms;
  var theVersion = options.version || this._version;
  var uploadGroup = options.targetId || this._groupId;

  // handle config for block-mode devices, which includes the file name and data
  if (options.filename != null) {
    return {
      filename: options.filename,
      filedata: options.filedata,
      deviceInfo: this._deviceInfoCache[driverId],
      timezone: timezone,
      groupId: uploadGroup,
      api: this._api,
      version: options.version,
      builder: builder,
      progress: options.progress,
      silent: Boolean(options.silent)
    };
  }

  return {
    deviceInfo: this._deviceInfoCache[driverId],
    deviceComms: comms[driverId],
    timezone: timezone,
    groupId: uploadGroup,
    api: this._api,
    version: options.version,
    builder: builder,
    progress: options.progress,
    silent: Boolean(options.silent)
  };
};

device.detectHid = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };
  chrome.hid.getDevices({vendorId:driverManifest.vendorId}, function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        deviceId: result.deviceId,
        vendorId: result.vendorId,
        productId: result.productId
      };

      if (self._os == 'win') {
        if (driverManifest.winPortPattern) {
          retval.portPattern = driverManifest.winPortPattern;
        }
      } else {
        if (driverManifest.macPortPattern) {
          retval.portPattern = driverManifest.macPortPattern;
        }
      }

      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }

    return cb(null, devdata);
  });
};

device.detectUsb = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };

  var getDevice = function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        vendorId: driverManifest.vendorId,
        productId: driverManifest.productId,
        portPattern: self._portpattern,
        usbDevice: result.device,
        path: result.path
      };
      if (self._os == 'win') {
        if (driverManifest.winPortPattern) {
          retval.portPattern = driverManifest.winPortPattern;
        }
      } else {
        if (driverManifest.macPortPattern) {
          retval.portPattern = driverManifest.macPortPattern;
        }
      }
      if (!!driverManifest.bitrate) {
        retval.bitrate = driverManifest.bitrate;
      }
      if (!!driverManifest.sendTimeout) {
        retval.sendTimeout = driverManifest.sendTimeout;
      }
      if(!!driverManifest.receiveTimeout) {
        retval.receiveTimeout = driverManifest.receiveTimeout;
      }
      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }
    return cb(null, devdata);
  };

  if (self._os === 'win') {
    var chromeVersion = /Chrome\/([0-9]+)/.exec(navigator.userAgent)[1];
    self.log('Chrome version detected as', chromeVersion);

    chrome.serial.getDevices(function(results) {
      if (chromeVersion >= 50) {
        // As of Chrome 50, product ID and vendor ID is returned
        // on Windows (only) for serial.getDevices
        // Chromium bug report: https://bugs.chromium.org/p/chromium/issues/detail?id=599101
        results = _.filter(results, function(p) {
          return (p.vendorId === driverManifest.vendorId) &&
          (p.productId === driverManifest.productId);
        });
      }
      else{
        // With Chrome 49 and earlier, there may be
        // multiple connected devices
        self.log('Connected device(s): ');
        for(var i in results) {
            self.log(results[i].path);
        }
      }
      getDevice(results);
    });
  }
  else {
    chrome.usb.getDevices(identification, function(results) {
      // this is the way it *should* work, with filtering done by
      // usb.getDevices, but it is not working on Windows
      getDevice(results);
    });
  }
};

device.detect = function(driverId, options, cb) {
  var self = this;
  if (_.isFunction(options)) {
    cb = options;
    options = { version: self._version };
  }
  var driverManifest = this.getDriverManifest(driverId);

  if(driverManifest.mode === 'HID'){
      this.detectHid(driverId, function(err, devdata) {

        if (err) {
          return cb(err);
        }

        if (!devdata) {
          return cb();
        }

        self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
        self.detectHelper(driverId, options, function(err, ftdiDevice) {

          if (err) {
            return cb(err);
          }
          device = _.assign(devdata, ftdiDevice);
          return cb(null, devdata);
        });
      });

  }else{

    this.detectUsb(driverId, function(err, devdata) {

      if (err) {
        return cb(err);
      }

      if (!devdata) {
        return cb();
      }

      self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
      self.detectHelper(driverId, options, function(err, ftdiDevice) {
        if (err) {
          return cb(err);
        }
        device = _.assign(devdata, ftdiDevice);
        return cb(null, devdata);
      });
    });
  }
};

device.detectAll = function(cb) {
  async.map(this.getDriverIds(), this.detect.bind(this), function(err, results) {
    if (err) {
      return cb(err);
    }
    // Filter out any nulls
    results = _.filter(results);
    cb(null, results);
  });
};

device.upload = function(driverId, options, cb) {
  var dm = this._createDriverManager(driverId, options);
  dm.process(driverId, function(err, result) {
    if (err) {
      return cb(err);
    }
    return cb(null, result.post_records);
  });
};

module.exports = device;
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/* global  __DEBUG__ */

var os = require('os');

var _ = require('lodash');
var async = require('async');

var serialDevice = require('../serialDevice');
var hidDevice = require('../hidDevice');
var driverManager = require('../driverManager');
var builder = require('../objectBuilder')();
var hid = require('node-hid');
var SerialPort = require('serialport');

var dexcomDriver = require('../drivers/dexcom/dexcomDriver');
var oneTouchMiniDriver = require('../drivers/onetouch/oneTouchMiniDriver');
var abbottPrecisionXtra = require('../drivers/abbott/abbottPrecisionXtra');
var tandemDriver = require('../drivers/tandem/tandemDriver');
var insuletOmniPod = require('../drivers/insulet/insuletDriver');
var oneTouchUltra2 = require('../drivers/onetouch/oneTouchUltra2');
var oneTouchVerioIQ = require('../drivers/onetouch/oneTouchVerioIQ');
var abbottFreeStyleLite = require('../drivers/abbott/abbottFreeStyleLite');
var bayerContourNext = require('../drivers/bayer/bayerContourNext');
var animasDriver = require('../drivers/animas/animasDriver');
var medtronicDriver = require('../drivers/medtronic/MedtronicDriver');

var device = {
  log: require('bows')('Device')
};

var hostMap = {
  'darwin': 'mac',
  'win32' : 'win',
  'linux': 'linux',
};

device._deviceDrivers = {
  'Dexcom': dexcomDriver,
  'OneTouchMini': oneTouchMiniDriver,
  'AbbottPrecisionXtra': abbottPrecisionXtra,
  'InsuletOmniPod': insuletOmniPod,
  'Tandem': tandemDriver,
  'OneTouchUltra2': oneTouchUltra2,
  'OneTouchVerioIQ': oneTouchVerioIQ,
  'AbbottFreeStyleLite': abbottFreeStyleLite,
  'AbbottFreeStyleFreedomLite': abbottFreeStyleLite,
  'BayerContourNext': bayerContourNext,
  'BayerContourNextUsb': bayerContourNext,
  'BayerContourUsb': bayerContourNext,
  'BayerContourNextLink': bayerContourNext,
  'Animas': animasDriver,
  'Medtronic': medtronicDriver
};

device._deviceComms = {
  'Dexcom': serialDevice,
  'OneTouchMini': serialDevice,
  'AbbottPrecisionXtra': serialDevice,
  'OneTouchUltra2': serialDevice,
  'OneTouchVerioIQ': serialDevice,
  'AbbottFreeStyleLite': serialDevice,
  'AbbottFreeStyleFreedomLite': serialDevice,
  'Tandem': serialDevice,
  'BayerContourNext': hidDevice,
  'BayerContourNextUsb': hidDevice,
  'BayerContourUsb': hidDevice,
  'BayerContourNextLink': hidDevice,
  'Animas': serialDevice,
  'Medtronic': hidDevice
};

device._silentComms = {};
_.forEach(_.keys(device._deviceComms), function(driverId) {

  var comm = device._deviceComms[driverId];
  device._silentComms[driverId] = comm({silent: true});
  device._deviceComms[driverId] = comm();
});

// this is a cache for device information
// we need it so that what we learn in detect()
// can be used by process().
device._deviceInfoCache = {};

device.init = function(options, cb) {
  var self=this;
  self._defaultTimezone = options.defaultTimezone;
  self._driverManifests = self._getAllDriverManifests();
  self._api = options.api;
  self._version = options.version;
  self._groupId = options.targetId;
  self._os = hostMap[os.platform()];
  cb();
};

device._getAllDriverManifests = function() {
  var manifest = require('../../manifest.json');

  var usbDevicesPermission = _.find(manifest.permissions, function(permission) {
    return permission.usbDevices;
  });
  return _.reduce(usbDevicesPermission.usbDevices, function(acc, usbDevice) {
    acc[usbDevice.driverId] = usbDevice;
    return acc;
  }, {});
};

device.getDriverManifests = function() {
  return _.cloneDeep(this._driverManifests);
};

device.getDriverIds = function() {
  return _.keys(this._driverManifests);
};

device.getDriverManifest = function(driverId) {
  var driverManifest = this._driverManifests[driverId];
  if (!driverManifest) {
    throw new Error('Could not find driver manifest for "' + driverId + '"');
  }
  return driverManifest;
};

device.detectHelper = function(driverId, options, cb) {
  // Detect can run on a loop, so don't pollute the console with logging
  options.silent = true;
  var dm = this._createDriverManager(driverId, options);
  dm.detect(driverId, cb);
};

device._createDriverManager = function(driverId, options) {
  var drivers = {};
  drivers[driverId] = this._deviceDrivers[driverId];
  var configs = {};
  configs[driverId] = this._createDriverConfig(driverId, options);
  configs.debug = __DEBUG__ || false;

  return driverManager(drivers, configs);
};

device._createDriverConfig = function(driverId, options) {
  options = options || {};
  var timezone = options.timezone || this._defaultTimezone;
  var comms = options.silent ? this._silentComms : this._deviceComms;
  var theVersion = options.version || this._version;
  var uploadGroup = options.targetId || this._groupId;

  // handle config for block-mode devices, which includes the file name and data
  if (options.filename != null) {
    return {
      filename: options.filename,
      filedata: options.filedata,
      deviceInfo: this._deviceInfoCache[driverId],
      timezone: timezone,
      groupId: uploadGroup,
      api: this._api,
      version: options.version,
      builder: builder,
      progress: options.progress,
      silent: Boolean(options.silent)
    };
  }

  var deviceInfo = this._deviceInfoCache[driverId];

  if(options.serialNumber) {
    _.assign(deviceInfo, {serialNumber: options.serialNumber});
  }

  return {
    deviceInfo: deviceInfo,
    deviceComms: comms[driverId],
    timezone: timezone,
    groupId: uploadGroup,
    api: this._api,
    version: options.version,
    builder: builder,
    progress: options.progress,
    silent: Boolean(options.silent)
  };
};

device.detectHid = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var results = hid.devices();
  results = _.filter(results, function(p) {
    return (p.vendorId === driverManifest.vendorId);
  });

  var devices = _.map(results, function(result) {
    var retval = {
      driverId: driverId,
      deviceId: result.deviceId,
      vendorId: result.vendorId,
      productId: result.productId
    };

    return retval;
  });

  var devdata = _.first(devices);

  if (devices.length > 1) {
    self.log('WARNING: More than one device found for "' + driverId + '"');
    device.othersConnected = devices.length - 1;
  }

  return cb(null, devdata);
};

device.detectUsb = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };

  var getDevice = function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        vendorId: driverManifest.vendorId,
        productId: driverManifest.productId,
        usbDevice: result.device,
        path: result.comName
      };
      if (!!driverManifest.bitrate) {
        retval.bitrate = driverManifest.bitrate;
      }
      if(!!driverManifest.ctsFlowControl) {
        retval.ctsFlowControl = driverManifest.ctsFlowControl;
      }
      if(!!driverManifest.sendTimeout){
        retval.sendTimeout = driverManifest.sendTimeout;
      }
      if(!!driverManifest.receiveTimeout) {
        retval.receiveTimeout = driverManifest.receiveTimeout;
      }
      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }
    return cb(null, devdata);
  };

  SerialPort.list(function (err, results) {
    console.log('Connected device(s):', results);
    results = _.filter(results, function(p) {
      var vendorId = parseInt(p.vendorId,16);
      var productId = parseInt(p.productId,16);
      if (self._os === 'win') {
        return (vendorId === driverManifest.vendorId) &&
        (productId === driverManifest.productId);
      } else {
        return (vendorId === driverManifest.vendorId) &&
        (productId === driverManifest.productId) &&
        p.comName.match('/dev/cu.+');
      }
    });
    getDevice(results);
  });
};

device.detect = function(driverId, options, cb) {
  var self = this;
  if (_.isFunction(options)) {
    cb = options;
    options = { version: self._version };
  }
  var driverManifest = this.getDriverManifest(driverId);

  if(driverManifest.mode === 'HID'){
      this.detectHid(driverId, function(err, devdata) {

        if (err) {
          return cb(err);
        }

        if (!devdata) {
          return cb();
        }

        self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
        self.detectHelper(driverId, options, function(err, ftdiDevice) {

          if (err) {
            return cb(err);
          }
          device = _.assign(devdata, ftdiDevice);
          return cb(null, devdata);
        });
      });

  }else{

    this.detectUsb(driverId, function(err, devdata) {

      if (err) {
        return cb(err);
      }

      if (!devdata) {
        return cb();
      }

      self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
      self.detectHelper(driverId, options, function(err, ftdiDevice) {
        if (err) {
          return cb(err);
        }
        device = _.assign(devdata, ftdiDevice);
        return cb(null, devdata);
      });
    });
  }
};

device.detectAll = function(cb) {
  async.map(this.getDriverIds(), this.detect.bind(this), function(err, results) {
    if (err) {
      return cb(err);
    }
    // Filter out any nulls
    results = _.filter(results);
    cb(null, results);
  });
};

device.upload = function(driverId, options, cb) {
  var dm = this._createDriverManager(driverId, options);
  dm.process(driverId, function(err, result) {
    return cb(err, result);
  });
};

module.exports = device;
<commit_msg>fix first char case of medtronic driver name<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/* global  __DEBUG__ */

var os = require('os');

var _ = require('lodash');
var async = require('async');

var serialDevice = require('../serialDevice');
var hidDevice = require('../hidDevice');
var driverManager = require('../driverManager');
var builder = require('../objectBuilder')();
var hid = require('node-hid');
var SerialPort = require('serialport');

var dexcomDriver = require('../drivers/dexcom/dexcomDriver');
var oneTouchMiniDriver = require('../drivers/onetouch/oneTouchMiniDriver');
var abbottPrecisionXtra = require('../drivers/abbott/abbottPrecisionXtra');
var tandemDriver = require('../drivers/tandem/tandemDriver');
var insuletOmniPod = require('../drivers/insulet/insuletDriver');
var oneTouchUltra2 = require('../drivers/onetouch/oneTouchUltra2');
var oneTouchVerioIQ = require('../drivers/onetouch/oneTouchVerioIQ');
var abbottFreeStyleLite = require('../drivers/abbott/abbottFreeStyleLite');
var bayerContourNext = require('../drivers/bayer/bayerContourNext');
var animasDriver = require('../drivers/animas/animasDriver');
var medtronicDriver = require('../drivers/medtronic/medtronicDriver');

var device = {
  log: require('bows')('Device')
};

var hostMap = {
  'darwin': 'mac',
  'win32' : 'win',
  'linux': 'linux',
};

device._deviceDrivers = {
  'Dexcom': dexcomDriver,
  'OneTouchMini': oneTouchMiniDriver,
  'AbbottPrecisionXtra': abbottPrecisionXtra,
  'InsuletOmniPod': insuletOmniPod,
  'Tandem': tandemDriver,
  'OneTouchUltra2': oneTouchUltra2,
  'OneTouchVerioIQ': oneTouchVerioIQ,
  'AbbottFreeStyleLite': abbottFreeStyleLite,
  'AbbottFreeStyleFreedomLite': abbottFreeStyleLite,
  'BayerContourNext': bayerContourNext,
  'BayerContourNextUsb': bayerContourNext,
  'BayerContourUsb': bayerContourNext,
  'BayerContourNextLink': bayerContourNext,
  'Animas': animasDriver,
  'Medtronic': medtronicDriver
};

device._deviceComms = {
  'Dexcom': serialDevice,
  'OneTouchMini': serialDevice,
  'AbbottPrecisionXtra': serialDevice,
  'OneTouchUltra2': serialDevice,
  'OneTouchVerioIQ': serialDevice,
  'AbbottFreeStyleLite': serialDevice,
  'AbbottFreeStyleFreedomLite': serialDevice,
  'Tandem': serialDevice,
  'BayerContourNext': hidDevice,
  'BayerContourNextUsb': hidDevice,
  'BayerContourUsb': hidDevice,
  'BayerContourNextLink': hidDevice,
  'Animas': serialDevice,
  'Medtronic': hidDevice
};

device._silentComms = {};
_.forEach(_.keys(device._deviceComms), function(driverId) {

  var comm = device._deviceComms[driverId];
  device._silentComms[driverId] = comm({silent: true});
  device._deviceComms[driverId] = comm();
});

// this is a cache for device information
// we need it so that what we learn in detect()
// can be used by process().
device._deviceInfoCache = {};

device.init = function(options, cb) {
  var self=this;
  self._defaultTimezone = options.defaultTimezone;
  self._driverManifests = self._getAllDriverManifests();
  self._api = options.api;
  self._version = options.version;
  self._groupId = options.targetId;
  self._os = hostMap[os.platform()];
  cb();
};

device._getAllDriverManifests = function() {
  var manifest = require('../../manifest.json');

  var usbDevicesPermission = _.find(manifest.permissions, function(permission) {
    return permission.usbDevices;
  });
  return _.reduce(usbDevicesPermission.usbDevices, function(acc, usbDevice) {
    acc[usbDevice.driverId] = usbDevice;
    return acc;
  }, {});
};

device.getDriverManifests = function() {
  return _.cloneDeep(this._driverManifests);
};

device.getDriverIds = function() {
  return _.keys(this._driverManifests);
};

device.getDriverManifest = function(driverId) {
  var driverManifest = this._driverManifests[driverId];
  if (!driverManifest) {
    throw new Error('Could not find driver manifest for "' + driverId + '"');
  }
  return driverManifest;
};

device.detectHelper = function(driverId, options, cb) {
  // Detect can run on a loop, so don't pollute the console with logging
  options.silent = true;
  var dm = this._createDriverManager(driverId, options);
  dm.detect(driverId, cb);
};

device._createDriverManager = function(driverId, options) {
  var drivers = {};
  drivers[driverId] = this._deviceDrivers[driverId];
  var configs = {};
  configs[driverId] = this._createDriverConfig(driverId, options);
  configs.debug = __DEBUG__ || false;

  return driverManager(drivers, configs);
};

device._createDriverConfig = function(driverId, options) {
  options = options || {};
  var timezone = options.timezone || this._defaultTimezone;
  var comms = options.silent ? this._silentComms : this._deviceComms;
  var theVersion = options.version || this._version;
  var uploadGroup = options.targetId || this._groupId;

  // handle config for block-mode devices, which includes the file name and data
  if (options.filename != null) {
    return {
      filename: options.filename,
      filedata: options.filedata,
      deviceInfo: this._deviceInfoCache[driverId],
      timezone: timezone,
      groupId: uploadGroup,
      api: this._api,
      version: options.version,
      builder: builder,
      progress: options.progress,
      silent: Boolean(options.silent)
    };
  }

  var deviceInfo = this._deviceInfoCache[driverId];

  if(options.serialNumber) {
    _.assign(deviceInfo, {serialNumber: options.serialNumber});
  }

  return {
    deviceInfo: deviceInfo,
    deviceComms: comms[driverId],
    timezone: timezone,
    groupId: uploadGroup,
    api: this._api,
    version: options.version,
    builder: builder,
    progress: options.progress,
    silent: Boolean(options.silent)
  };
};

device.detectHid = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var results = hid.devices();
  results = _.filter(results, function(p) {
    return (p.vendorId === driverManifest.vendorId);
  });

  var devices = _.map(results, function(result) {
    var retval = {
      driverId: driverId,
      deviceId: result.deviceId,
      vendorId: result.vendorId,
      productId: result.productId
    };

    return retval;
  });

  var devdata = _.first(devices);

  if (devices.length > 1) {
    self.log('WARNING: More than one device found for "' + driverId + '"');
    device.othersConnected = devices.length - 1;
  }

  return cb(null, devdata);
};

device.detectUsb = function(driverId, cb) {
  var self = this;
  var driverManifest = this.getDriverManifest(driverId);
  var identification = {
    vendorId: driverManifest.vendorId,
    productId: driverManifest.productId
  };

  var getDevice = function(results) {
    var devices = _.map(results, function(result) {
      var retval = {
        driverId: driverId,
        vendorId: driverManifest.vendorId,
        productId: driverManifest.productId,
        usbDevice: result.device,
        path: result.comName
      };
      if (!!driverManifest.bitrate) {
        retval.bitrate = driverManifest.bitrate;
      }
      if(!!driverManifest.ctsFlowControl) {
        retval.ctsFlowControl = driverManifest.ctsFlowControl;
      }
      if(!!driverManifest.sendTimeout){
        retval.sendTimeout = driverManifest.sendTimeout;
      }
      if(!!driverManifest.receiveTimeout) {
        retval.receiveTimeout = driverManifest.receiveTimeout;
      }
      return retval;
    });

    var devdata = _.first(devices);

    if (devices.length > 1) {
      self.log('WARNING: More than one device found for "' + driverId + '"');
      device.othersConnected = devices.length - 1;
    }
    return cb(null, devdata);
  };

  SerialPort.list(function (err, results) {
    console.log('Connected device(s):', results);
    results = _.filter(results, function(p) {
      var vendorId = parseInt(p.vendorId,16);
      var productId = parseInt(p.productId,16);
      if (self._os === 'win') {
        return (vendorId === driverManifest.vendorId) &&
        (productId === driverManifest.productId);
      } else {
        return (vendorId === driverManifest.vendorId) &&
        (productId === driverManifest.productId) &&
        p.comName.match('/dev/cu.+');
      }
    });
    getDevice(results);
  });
};

device.detect = function(driverId, options, cb) {
  var self = this;
  if (_.isFunction(options)) {
    cb = options;
    options = { version: self._version };
  }
  var driverManifest = this.getDriverManifest(driverId);

  if(driverManifest.mode === 'HID'){
      this.detectHid(driverId, function(err, devdata) {

        if (err) {
          return cb(err);
        }

        if (!devdata) {
          return cb();
        }

        self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
        self.detectHelper(driverId, options, function(err, ftdiDevice) {

          if (err) {
            return cb(err);
          }
          device = _.assign(devdata, ftdiDevice);
          return cb(null, devdata);
        });
      });

  }else{

    this.detectUsb(driverId, function(err, devdata) {

      if (err) {
        return cb(err);
      }

      if (!devdata) {
        return cb();
      }

      self._deviceInfoCache[driverId] = _.cloneDeep(devdata);
      self.detectHelper(driverId, options, function(err, ftdiDevice) {
        if (err) {
          return cb(err);
        }
        device = _.assign(devdata, ftdiDevice);
        return cb(null, devdata);
      });
    });
  }
};

device.detectAll = function(cb) {
  async.map(this.getDriverIds(), this.detect.bind(this), function(err, results) {
    if (err) {
      return cb(err);
    }
    // Filter out any nulls
    results = _.filter(results);
    cb(null, results);
  });
};

device.upload = function(driverId, options, cb) {
  var dm = this._createDriverManager(driverId, options);
  dm.process(driverId, function(err, result) {
    return cb(err, result);
  });
};

module.exports = device;
<|endoftext|>


<commit_before>var co = require('co')
	, thunkify = require('thunkify')
	, fs = require('fs')
	, path = require('path')
	, inherit = require('util').inherits
	, lodash = require('lodash')
	, extend = lodash.extend
	, flatten = lodash.flatten
	, unique = lodash.unique
	, difference = lodash.difference
	, compact = lodash.compact
	, fileFactory = require('./file')
	, clearIDCache = require('identify-resource').clearCache
	, cnsl = require('../utils/cnsl')
	, error = cnsl.error
	, debug = cnsl.debug
	, strong = cnsl.strong
	, colour = cnsl.colour
	, print = cnsl.print
	, warn = cnsl.warn
	, fsutils = require('recur-fs')
	// readdir thunk
	, readdir = function (dir, filter) {
			return function (fn) {
				fsutils.readdir(dir, filter, null, function (err, filepaths, dirpaths) {
					if (err) return fn(err);
					fn(null, filepaths);
				});
			}
		};

/**
 * Target instance factory
 * @param {Object} data
 * @returns {Target}
 */
module.exports = function (data) {
	var instance = new Target(data);
	return instance;
};

/**
 * Constructor
 * @param {Object} options
 */
function Target (options) {
/* Decorated properties
	this.type = '';
	this.sources = null;
	this.runtimeOptions = null;
	this.fileCache = null;
	this.fileExtensions = null;
	this.input = '';
	this.output = '';
	this.inputPath = '';
	this.outputPath = '';
	this.workflow = null;
	this.isDir = false;
	this.modular = false;
	this.targets = null;
	this.parent = null;
	this.hasChildren = false;
	this.bootstrap = false;
	this.boilerplate = false;
*/
	extend(this, options);
	this.fileFactoryOptions = {
		type: this.type,
		fileExtensions: this.fileExtensions,
		sources: this.sources,
		runtimeOptions: this.runtimeOptions
	};
	this.fileOptions = {
		filepath: this.outputPath,
	};
	this.fileFilter = this.fileExtensions ? new RegExp(this.fileExtensions.join('$|') + '$') : null;
	this.referencedFiles = [];

	debug("created "
		+ this.type
		+ " Target instance with input: "
		+ (strong(this.input))
		+ " and output: "
		+ (strong(this.output)), 2);
}

/**
 * Run build
 * @returns {Array}
 */
Target.prototype.build = function * () {
	this.referencedFiles = [];

	cnsl.start(this.input);

	print("building "
		+ (strong(path.basename(this.inputPath)))
		+ " to "
		+ (strong(path.basename(this.outputPath))), 2);

	// Execute 'before' hook
	if (this.before) yield this.executeHook('before', this);
	// Parse and start build process
	var files = yield this.parse(this.isDir, this.inputPath, this.fileFilter, this.fileFactoryOptions);
	// Process files
	files = yield this.process(files, this.workflow);
	// Execute 'afterEach' hooks
	if (this.afterEach) {
		yield files.map(function (file) {
			return this.executeHook('afterEach', file);
		}, this);
	}

	print("[processed "
		+ strong(this.referencedFiles.length)
		+ (this.referencedFiles.length > 1 ? " files" : " file")
		+ " in "
		+ colour((cnsl.stop(this.input) / 1000) + 's', cnsl.CYAN)
		+ "]", 3);

	// Write writeable files
	var filepaths = yield this.write(files);
	if (this.hasChildren) {
		// Lock files to prevent inclusion in downstream targets
		this.lock(this.referencedFiles);
		// Build child targets in sequence
		for (var i = 0, n = this.targets.length; i < n; i++) {
			filepaths = filepaths.concat(yield this.targets[i].build());
		}
		this.unlock(this.referencedFiles);
	}
	// Execute 'after' hook
	if (this.after) yield this.executeHook('after', this);

	// Reset
	this.reset();

	// Return output paths on completion
	return filepaths;
};

/**
 * Parse 'input' source files
 * @param {Boolean} isDir
 * @param {String} input
 * @param {RegExp} filter
 * @param {Object} options
 * @returns {Array}
 */
Target.prototype.parse = function * (isDir, input, filter, options) {
	var files;

	// Input is directory
	if (isDir) {
		// Grab all files (filtered by file extension)
		var filepaths = yield readdir(input, filter);
		// Create file instances
		files = filepaths.map(function (filepath) {
			return fileFactory(filepath, options);
		}).filter(function (file) {
			return file != null;
		});
		if (!files.length) warn('no valid source files found in ' + strong(input), 4);

	// Input is file
	} else {
		// Create File instance
		var file = fileFactory(input, options);
		file.isRoot = true;
		if (!file) {
			warn(strong(input) + ' not found in project sources', 4);
			files = [];
		} else {
			files = [file];
		}
	}

	return files;
};

/**
 * Process batch workflow 'commands' for 'files'
 * @param {Array} files
 * @param {Array} workflow
 * @returns {Array}
 */
Target.prototype.process = function * (files, workflow) {
	// Recursive batch run 'fls' with 'tasks'
	function * batch (fls, tasks) {
		// Run file tasks in parallel
		var deps = yield fls.map(function (fl) {
			return fl.run(tasks);
		});
		// Process files not yet processed
		deps = difference(unique(flatten(compact(deps))), files);
		if (deps.length) {
			// Store
			files = files.concat(deps);
			yield batch(deps, tasks);
		}
	}

	yield batch(files, workflow[0]);

	// Store all referenced files
	this.referencedFiles = files;

	// Second set is filtered on writeability
	if (workflow.length > 1) {
		// Filter writeable files
		files = files.filter(function (file) {
			return file.getIsWriteable();
		});
		yield batch(files, workflow[1]);
	}

	return files;
};

/**
 * Write generated content
 * @param {Array} files
 * @returns {Array}
 */
Target.prototype.write = function * (files) {
	var options = {
				bootstrap: this.bootstrap,
				boilerplate: this.boilerplate
			}
		, filepaths = []
		, file, filepath;

	// Write files in sequence
	for (var i = 0, n = files.length; i < n; i++) {
		file = files[i];
		filepath = this.outputPath;
		// Resolve output path if directory
		if (!path.extname(filepath).length) filepath = path.join(filepath, file.id) + '.' + file.type;
		// Store
		filepaths.push(filepath);

		// Write file
		yield file.write(filepath, options);
	}

	return filepaths;
};

/**
 * Set lock flag for 'files'
 * @param {Array} files
 */
Target.prototype.lock = function (files) {
	files.forEach(function (file) {
		file.isLocked = true;
	});
};

/**
 * Unset lock flag for 'files'
 * @param {Array} files
 */
Target.prototype.unlock = function (files) {
	files.forEach(function (file) {
		file.isLocked = false;
	});
};

/**
 * Determine if 'file' is a referenced file (child targets included)
 * @param {File} file
 * @returns {Boolean}
 */
Target.prototype.hasFile = function (file) {
	var referenced;

	referenced = ~this.referencedFiles.indexOf(file);
	if (referenced) {
		return true;
	} else if (this.hasChildren) {
		for (var i = 0, n = this.targets.length; i < n; i++) {
			referenced = this.targets[i].hasFile(file);
			if (referenced) return true;
		}
	}
	return false;
};

/**
 * Reset modified files
 */
Target.prototype.reset = function () {
	this.referencedFiles.forEach(function (file) {
		file.reset();
	});
	clearIDCache();
};

/**
 * Execute the 'hook' function with a particular 'context'
 * @param {String} hook
 * @param {Object} context
 */
Target.prototype.executeHook = function (hook, context) {
	var hook = this[hook];
	print('executing ' + hook + ' hook...', 3);

	// Return thunk
	return function (done) {
		// Make global objects available to the function
		hook(global, process, console, require, context, this.runtimeOptions, done);
	}
};
<commit_msg>add clearing of template cache on reset()<commit_after>var co = require('co')
	, thunkify = require('thunkify')
	, fs = require('fs')
	, path = require('path')
	, inherit = require('util').inherits
	, lodash = require('lodash')
	, extend = lodash.extend
	, flatten = lodash.flatten
	, unique = lodash.unique
	, difference = lodash.difference
	, compact = lodash.compact
	, fileFactory = require('./file')
	, clearIDCache = require('identify-resource').clearCache
	, clearTemplateCache = require('transfigure').clearCache
	, cnsl = require('../utils/cnsl')
	, error = cnsl.error
	, debug = cnsl.debug
	, strong = cnsl.strong
	, colour = cnsl.colour
	, print = cnsl.print
	, warn = cnsl.warn
	, fsutils = require('recur-fs')
	// readdir thunk
	, readdir = function (dir, filter) {
			return function (fn) {
				fsutils.readdir(dir, filter, null, function (err, filepaths, dirpaths) {
					if (err) return fn(err);
					fn(null, filepaths);
				});
			}
		};

/**
 * Target instance factory
 * @param {Object} data
 * @returns {Target}
 */
module.exports = function (data) {
	var instance = new Target(data);
	return instance;
};

/**
 * Constructor
 * @param {Object} options
 */
function Target (options) {
/* Decorated properties
	this.type = '';
	this.sources = null;
	this.runtimeOptions = null;
	this.fileCache = null;
	this.fileExtensions = null;
	this.input = '';
	this.output = '';
	this.inputPath = '';
	this.outputPath = '';
	this.workflow = null;
	this.isDir = false;
	this.modular = false;
	this.targets = null;
	this.parent = null;
	this.hasChildren = false;
	this.bootstrap = false;
	this.boilerplate = false;
*/
	extend(this, options);
	this.fileFactoryOptions = {
		type: this.type,
		fileExtensions: this.fileExtensions,
		sources: this.sources,
		runtimeOptions: this.runtimeOptions
	};
	this.fileOptions = {
		filepath: this.outputPath,
	};
	this.fileFilter = this.fileExtensions ? new RegExp(this.fileExtensions.join('$|') + '$') : null;
	this.referencedFiles = [];

	debug("created "
		+ this.type
		+ " Target instance with input: "
		+ (strong(this.input))
		+ " and output: "
		+ (strong(this.output)), 2);
}

/**
 * Run build
 * @returns {Array}
 */
Target.prototype.build = function * () {
	this.referencedFiles = [];

	cnsl.start(this.input);

	print("building "
		+ (strong(path.basename(this.inputPath)))
		+ " to "
		+ (strong(path.basename(this.outputPath))), 2);

	// Execute 'before' hook
	if (this.before) yield this.executeHook('before', this);
	// Parse and start build process
	var files = yield this.parse(this.isDir, this.inputPath, this.fileFilter, this.fileFactoryOptions);
	// Process files
	files = yield this.process(files, this.workflow);
	// Execute 'afterEach' hooks
	if (this.afterEach) {
		yield files.map(function (file) {
			return this.executeHook('afterEach', file);
		}, this);
	}

	print("[processed "
		+ strong(this.referencedFiles.length)
		+ (this.referencedFiles.length > 1 ? " files" : " file")
		+ " in "
		+ colour((cnsl.stop(this.input) / 1000) + 's', cnsl.CYAN)
		+ "]", 3);

	// Write writeable files
	var filepaths = yield this.write(files);
	if (this.hasChildren) {
		// Lock files to prevent inclusion in downstream targets
		this.lock(this.referencedFiles);
		// Build child targets in sequence
		for (var i = 0, n = this.targets.length; i < n; i++) {
			filepaths = filepaths.concat(yield this.targets[i].build());
		}
		this.unlock(this.referencedFiles);
	}
	// Execute 'after' hook
	if (this.after) yield this.executeHook('after', this);

	// Reset
	this.reset();

	// Return output paths on completion
	return filepaths;
};

/**
 * Parse 'input' source files
 * @param {Boolean} isDir
 * @param {String} input
 * @param {RegExp} filter
 * @param {Object} options
 * @returns {Array}
 */
Target.prototype.parse = function * (isDir, input, filter, options) {
	var files;

	// Input is directory
	if (isDir) {
		// Grab all files (filtered by file extension)
		var filepaths = yield readdir(input, filter);
		// Create file instances
		files = filepaths.map(function (filepath) {
			return fileFactory(filepath, options);
		}).filter(function (file) {
			return file != null;
		});
		if (!files.length) warn('no valid source files found in ' + strong(input), 4);

	// Input is file
	} else {
		// Create File instance
		var file = fileFactory(input, options);
		file.isRoot = true;
		if (!file) {
			warn(strong(input) + ' not found in project sources', 4);
			files = [];
		} else {
			files = [file];
		}
	}

	return files;
};

/**
 * Process batch workflow 'commands' for 'files'
 * @param {Array} files
 * @param {Array} workflow
 * @returns {Array}
 */
Target.prototype.process = function * (files, workflow) {
	// Recursive batch run 'fls' with 'tasks'
	function * batch (fls, tasks) {
		// Run file tasks in parallel
		var deps = yield fls.map(function (fl) {
			return fl.run(tasks);
		});
		// Process files not yet processed
		deps = difference(unique(flatten(compact(deps))), files);
		if (deps.length) {
			// Store
			files = files.concat(deps);
			yield batch(deps, tasks);
		}
	}

	yield batch(files, workflow[0]);

	// Store all referenced files
	this.referencedFiles = files;

	// Second set is filtered on writeability
	if (workflow.length > 1) {
		// Filter writeable files
		files = files.filter(function (file) {
			return file.getIsWriteable();
		});
		yield batch(files, workflow[1]);
	}

	return files;
};

/**
 * Write generated content
 * @param {Array} files
 * @returns {Array}
 */
Target.prototype.write = function * (files) {
	var options = {
				bootstrap: this.bootstrap,
				boilerplate: this.boilerplate
			}
		, filepaths = []
		, file, filepath;

	// Write files in sequence
	for (var i = 0, n = files.length; i < n; i++) {
		file = files[i];
		filepath = this.outputPath;
		// Resolve output path if directory
		if (!path.extname(filepath).length) filepath = path.join(filepath, file.id) + '.' + file.type;
		// Store
		filepaths.push(filepath);

		// Write file
		yield file.write(filepath, options);
	}

	return filepaths;
};

/**
 * Set lock flag for 'files'
 * @param {Array} files
 */
Target.prototype.lock = function (files) {
	files.forEach(function (file) {
		file.isLocked = true;
	});
};

/**
 * Unset lock flag for 'files'
 * @param {Array} files
 */
Target.prototype.unlock = function (files) {
	files.forEach(function (file) {
		file.isLocked = false;
	});
};

/**
 * Determine if 'file' is a referenced file (child targets included)
 * @param {File} file
 * @returns {Boolean}
 */
Target.prototype.hasFile = function (file) {
	var referenced;

	referenced = ~this.referencedFiles.indexOf(file);
	if (referenced) {
		return true;
	} else if (this.hasChildren) {
		for (var i = 0, n = this.targets.length; i < n; i++) {
			referenced = this.targets[i].hasFile(file);
			if (referenced) return true;
		}
	}
	return false;
};

/**
 * Reset modified files
 */
Target.prototype.reset = function () {
	this.referencedFiles.forEach(function (file) {
		file.reset();
	});
	clearIDCache();
	clearTemplateCache();
};

/**
 * Execute the 'hook' function with a particular 'context'
 * @param {String} hook
 * @param {Object} context
 */
Target.prototype.executeHook = function (hook, context) {
	var hook = this[hook];
	print('executing ' + hook + ' hook...', 3);

	// Return thunk
	return function (done) {
		// Make global objects available to the function
		hook(global, process, console, require, context, this.runtimeOptions, done);
	}
};
<|endoftext|>


<commit_before>/*
criticalCSS by @scottjehl. Run this on your CSS, get the styles that are applicable in the viewport (critical). The url arg should be any part of the URL of the stylesheets you'd like to parse. So, 'all.css' or '/css/' would both work.
*/
/*global window:true*/
(function(exports){
	"use strict";
	/**
	 * opts
	 * opts.sheets = document.styleSheets
	 * opts.maxTop = window.innerHeight
	 * opts.querySelector = function( selector ){
	 *     return page.evaluate(function(s){
	 *       return document.querySelector( s );
	 *     },selector);
	 * opts.matchMedia = window.matchMedia
	 */
	var criticalCSS = function( url, opts ){
		var sheets = opts.sheets,
			maxTop = opts.maxTop,
			critical = [];

			var removeNull = function( el ){
				return el !== null;
			};

		var critCSSText;
		var aboveFold = function( rule ){
			if( !rule.selectorText ){
				return false;
			}
			var selectors = rule.selectorText.split(","),
				criticalSelectors = [];
			for( var k = 0, sl = selectors.length; k < sl; k++ ) {
				var elem = opts.querySelector( selectors[ k ] );
				if( elem && elem.offsetTop <= maxTop ){
					criticalSelectors.push( selectors[ k ] );
				}
			}
			if( criticalSelectors.length ){
				return criticalSelectors.join(",") + rule.cssText.match( /\{.+/ );
			}
			else {
				return false;
			}
		};

		sheets = Array.prototype.filter.call(sheets, removeNull);

		for( var i = 0, l = sheets.length; i < l; i++ ){
			var sheet = sheets[ i ],
				href = sheet.href,
				rules = sheet.rules;

				rules = Array.prototype.filter.call( rules, removeNull );

			if( url && href && href.indexOf( url ) > -1 ){
				for( var j = 0, rl = rules.length; j < rl; j++ ){

					var media = rules[ j ].media,
						matchingRules = [];
					if( media && opts.matchMedia( media.mediaText ).matches ){
						var innerRules = rules[ j ].rules;
						for( var k in innerRules ){
							critCSSText = aboveFold( innerRules[ k ] );
							if( critCSSText ){
								matchingRules.push( critCSSText );
							}
						}
						if( matchingRules.length ){
							matchingRules.unshift( "@media " + media.mediaText + "{" );
							matchingRules.push( "}" );
						}

					} else if( !media ){
						critCSSText = aboveFold( rules[ j ] );
						if( critCSSText && !critCSSText.match(/^\s+$/) ){
							matchingRules.push( critCSSText );
						}
					}
					critical.push( matchingRules.join( "\n" ) );
				}

			}
		}
		return critical.join( "\n" );
	};

	exports.criticalCSS = criticalCSS;

}(typeof exports === 'object' && exports || this));
<commit_msg>JSHINT<commit_after>/*
criticalCSS by @scottjehl. Run this on your CSS, get the styles that are applicable in the viewport (critical). The url arg should be any part of the URL of the stylesheets you'd like to parse. So, 'all.css' or '/css/' would both work.
*/
(function(exports){
	"use strict";
	/**
	 * opts
	 * opts.sheets = document.styleSheets
	 * opts.maxTop = window.innerHeight
	 * opts.querySelector = function( selector ){
	 *     return page.evaluate(function(s){
	 *       return document.querySelector( s );
	 *     },selector);
	 * opts.matchMedia = window.matchMedia
	 */
	var criticalCSS = function( url, opts ){
		var sheets = opts.sheets,
			maxTop = opts.maxTop,
			critical = [];

			var removeNull = function( el ){
				return el !== null;
			};

		var critCSSText;
		var aboveFold = function( rule ){
			if( !rule.selectorText ){
				return false;
			}
			var selectors = rule.selectorText.split(","),
				criticalSelectors = [];
			for( var k = 0, sl = selectors.length; k < sl; k++ ) {
				var elem = opts.querySelector( selectors[ k ] );
				if( elem && elem.offsetTop <= maxTop ){
					criticalSelectors.push( selectors[ k ] );
				}
			}
			if( criticalSelectors.length ){
				return criticalSelectors.join(",") + rule.cssText.match( /\{.+/ );
			}
			else {
				return false;
			}
		};

		sheets = Array.prototype.filter.call(sheets, removeNull);

		for( var i = 0, l = sheets.length; i < l; i++ ){
			var sheet = sheets[ i ],
				href = sheet.href,
				rules = sheet.rules;

				rules = Array.prototype.filter.call( rules, removeNull );

			if( url && href && href.indexOf( url ) > -1 ){
				for( var j = 0, rl = rules.length; j < rl; j++ ){

					var media = rules[ j ].media,
						matchingRules = [];
					if( media && opts.matchMedia( media.mediaText ).matches ){
						var innerRules = rules[ j ].rules;
						for( var k in innerRules ){
							critCSSText = aboveFold( innerRules[ k ] );
							if( critCSSText ){
								matchingRules.push( critCSSText );
							}
						}
						if( matchingRules.length ){
							matchingRules.unshift( "@media " + media.mediaText + "{" );
							matchingRules.push( "}" );
						}

					} else if( !media ){
						critCSSText = aboveFold( rules[ j ] );
						if( critCSSText && !critCSSText.match(/^\s+$/) ){
							matchingRules.push( critCSSText );
						}
					}
					critical.push( matchingRules.join( "\n" ) );
				}

			}
		}
		return critical.join( "\n" );
	};

	exports.criticalCSS = criticalCSS;

}(typeof exports === 'object' && exports || this));
<|endoftext|>


<commit_before>/**
 * Misc utils
 */

var config = require('./config')
  , fs = require('fs')
  , crypto = require('crypto')
  , childProcess = require('child_process')
  , util = require('util')
  , Stream = require('stream')
  , request = require('request')
  , async = require('async')
  ;

/**
 * Check if folder exists and create it on the fly if it is not the case
 */
function ensureFolderExists (folder, callback) {
  fs.exists(folder, function (exists) {
    if (exists) {
      return callback();
    } else {
      fs.mkdir(folder, '0777', callback);
    }
  });
}
module.exports.ensureFolderExists = ensureFolderExists;

/**
 * Check that two files are identical.
 */
module.exports.checkIfFilesAreIdentical = function (file1, file2, callback) {
  fs.exists(file1, function (exists1) {
    fs.exists(file2, function (exists2) {

      if (! exists1 && ! exists2) { return callback(null, true); }
      if (! (exists1 && exists2)) { return callback(null, false); }

      fs.readFile(file1, 'utf8', function (err, data1) {
        if (err) { return callback(err); }
        fs.readFile(file2, 'utf8', function (err, data2) {
          if (err) { return callback(err); }

          if (data1 === data2) {
            return callback(null, true);
          } else {
            return callback(null, false);
          }
        });
      });
    });
  });
};

/**
 * Replace target by file
 * Erase target before copying if it exists
 * If file doesn't exist, also erase target
 */
module.exports.copySafe = function (file, target, callback) {
  async.waterfall([
    function (cb) {
    fs.exists(target, function (existsT) {
      if (!existsT) { return cb(); }
      fs.unlink(target, function (err) {
        return cb(err);
      });
    });
  }
  , function (cb) {
    fs.exists(file, function (existsF) {
      if (!existsF) { return cb(); }
      fs.readFile(file, 'utf8', function (err, data) {
        if (err) { return cb(err); }
        fs.writeFile(target, data, 'utf8', function (err) {
          return cb(err);
        });
      });
    });
  }
  ], callback);
};

// Generates a random alphanumerical string of length len (very low probability for it to be of a smaller length)
// Taken from Connect
function uid (len) {
  return crypto.randomBytes(Math.ceil(len))
    .toString('base64')
    .replace(/[^a-zA-Z0-9]/g, '')
    .slice(0, len);
}
module.exports.uid = uid;

/**
 * Sanitize a script by replacing all newlines (which are OS-dependent)
 * by semi colons if the end-of-line colon is missing and then
 * removing all fucking new lines
 */
function sanitizeScript (script) {
  var res = script;

  res = res.replace(/([^;])\r\n/g, '$1;');
  res = res.replace(/\r\n/g, '');

  res = res.replace(/([^;])\r/g, '$1;');
  res = res.replace(/\r/g, '');

  res = res.replace(/([^;])\n/g, '$1;');
  res = res.replace(/\n/g, '');

  return res;
}

/**
 * Execute any bash script
 * Does it by copying it in a temporary file then using bash on it.
 * We do it this way to get the output of the script in real time
 * @param {String} script Script to execute, as a string
 * @param {Object} env Object containing any environment variables
 * @param {WritableStream} out All output logging goes here
 * @param {Function} callback To be called after script ends
 */
module.exports.executeBashScript = function (script, env, out, callback) {
  var executor
    , scriptFileName = config.workspace + '/_temp/script' + uid(13) + '.sh'
    ;

  env = env || {};

  script = sanitizeScript(script);
  ensureFolderExists(config.workspace + '/_temp', function (err) {
    if (err) { return callback(err); }

    fs.writeFile(scriptFileName, script, 'utf8', function (err) {
      if (err) { return callback(err); }

      executor = childProcess.spawn('bash', [scriptFileName], { env: env });
      if (out) { executor.stdout.pipe(out, { end: false }); }
      if (out) { executor.stderr.pipe(out, { end: false }); }

      executor.on('exit', function (code) {
        var error = code === 0 ? null : "Error while executing script";

        fs.unlink(scriptFileName, function () {
          callback(error);
        });
      });
    });
  });
};

/**
 * Transform an object indexed by numbers into an array indexed in the same order
 * Object must be indexed starting from 1, which is the case of the previousBuilds object
 */
module.exports.objectToArrayInOrder = function (obj) {
  var res = [], fields = Object.keys(obj), field, i;

  for (i = 0; i < fields.length; i += 1) {
    field = fields[i];
    res[parseInt(field, 10) - 1] = obj[field.toString()];
  }

  return res;
};

/**
 * Passthrough stream that can be written to and write to multiple streams
 * Written by Felix Geisendorfer
 */

function PassthroughStream() {
  this.writable = true;
  this.readable = true;
}
util.inherits(PassthroughStream, Stream);

PassthroughStream.prototype.write = function(data) {
  this.emit('data', data);
};

PassthroughStream.prototype.end = function() {
  this.emit('end');
};

PassthroughStream.prototype.destroy = function() {
  this.emit('close');
};

module.exports.PassthroughStream = PassthroughStream;

/**
 * Send a message to Hipchat (asynchronously). data must contain the following properties
 *  * room_id
 *  * from - arbitrary name
 *  * message_format - 'text' or 'html'
 *  * message
 *  * notify - 1 or 0
 *  * color - 'yellow', 'red', 'green', 'purple', 'gray' or 'random'
 */
module.exports.sendMessageToHipchat = function (data) {
  var uri = "https://api.hipchat.com/v1/rooms/message?format=json&auth_token=" + config.hipchatToken
    , keys = Object.keys(data);

  keys.forEach(function (key) {
    uri += '&' + key + '=' + encodeURIComponent(data[key]);
  });

  request.get({ headers: {"Accept": "application/json"}, uri: uri}, function (a, b, c) {
  });
}

<commit_msg>Correct bug<commit_after>/**
 * Misc utils
 */

var config = require('./config')
  , fs = require('fs')
  , crypto = require('crypto')
  , childProcess = require('child_process')
  , util = require('util')
  , Stream = require('stream')
  , request = require('request')
  , async = require('async')
  ;

/**
 * Check if folder exists and create it on the fly if it is not the case
 */
function ensureFolderExists (folder, callback) {
  fs.exists(folder, function (exists) {
    if (exists) {
      return callback();
    } else {
      fs.mkdir(folder, '0777', callback);
    }
  });
}
module.exports.ensureFolderExists = ensureFolderExists;

/**
 * Check that two files are identical.
 */
module.exports.checkIfFilesAreIdentical = function (file1, file2, callback) {
  fs.exists(file1, function (exists1) {
    fs.exists(file2, function (exists2) {

      if (! exists1 && ! exists2) { return callback(null, true); }
      if (! (exists1 && exists2)) { return callback(null, false); }

      fs.readFile(file1, 'utf8', function (err, data1) {
        if (err) { return callback(err); }
        fs.readFile(file2, 'utf8', function (err, data2) {
          if (err) { return callback(err); }

          if (data1 === data2) {
            return callback(null, true);
          } else {
            return callback(null, false);
          }
        });
      });
    });
  });
};

/**
 * Replace target by file
 * Erase target before copying if it exists
 * If file doesn't exist, also erase target
 */
module.exports.copySafe = function (file, target, callback) {
  async.waterfall([
    function (cb) {
    fs.exists(target, function (existsT) {
      if (!existsT) { return cb(); }
      fs.unlink(target, function (err) {
        return cb(err);
      });
    });
  }
  , function (cb) {
    fs.exists(file, function (existsF) {
      if (!existsF) { return cb(); }
      fs.readFile(file, 'utf8', function (err, data) {
        if (err) { return cb(err); }
        fs.writeFile(target, data, 'utf8', function (err) {
          return cb(err);
        });
      });
    });
  }
  ], callback);
};

// Generates a random alphanumerical string of length len (very low probability for it to be of a smaller length)
// Taken from Connect
function uid (len) {
  return crypto.randomBytes(Math.ceil(len))
    .toString('base64')
    .replace(/[^a-zA-Z0-9]/g, '')
    .slice(0, len);
}
module.exports.uid = uid;

/**
 * Sanitize a script by replacing all newlines (which are OS-dependent)
 * by semi colons if the end-of-line colon is missing and then
 * removing all fucking new lines
 */
function sanitizeScript (script) {
  var res = script;

  res = res.replace(/([^;])\r\n/g, '$1;');
  res = res.replace(/\r\n/g, '');

  res = res.replace(/([^;])\r/g, '$1;');
  res = res.replace(/\r/g, '');

  res = res.replace(/([^;])\n/g, '$1;');
  res = res.replace(/\n/g, '');

  return res;
}

/**
 * Execute any bash script
 * Does it by copying it in a temporary file then using bash on it.
 * We do it this way to get the output of the script in real time
 * @param {String} script Script to execute, as a string
 * @param {Object} env Object containing any environment variables
 * @param {WritableStream} out All output logging goes here
 * @param {Function} callback To be called after script ends
 */
module.exports.executeBashScript = function (script, env, out, callback) {
  var executor
    , scriptFileName = config.workspace + '/_temp/script' + uid(13) + '.sh'
    , options = env ? { env: env } : {}
    ;

  script = sanitizeScript(script);
  ensureFolderExists(config.workspace + '/_temp', function (err) {
    if (err) { return callback(err); }

    fs.writeFile(scriptFileName, script, 'utf8', function (err) {
      if (err) { return callback(err); }

      executor = childProcess.spawn('bash', [scriptFileName], options);
      if (out) { executor.stdout.pipe(out, { end: false }); }
      if (out) { executor.stderr.pipe(out, { end: false }); }

      executor.on('exit', function (code) {
        var error = code === 0 ? null : "Error while executing script";

        fs.unlink(scriptFileName, function () {
          callback(error);
        });
      });
    });
  });
};

/**
 * Transform an object indexed by numbers into an array indexed in the same order
 * Object must be indexed starting from 1, which is the case of the previousBuilds object
 */
module.exports.objectToArrayInOrder = function (obj) {
  var res = [], fields = Object.keys(obj), field, i;

  for (i = 0; i < fields.length; i += 1) {
    field = fields[i];
    res[parseInt(field, 10) - 1] = obj[field.toString()];
  }

  return res;
};

/**
 * Passthrough stream that can be written to and write to multiple streams
 * Written by Felix Geisendorfer
 */

function PassthroughStream() {
  this.writable = true;
  this.readable = true;
}
util.inherits(PassthroughStream, Stream);

PassthroughStream.prototype.write = function(data) {
  this.emit('data', data);
};

PassthroughStream.prototype.end = function() {
  this.emit('end');
};

PassthroughStream.prototype.destroy = function() {
  this.emit('close');
};

module.exports.PassthroughStream = PassthroughStream;

/**
 * Send a message to Hipchat (asynchronously). data must contain the following properties
 *  * room_id
 *  * from - arbitrary name
 *  * message_format - 'text' or 'html'
 *  * message
 *  * notify - 1 or 0
 *  * color - 'yellow', 'red', 'green', 'purple', 'gray' or 'random'
 */
module.exports.sendMessageToHipchat = function (data) {
  var uri = "https://api.hipchat.com/v1/rooms/message?format=json&auth_token=" + config.hipchatToken
    , keys = Object.keys(data);

  keys.forEach(function (key) {
    uri += '&' + key + '=' + encodeURIComponent(data[key]);
  });

  request.get({ headers: {"Accept": "application/json"}, uri: uri}, function (a, b, c) {
  });
}

<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');

const evaluateVars = require('./evaluate_vars');

function readFile(filename, vars) {
  const content = fs.readFileSync(filename)
    .toString();

  const newContent = evaluateVars(content, vars);

  try {
    const items = JSON.parse(newContent);
    return items;
  } catch (error) {
    throw new Error(`couldn't parse package.json at '${filename}': ${parseError.message}`);
  }
}

function readDir(dirname, vars) {

  const store = {};

  const entries = fs.readdirSync(dirname);

  for (let index = 0; index < entries.length; index++) {
    const entry = entries[index];

    if (entry === '.' || entry === '..') {
      // skip
    } else {

      const pathName = path.join(dirname, entry);

      const lstatSync = fs.lstatSync(pathName);

      if (lstatSync.isDirectory()) {
        const subStore = readDir(pathName, vars);

        store[entry] = subStore;
      } else {
        const subStore = readFile(pathName, vars);

        // TODO: should it be json every time?
        const namespace = path.basename(pathName, '.json');

        store[namespace] = subStore;
      }
    }
  }

  return store;
}

/**
 * Entrypoint to start a scann for the given directory
 * to load all '*.conf' file and parse them as json files.
 *
 * @method dirScanner
 * @public
 *
 * @param {string} startDir - the directory to start the scann.
 * @param {object} vars - a key value list of vars that need to be
 *  evaluated within the config content.
 * @return {object} - Returns the store for all configurations.
 */
function dirScanner(startDir, vars) {

  const store = readDir(startDir, vars);

  return store;
}

module.exports = dirScanner;
<commit_msg>Update error message for readFile error handler<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

const evaluateVars = require('./evaluate_vars');

function readFile(filename, vars) {
  const content = fs.readFileSync(filename)
    .toString();

  const newContent = evaluateVars(content, vars);

  try {
    const items = JSON.parse(newContent);
    return items;
  } catch (error) {
    throw new Error(`couldn't parse JSON-file at '${filename}': ${error.message}`);
  }
}

function readDir(dirname, vars) {

  const store = {};

  const entries = fs.readdirSync(dirname);

  for (let index = 0; index < entries.length; index++) {
    const entry = entries[index];

    if (entry === '.' || entry === '..') {
      // skip
    } else {

      const pathName = path.join(dirname, entry);

      const lstatSync = fs.lstatSync(pathName);

      if (lstatSync.isDirectory()) {
        const subStore = readDir(pathName, vars);

        store[entry] = subStore;
      } else {
        const subStore = readFile(pathName, vars);

        // TODO: should it be json every time?
        const namespace = path.basename(pathName, '.json');

        store[namespace] = subStore;
      }
    }
  }

  return store;
}

/**
 * Entrypoint to start a scann for the given directory
 * to load all '*.conf' file and parse them as json files.
 *
 * @method dirScanner
 * @public
 *
 * @param {string} startDir - the directory to start the scann.
 * @param {object} vars - a key value list of vars that need to be
 *  evaluated within the config content.
 * @return {object} - Returns the store for all configurations.
 */
function dirScanner(startDir, vars) {

  const store = readDir(startDir, vars);

  return store;
}

module.exports = dirScanner;
<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
 * @author David Spreekmeester <david@grrr.nl>
 */
const _ = require('lodash')

function getArguments() {
    var args = []
    var baseCommandPos = false
console.logs(process.argv)
    for (var i in process.argv) {
        if (
            baseCommandPos !== false &&
            i > (baseCommandPos + 1)
        ) {
            args.push(process.argv[i])
        }

        if (
            (
                _.endsWith(process.argv[i], '.js') ||
                '12g' === process.argv[i]
            ) && baseCommandPos === false
        ) {
            // This is the executable or the entry node script
            baseCommandPos = Number(i)
        }
    }

    return args.join(' ')
}

module.exports = {
    commands: function(subcommander) {
        var dock = subcommander.command('dock', {
            desc: 'Docker Compose convenience method',
            callback: function (options) {
                var dock = require('./dock.js')
                dock(getArguments())
            }
        })
    }
};
<commit_msg>Debugging npm dist<commit_after>#!/usr/bin/env node
/**
 * @author David Spreekmeester <david@grrr.nl>
 */
const _ = require('lodash')

function getArguments() {
    var args = []
    var baseCommandPos = false
console.log(process.argv)
    for (var i in process.argv) {
        if (
            baseCommandPos !== false &&
            i > (baseCommandPos + 1)
        ) {
            args.push(process.argv[i])
        }

        if (
            (
                _.endsWith(process.argv[i], '.js') ||
                '12g' === process.argv[i]
            ) && baseCommandPos === false
        ) {
            // This is the executable or the entry node script
            baseCommandPos = Number(i)
        }
    }

    return args.join(' ')
}

module.exports = {
    commands: function(subcommander) {
        var dock = subcommander.command('dock', {
            desc: 'Docker Compose convenience method',
            callback: function (options) {
                var dock = require('./dock.js')
                dock(getArguments())
            }
        })
    }
};
<|endoftext|>