<commit_before>/* jshint node: true */
'use strict';

var EventEmitter = require('events').EventEmitter;
var rtc = require('rtc');
var defaults = require('cog/defaults');
var reTrailingSlash = /\/$/;

/**
  # rtc-quickconnect

  This is a very high level helper library designed to help you get up
  an running with WebRTC really, really quickly.  By using this module you
  are trading off some flexibility, so if you need a more flexible
  configuration you should drill down into lower level components of the
  [rtc.io](http://www.rtc.io) suite.

  ## Example Usage

  In the simplest case you simply call quickconnect with a single string
  argument to establish a namespace for your demo or application.  This string
  will then be combined with randomly generated location hash that will
  determine the room for your application signalling.

  <<< examples/simple.js

  ## Example Usage (Using Data Channels)

  By default, the `RTCPeerConnection` created by quickconnect will not be
  "data channels ready".  You can change that very simply, by flagging
  `data` as `true` during quickconnect initialization:

  <<< examples/datachannel.js

  ## How it works?

  The `rtc-quickconnect` module makes use of our internal, publicly available
  signaller which uses [socket.io](http://socket.io/) and our
  [signalling adapter](https://github.com/rtc-io/rtc-signaller-socket.io).

  Our test signaller is exactly that, __something we use for testing__.  If
  you want to run your own signaller this is very simple and you should
  consult the `rtc-signaller-socket.io` module for information on how to
  do this.  Once you have this running, simply provide quickconnect a
  signaller option when creating:

  ```js
  var quickconnect = require('rtc-quickconnect');

  quickconnect({ ns: 'test', signaller: 'http://mysignaller.com:3000' });
  ```

  ## Full Reactive Stream Conference Example

  <<< examples/conference.js

**/
module.exports = function(opts) {
  var hash = location.hash.slice(1);
  var emitter = new EventEmitter();
  var signaller;
  var logger;
  var peers = {};

  function channel(peerId, dc) {
    dc.addEventListener('open', function(evt) {
      emitter.emit('dc:open', dc, peerId);
    });

  }

  // if the opts is a string, then we only have a namespace
  if (typeof opts == 'string' || (opts instanceof String)) {
    opts = {
      ns: opts
    };
  }

  // initialise the deafult opts
  opts = defaults(opts, {
    signaller: 'http://rtcjs.io:50000'
  });

  // create our logger
  logger = rtc.logger(opts.ns);

  // if debug is enabled, then let's get some noisy logging going
  if (opts.debug) {
    rtc.logger.enable('*');
  }

  // if the hash is not assigned, then create a random hash value
  if (! hash) {
    hash = location.hash = '' + (Math.pow(2, 53) * Math.random());
  }

  // load socket.io script
  loadSocketIO(opts.signaller, function() {
    // create our signaller
    signaller = rtc.signaller(io.connect(opts.signaller), {
      dataEvent: 'message',
      openEvent: 'connect'
    });

    signaller.on('announce', function(data) {
      var peer;
      var dc;

      // if this is a known peer then abort
      if (peers[data.id]) {
        return;
      }

      // create a peer
      peer = peers[data.id] = rtc.createConnection(opts);

      // trigger the peer event
      emitter.emit('peer', peer, data.id);

      // if we are working with data channels, create a data channel too
      if (opts.data && (! data.answer)) {
        channel(data.id, peer.createDataChannel('tx', { reliable: false }));
      }
      else if (opts.data) {
        peer.addEventListener('datachannel', function(evt) {
          channel(data.id, evt.channel);
        });
      }

      // couple the connections
      rtc.couple(peer, { id: data.id }, signaller);

      // if not an answer, then announce back to the caller
      if (! data.answer) {
        signaller.to(data.id).announce({
          room: opts.ns + '#' + hash,
          answer: true
        });
      }
    });

    // pass on leave events
    signaller.on('leave', emitter.emit.bind(emitter, 'leave'));

    // time to announce ourselves
    signaller.announce({ room: opts.ns + '#' + hash });
  });

  return emitter;
};

function loadSocketIO(url, callback) {
  var script = document.createElement('script');
  script.src = url.replace(reTrailingSlash, '') + '/socket.io/socket.io.js';
  script.addEventListener('load', callback);
  document.body.appendChild(script);
};<commit_msg>pass through original announce data in peer event<commit_after>/* jshint node: true */
'use strict';

var EventEmitter = require('events').EventEmitter;
var rtc = require('rtc');
var defaults = require('cog/defaults');
var reTrailingSlash = /\/$/;

/**
  # rtc-quickconnect

  This is a very high level helper library designed to help you get up
  an running with WebRTC really, really quickly.  By using this module you
  are trading off some flexibility, so if you need a more flexible
  configuration you should drill down into lower level components of the
  [rtc.io](http://www.rtc.io) suite.

  ## Example Usage

  In the simplest case you simply call quickconnect with a single string
  argument to establish a namespace for your demo or application.  This string
  will then be combined with randomly generated location hash that will
  determine the room for your application signalling.

  <<< examples/simple.js

  ## Example Usage (Using Data Channels)

  By default, the `RTCPeerConnection` created by quickconnect will not be
  "data channels ready".  You can change that very simply, by flagging
  `data` as `true` during quickconnect initialization:

  <<< examples/datachannel.js

  ## How it works?

  The `rtc-quickconnect` module makes use of our internal, publicly available
  signaller which uses [socket.io](http://socket.io/) and our
  [signalling adapter](https://github.com/rtc-io/rtc-signaller-socket.io).

  Our test signaller is exactly that, __something we use for testing__.  If
  you want to run your own signaller this is very simple and you should
  consult the `rtc-signaller-socket.io` module for information on how to
  do this.  Once you have this running, simply provide quickconnect a
  signaller option when creating:

  ```js
  var quickconnect = require('rtc-quickconnect');

  quickconnect({ ns: 'test', signaller: 'http://mysignaller.com:3000' });
  ```

  ## Full Reactive Stream Conference Example

  <<< examples/conference.js

**/
module.exports = function(opts) {
  var hash = location.hash.slice(1);
  var emitter = new EventEmitter();
  var signaller;
  var logger;
  var peers = {};

  function channel(peerId, dc) {
    dc.addEventListener('open', function(evt) {
      emitter.emit('dc:open', dc, peerId);
    });

  }

  // if the opts is a string, then we only have a namespace
  if (typeof opts == 'string' || (opts instanceof String)) {
    opts = {
      ns: opts
    };
  }

  // initialise the deafult opts
  opts = defaults(opts, {
    signaller: 'http://rtcjs.io:50000'
  });

  // create our logger
  logger = rtc.logger(opts.ns);

  // if debug is enabled, then let's get some noisy logging going
  if (opts.debug) {
    rtc.logger.enable('*');
  }

  // if the hash is not assigned, then create a random hash value
  if (! hash) {
    hash = location.hash = '' + (Math.pow(2, 53) * Math.random());
  }

  // load socket.io script
  loadSocketIO(opts.signaller, function() {
    // create our signaller
    signaller = rtc.signaller(io.connect(opts.signaller), {
      dataEvent: 'message',
      openEvent: 'connect'
    });

    signaller.on('announce', function(data) {
      var peer;
      var dc;

      // if this is a known peer then abort
      if (peers[data.id]) {
        return;
      }

      // create a peer
      peer = peers[data.id] = rtc.createConnection(opts);

      // trigger the peer event
      emitter.emit('peer', peer, data.id, data);

      // if we are working with data channels, create a data channel too
      if (opts.data && (! data.answer)) {
        channel(data.id, peer.createDataChannel('tx', { reliable: false }));
      }
      else if (opts.data) {
        peer.addEventListener('datachannel', function(evt) {
          channel(data.id, evt.channel);
        });
      }

      // couple the connections
      rtc.couple(peer, { id: data.id }, signaller);

      // if not an answer, then announce back to the caller
      if (! data.answer) {
        signaller.to(data.id).announce({
          room: opts.ns + '#' + hash,
          answer: true
        });
      }
    });

    // pass on leave events
    signaller.on('leave', emitter.emit.bind(emitter, 'leave'));

    // time to announce ourselves
    signaller.announce({ room: opts.ns + '#' + hash });
  });

  return emitter;
};

function loadSocketIO(url, callback) {
  var script = document.createElement('script');
  script.src = url.replace(reTrailingSlash, '') + '/socket.io/socket.io.js';
  script.addEventListener('load', callback);
  document.body.appendChild(script);
};<|endoftext|>


<commit_before>'use strict';

module.exports = function(options) {
  var useOptions = {
    guessName: function(name) {
      return '';
    }, mapping: {}
  };

  var target = {};
  var handler = {};

  var setMapping = function(items) {
    if (false === (items instanceof Object)) {
      return;
    }

    Object.keys(items).every(function(index) {
      let item = items[index];

      switch (typeof item) {
        case 'string':
          useOptions.mapping[index] = item;
          break;

        case 'object':
        case 'function':
          target[index] = item;
          break;

        default:
          break;
      }
    });
  };

  var setOptions = function(items) {
    if (false === (items instanceof Object)) {
      return;
    }

    if (items.hasOwnProperty('guessName') && ('function' === typeof items.guessName)) {
      useOptions.guessName = items.guessName;
    }

    if (items.hasOwnProperty('mapping')) {
      setMapping(items.mapping);
    }
  };

  var packageName = function(name) {
    return name.trim().replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/[-\s]+/g, '-').toLowerCase();
  };

  var getNames = function(property) {
    const mapping = useOptions.mapping;
    let names = [];

    if (mapping.hasOwnProperty(property)) {
      names.push(mapping[property]);
    }

    names.push(property);
    names.push(packageName(property));

    try {
      let guessName = useOptions.guessName(property);

      if (('string' === typeof guessName) && ('' !== guessName)) {
        names.push(guessName);
      }

      return names;
    } catch (error) {
      return names;
    }
  };

  var getRequire = function(names) {
    let value = null;

    names.every(function(name) {
      try {
        value = require(name);
        return false;
      } catch (error) {
        return true;
      }
    });

    return value;
  };

  handler.get = function(target, property, receiver) {
    if (target.hasOwnProperty(property)) {
      return target[property];
    }

    const names = getNames(property);
    target[property] = getRequire(names);

    if (null !== target[property]) {
      return target[property];
    }

    delete target[property];
    const error = new Error("Cannot find module '" + property + "'");
    error.code = 'MODULE_NOT_FOUND';
    throw error;
  };

  setOptions(options);
  return new Proxy(target, handler);
};
<commit_msg>use instanceof to check object<commit_after>'use strict';

module.exports = function(options) {
  let target = {};
  let handler = {};

  let useOptions = {
    mapping: {}, guessName: function(name) {
      return '';
    }
  };

  let setMapping = function(items) {
    if (false === (items instanceof Object)) {
      return;
    }

    Object.keys(items).every(function(index) {
      let item = items[index];

      switch (typeof item) {
        case 'string':
          useOptions.mapping[index] = item;
          break;

        case 'object':
        case 'function':
          target[index] = item;
          break;

        default:
          break;
      }
    });
  };

  let setOptions = function(items) {
    if (false === (items instanceof Object)) {
      return;
    }

    if (items.hasOwnProperty('guessName') && ('function' === typeof items.guessName)) {
      useOptions.guessName = items.guessName;
    }

    if (items.hasOwnProperty('mapping')) {
      setMapping(items.mapping);
    }
  };

  let packageName = function(property) {
    return property.trim().replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/[-\s]+/g, '-').toLowerCase();
  };

  let getNames = function(property) {
    let mapping = useOptions.mapping;
    let names = [];

    if (mapping.hasOwnProperty(property)) {
      names.push(mapping[property]);
    }

    names.push(property);
    names.push(packageName(property));

    try {
      let guessName = useOptions.guessName(property);

      if (('string' === typeof guessName) && ('' !== guessName)) {
        names.push(guessName);
      }

      return names;
    } catch (error) {
      return names;
    }
  };

  let getRequire = function(property) {
    let value = null;

    getNames(property).every(function(name) {
      try {
        value = require(name);
        return false;
      } catch (error) {
        return true;
      }
    });

    return value;
  };

  handler.get = function(target, property) {
    if (target.hasOwnProperty(property)) {
      return target[property];
    }

    target[property] = getRequire(property);

    if (null !== target[property]) {
      return target[property];
    }

    delete target[property];
    let error = new Error("Cannot find module '" + property + "'");
    error.code = 'MODULE_NOT_FOUND';
    throw error;
  };

  setOptions(options);
  return new Proxy(target, handler);
};
<|endoftext|>


<commit_before>"use strict";

var strValidator = require('validator');
var _ = require('lodash');

var defaultValidators = {
  notEmpty: function (value) {
    return !_.isEmpty(value);
  },
  len: function (value, rule) {
    var result = false;
    if (strValidator.isInt(rule.len)) {
      result = strValidator.isLength(value, rule.len);
    } else if (isArrayOfTwoLength(rule.len)) {
      result = strValidator.isLength(value, rule.len[0], rule.len[1]);
    }
    return result;
  }
};

var defaultMessages = {
  len: function (value, rule) {
    var msg = 'Invalid len';

    if (strValidator.isInt(rule.len)) {
      msg = 'Expected min ' + rule.len + ' symbols';
    } else if (isArrayOfTwoLength(rule.len)) {
      msg = 'Expected [' + rule.len[0] + ',' + rule.len[1] + ']  symbols.';
    }

    msg += ' Given: ' + value.length;

    return msg;
  },
  notEmpty: 'Is empty',
  required: 'Is undefined',
  default: 'Error'
};

var defaultOptions = {
  required: true,
  findFirst: true
};

function validate(value, rule) {
  var isRequired = _.isUndefined(rule.required) ? defaultOptions.required : rule.required;
  var isFindFirst = _.isUndefined(rule.findFirst) ? defaultOptions.findFirst : rule.findFirst;
  var ruleErrors = {};
  var validators = [];

  if (_.isUndefined(value) && isRequired)
    return isRequired ? {isRequired: prepareMessage('required', value, rule)} : {};

  _.forOwn(defaultValidators, function (value, key) {
    if (!_.isUndefined(rule[key])) {
      validators.push([key, value]);
    }
  });

  _.forOwn(rule.custom, function (value, key) {
    if (_.isFunction(value)) {
      validators.push([key, value]);
    }
  });

  for (var i = 0; i < validators.length; i++) {
    var validatorName = validators[i][0];
    var validatorFunc = validators[i][1];
    var result = validatorFunc(value, rule);
    if (!result) {
      ruleErrors[validatorName] = prepareMessage(validatorName, value, rule);
    }
    if (_.size(ruleErrors) && isFindFirst) break;
  }

  return ruleErrors;
}

function prepareMessage(validatorName, value, rule) {
  var message;
  if (!_.isUndefined(rule.msg[validatorName])) {
    message = rule.msg[validatorName];
  } else if (!_.isUndefined(defaultMessages[validatorName])) {
    message = defaultMessages[validatorName];
  } else {
    message = defaultMessages['default'];
  }

  if (_.isFunction(message))
    return message(value, rule);
  else if (_.isString(message))
    return message;
}

function isArrayOfTwoLength(obj) {
  return _.isArray(obj) && obj.length === 2;
}

var validateIt = function(objectToCheck, rules) {
  var errors = {};
  for (var i = 0; i < rules.length; i++) {
    var rule = rules[i];
    errors[rule.name] = validate(objectToCheck[rule.name], rule);
  }
  return errors;
};

module.exports = validateIt;<commit_msg>when rule.msg is undefined script fails -> add check<commit_after>"use strict";

var strValidator = require('validator');
var _ = require('lodash');

var defaultValidators = {
  notEmpty: function (value) {
    return !_.isEmpty(value);
  },
  len: function (value, rule) {
    var result = false;
    if (strValidator.isInt(rule.len)) {
      result = strValidator.isLength(value, rule.len);
    } else if (isArrayOfTwoLength(rule.len)) {
      result = strValidator.isLength(value, rule.len[0], rule.len[1]);
    }
    return result;
  }
};

var defaultMessages = {
  len: function (value, rule) {
    var msg = 'Invalid len';

    if (strValidator.isInt(rule.len)) {
      msg = 'Expected min ' + rule.len + ' symbols';
    } else if (isArrayOfTwoLength(rule.len)) {
      msg = 'Expected [' + rule.len[0] + ',' + rule.len[1] + ']  symbols.';
    }

    msg += ' Given: ' + value.length;

    return msg;
  },
  notEmpty: 'Is empty',
  required: 'Is undefined',
  default: 'Error'
};

var defaultOptions = {
  required: true,
  findFirst: true
};

function validate(value, rule) {
  var isRequired = _.isUndefined(rule.required) ? defaultOptions.required : rule.required;
  var isFindFirst = _.isUndefined(rule.findFirst) ? defaultOptions.findFirst : rule.findFirst;
  var ruleErrors = {};
  var validators = [];

  if (_.isUndefined(value) && isRequired)
    return isRequired ? {isRequired: prepareMessage('required', value, rule)} : {};

  _.forOwn(defaultValidators, function (value, key) {
    if (!_.isUndefined(rule[key])) {
      validators.push([key, value]);
    }
  });

  _.forOwn(rule.custom, function (value, key) {
    if (_.isFunction(value)) {
      validators.push([key, value]);
    }
  });

  for (var i = 0; i < validators.length; i++) {
    var validatorName = validators[i][0];
    var validatorFunc = validators[i][1];
    var result = validatorFunc(value, rule);
    if (!result) {
      ruleErrors[validatorName] = prepareMessage(validatorName, value, rule);
    }
    if (_.size(ruleErrors) && isFindFirst) break;
  }

  return ruleErrors;
}

function prepareMessage(validatorName, value, rule) {
  var message;
  if (_.isObject(rule.msg) && !_.isUndefined(rule.msg[validatorName])) {
    message = rule.msg[validatorName];
  } else if (!_.isUndefined(defaultMessages[validatorName])) {
    message = defaultMessages[validatorName];
  } else {
    message = defaultMessages['default'];
  }

  if (_.isFunction(message))
    return message(value, rule);
  else if (_.isString(message))
    return message;
}

function isArrayOfTwoLength(obj) {
  return _.isArray(obj) && obj.length === 2;
}

var validateIt = function(objectToCheck, rules) {
  var errors = {};
  for (var i = 0; i < rules.length; i++) {
    var rule = rules[i];
    errors[rule.name] = validate(objectToCheck[rule.name], rule);
  }
  return errors;
};

module.exports = validateIt;<|endoftext|>


<commit_before>const TabGroup = require("electron-tabs")
const dragula = require("dragula")
const electron = require('electron')
const contextMenu = require('electron-context-menu')
                                                                                const loadJsonFile = require('load-json-file')
const jsStringEscape = require('js-string-escape')

// build toolbar

var toolbar = document.getElementsByClassName('ztoolbar')[0];
let tabGroup = new TabGroup({
  ready: function (tabGroup) {
    dragula([tabGroup.tabContainer], {
        direction: "horizontal"
    });
  }
});
var activate=false;

// config is set via IPC call
var mainConfig;

// hook up with the main process for initialization
var ipcRenderer = electron.ipcRenderer;
ipcRenderer.on('initialize', (event,config) => {
  mainConfig=config;
//  console.log("received config "+JSON.stringify(config));
  // partitionSpan.innerHTML="("+config.partition+")  ";
  // set title
  document.title = 'z-front-side'+(config.partition!=null? ' ('+jsStringEscape(config.partition)+')':'');

  // read the config
  console.log("Loading config "+config.configFile);
  loadJsonFile(config.configFile).then(config => {
    if (config.content==null) {
      config.content=[{button:"google",tabTitle:"google",url:"https://www.google.com", preload: 0 }];
    }
    // merge in main config
    mainConfig.content=config.content;
    // initialize
    initialize();
  });
});

// modal
function showModal() {
  document.getElementById('modal').style.display = "block";
}

function hideModal() {
  document.getElementById('modal').style.display = "none";
}

//
// handling of login
//

// a key identifying the current login
var loginKey;
// the main sends us a request to get login data
ipcRenderer.on('get-login-auth', (event,key,request,authInfo) => {
  loginKey = key;
  showLogin(request,authInfo);
});

function showLogin(request,authInfo) {
  showModal();
  getLoginPopup().style.display = "block";
  getLoginInfo().innerHTML=jsStringEscape(request.url);
}

function hideLogin() {
  loginKey=null;
  hideModal();
  getLoginPopup().style.display = "none";
  getLoginPasswordField().value='';
}

// to confirm login
module.exports.onLoginOk=() => {
  var userNameField = getLoginUserNameField();
  var passwordField = getLoginPasswordField();
  // send back to main
  ipcRenderer.send('set-login-auth',loginKey,userNameField.value,passwordField.value);
  // clear
  hideLogin();
};
// to cancel login
module.exports.onLoginCancel=() => {
  // ipcRenderer.send('cancel-login-auth',loginKey);
  hideLogin();
};

function getLoginPopup() {
  return document.getElementById('login_popup');
}
function getLoginInfo() {
  return document.getElementById('login_info');
}
function getLoginUserNameField() {
  return document.getElementById('login_username');
}
function getLoginPasswordField() {
  return document.getElementById('login_password');
}

// end login handling

// this only after config has been passed, i.e. when main tells us to init.
function initialize() {

  // load buttons and tabs
  var preloads={};
  for (var i in mainConfig.content) {
    var content = mainConfig.content[i];
    addButton(toolbar,content.button,createTabFunction(content));
    if (content.preload!=null) {
      preloads[content.preload]=content;
    }
  }

  // create initially loaded tabs
  var first=true;
  for (var key in Object.keys(preloads).sort()) {
    var content = preloads[key];
    if (content) {
      var tab = addTab(content.tabTitle,content.url, content.attributes);
      if (first) {
        tab.activate();
        first = false;
      }
    }
  }
  // after this tabs activate right away
  activate=true;
}

// in order to resolve the unwrap the closure!
function createTabFunction(content) {
  return ()=>{addTab(content.tabTitle,content.url, content.attributes)};
}

// -------------------- pre-init setup --------------

// the loading indicator
var loading = document.createElement('img');
loading.style.float='right';
loading.style.visibility = 'hidden';
loading.src='./loading.gif';
toolbar.appendChild(loading);

function checkLoad() {
  for (var i=0;i<tabGroup.tabs.length;i++) {
    var tab = tabGroup.tabs[i];
    if (tab.webview.isLoading()) {
      loading.style.visibility = 'visible';
      return;
    }
  }
  loading.style.visibility = 'hidden';
}

// back button

addButton(toolbar,"<<back",()=>{
  var tab = tabGroup.getActiveTab();
  if (tab!=null) {
    tab.webview.goBack();
  }
},"left");

// config button

addButton(toolbar,"cfg",()=>{
  electron.shell.openItem(mainConfig.configFile);
},"right");

// --------------------

function addButton(toolbar,title,addFn,float) {
  var button = document.createElement('BUTTON');
  button.innerHTML=title;
  button.onclick=addFn;
  if (float) {
    button.style="float:"+float;
  }
  toolbar.appendChild(button);
}
function addTab(title,src, attributes) {

  // default attributes for every inner webview:
  // the same partition (!), plugins and popups allowed.
  var wva = {
    partition:mainConfig.partition,
    plugins:true,
    allowpopups:true
  };
  // mix-in more attributes, if specified.
  if (attributes) {
    // copy
    for (var p in attributes) {
      wva[p]=attributes[p];
    }
  }

  var tab = tabGroup.addTab({
    title: title,
    src: src,
    visible: true,
    webviewAttributes: wva
  });
  if (activate) {
    tab.activate();
  }

  // allow opening links in external browser
  tab.webview.addEventListener('new-window', (e) => {
    electron.shell.openExternal(e.url);
  });
  // loading indicator
  tab.webview.addEventListener('did-start-loading', checkLoad);
  tab.webview.addEventListener('did-stop-loading', checkLoad);
  tab.webview.addEventListener('did-finish-load', checkLoad);
  tab.webview.addEventListener('did-fail-load', checkLoad);
  tab.webview.addEventListener('destroyed',checkLoad);
  tab.webview.addEventListener('crashed',checkLoad);
  tab.webview.addEventListener('close',checkLoad);
  // set context menu
  contextMenu({
    window:tab.webview,
    append: (params,BrowserWindow)=>[{
      label:'reload',
      click: ()=>{tab.webview.reload();}
    },{
      label:'copy document link',
      click: ()=>{electron.clipboard.writeText(tab.webview.src);}
    },{
      label:'open in external browser',
      visible: params.linkURL != null,
      click: ()=>{electron.shell.openExternal(params.linkURL);}
    },{
      label:'print',
      click: ()=>{tab.webview.print();}
    }]
  });
  return tab;
}

/*
const protocol = require('url').parse(e.url).protocol
if (protocol === 'http:' || protocol === 'https:') {
  shell.openExternal(e.url)
}
*/
<commit_msg>fixing preload<commit_after>const TabGroup = require("electron-tabs")
const dragula = require("dragula")
const electron = require('electron')
const contextMenu = require('electron-context-menu')
                                                                                const loadJsonFile = require('load-json-file')
const jsStringEscape = require('js-string-escape')

// build toolbar

var toolbar = document.getElementsByClassName('ztoolbar')[0];
let tabGroup = new TabGroup({
  ready: function (tabGroup) {
    dragula([tabGroup.tabContainer], {
        direction: "horizontal"
    });
  }
});
var activate=false;

// config is set via IPC call
var mainConfig;

// hook up with the main process for initialization
var ipcRenderer = electron.ipcRenderer;
ipcRenderer.on('initialize', (event,config) => {
  mainConfig=config;
//  console.log("received config "+JSON.stringify(config));
  // partitionSpan.innerHTML="("+config.partition+")  ";
  // set title
  document.title = 'z-front-side'+(config.partition!=null? ' ('+jsStringEscape(config.partition)+')':'');

  // read the config
  console.log("Loading config "+config.configFile);
  loadJsonFile(config.configFile).then(config => {
    if (config.content==null) {
      config.content=[{button:"google",tabTitle:"google",url:"https://www.google.com", preload: 0 }];
    }
    // merge in main config
    mainConfig.content=config.content;
    // initialize
    initialize();
  });
});

// modal
function showModal() {
  document.getElementById('modal').style.display = "block";
}

function hideModal() {
  document.getElementById('modal').style.display = "none";
}

//
// handling of login
//

// a key identifying the current login
var loginKey;
// the main sends us a request to get login data
ipcRenderer.on('get-login-auth', (event,key,request,authInfo) => {
  loginKey = key;
  showLogin(request,authInfo);
});

function showLogin(request,authInfo) {
  showModal();
  getLoginPopup().style.display = "block";
  getLoginInfo().innerHTML=jsStringEscape(request.url);
}

function hideLogin() {
  loginKey=null;
  hideModal();
  getLoginPopup().style.display = "none";
  getLoginPasswordField().value='';
}

// to confirm login
module.exports.onLoginOk=() => {
  var userNameField = getLoginUserNameField();
  var passwordField = getLoginPasswordField();
  // send back to main
  ipcRenderer.send('set-login-auth',loginKey,userNameField.value,passwordField.value);
  // clear
  hideLogin();
};
// to cancel login
module.exports.onLoginCancel=() => {
  // ipcRenderer.send('cancel-login-auth',loginKey);
  hideLogin();
};

function getLoginPopup() {
  return document.getElementById('login_popup');
}
function getLoginInfo() {
  return document.getElementById('login_info');
}
function getLoginUserNameField() {
  return document.getElementById('login_username');
}
function getLoginPasswordField() {
  return document.getElementById('login_password');
}

// end login handling

// this only after config has been passed, i.e. when main tells us to init.
function initialize() {

  // load buttons and tabs
  var preloads={};
  for (var i in mainConfig.content) {
    var content = mainConfig.content[i];
    addButton(toolbar,content.button,createTabFunction(content));
    if (content.preload!=null) {
      preloads[content.preload]=content;
    }
  }

  // create initially loaded tabs
  var first=true;
  var keys = Object.keys(preloads).sort();
  for (var x in keys) {
    var key = keys[x];
    var content = preloads[key];
    if (content) {
      var tab = addTab(content.tabTitle,content.url, content.attributes);
      if (first) {
        tab.activate();
        first = false;
      }
    }
  }
  // after this tabs activate right away
  activate=true;
}

// in order to resolve the unwrap the closure!
function createTabFunction(content) {
  return ()=>{addTab(content.tabTitle,content.url, content.attributes)};
}

// -------------------- pre-init setup --------------

// the loading indicator
var loading = document.createElement('img');
loading.style.float='right';
loading.style.visibility = 'hidden';
loading.src='./loading.gif';
toolbar.appendChild(loading);

function checkLoad() {
  for (var i=0;i<tabGroup.tabs.length;i++) {
    var tab = tabGroup.tabs[i];
    if (tab.webview.isLoading()) {
      loading.style.visibility = 'visible';
      return;
    }
  }
  loading.style.visibility = 'hidden';
}

// back button

addButton(toolbar,"<<back",()=>{
  var tab = tabGroup.getActiveTab();
  if (tab!=null) {
    tab.webview.goBack();
  }
},"left");

// config button

addButton(toolbar,"cfg",()=>{
  electron.shell.openItem(mainConfig.configFile);
},"right");

// --------------------

function addButton(toolbar,title,addFn,float) {
  var button = document.createElement('BUTTON');
  button.innerHTML=title;
  button.onclick=addFn;
  if (float) {
    button.style="float:"+float;
  }
  toolbar.appendChild(button);
}
function addTab(title,src, attributes) {

  // default attributes for every inner webview:
  // the same partition (!), plugins and popups allowed.
  var wva = {
    partition:mainConfig.partition,
    plugins:true,
    allowpopups:true
  };
  // mix-in more attributes, if specified.
  if (attributes) {
    // copy
    for (var p in attributes) {
      wva[p]=attributes[p];
    }
  }

  var tab = tabGroup.addTab({
    title: title,
    src: src,
    visible: true,
    webviewAttributes: wva
  });
  if (activate) {
    tab.activate();
  }

  // allow opening links in external browser
  tab.webview.addEventListener('new-window', (e) => {
    electron.shell.openExternal(e.url);
  });
  // loading indicator
  tab.webview.addEventListener('did-start-loading', checkLoad);
  tab.webview.addEventListener('did-stop-loading', checkLoad);
  tab.webview.addEventListener('did-finish-load', checkLoad);
  tab.webview.addEventListener('did-fail-load', checkLoad);
  tab.webview.addEventListener('destroyed',checkLoad);
  tab.webview.addEventListener('crashed',checkLoad);
  tab.webview.addEventListener('close',checkLoad);
  // set context menu
  contextMenu({
    window:tab.webview,
    append: (params,BrowserWindow)=>[{
      label:'reload',
      click: ()=>{tab.webview.reload();}
    },{
      label:'copy document link',
      click: ()=>{electron.clipboard.writeText(tab.webview.src);}
    },{
      label:'open in external browser',
      visible: params.linkURL != null,
      click: ()=>{electron.shell.openExternal(params.linkURL);}
    },{
      label:'print',
      click: ()=>{tab.webview.print();}
    }]
  });
  return tab;
}

/*
const protocol = require('url').parse(e.url).protocol
if (protocol === 'http:' || protocol === 'https:') {
  shell.openExternal(e.url)
}
*/
<|endoftext|>


<commit_before>const fs = require('fs')
const jsdom = require('jsdom')

let request = (url, callback = () => {}) => require('request')({
    url,
    headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.148 Safari/537.36 Vivaldi/1.4.589.38'
    }
}, (err, response, body) => {
    if (err || response.statusCode != 200)
        return callback(err || new Error('Lyrics unavailable'))

    callback(null, response, body)
})

let dom = (body, callback = () => {}) => jsdom.env(body, (err, window) => {
    if (err) return callback(err)

    let $ = x => Array.from(window.document.querySelectorAll(x))
    let text = x => x ? Array.from(x.childNodes).find(y => y.nodeName == '#text').nodeValue : null

    callback(null, window, $, text)
})

exports.get = function(query, callback = () => {}) {
    exports.searchFor(query, (err, list) => {
        if (err || list.length == 0)
            return callback(err || new Error('Nothing found'))

        exports.extractFrom(list[0].url, (err, result) => {
            if (err) return callback(err)

            callback(null, result)
        })
    })
}

exports.searchFor = function(query, callback = () => {}) {
    let url = `https://musixmatch.com/search/${encodeURI(query)}/tracks`

    request(url, (err, response, body) => {
        if (err) return callback(err)

        dom(body, (err, window, $, text) => {
            if (err) return callback(err)

            callback(null, $('.tracks.list li').map(li => li = {
                title: text(li.querySelector('.title span')),
                artist: text(li.querySelector('.artist')),
                url: `https://musixmatch.com${li.querySelector('.title').href}`,
                art: 'https:' + li.querySelector('img').srcset.split(',').splice(-1)[0].trim().split(' ')[0]
            }))
        })
    })
}

exports.extractFrom = function(url, callback = () => {}) {
    request(url, (err, response, body) => {
        if (err) return callback(err)

        dom(body, (err, window, $, text) => {
            if (err) return callback(err)

            callback(null, {
                title: text($('.mxm-track-title__track')[0]),
                artists: $('.mxm-track-title__artist').map(x => text(x)),
                album: text($('.mxm-track-footer__album h2')[0]),
                art: 'https:' + $('.banner-album-image img')[0].src,
                lyrics: $('.mxm-lyrics__content').map(x => text(x)).join('\n\n')
            })
        })
    })
}
<commit_msg>Include url in extraction info<commit_after>const fs = require('fs')
const jsdom = require('jsdom')

let request = (url, callback = () => {}) => require('request')({
    url,
    headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.148 Safari/537.36 Vivaldi/1.4.589.38'
    }
}, (err, response, body) => {
    if (err || response.statusCode != 200)
        return callback(err || new Error('Lyrics unavailable'))

    callback(null, response, body)
})

let dom = (body, callback = () => {}) => jsdom.env(body, (err, window) => {
    if (err) return callback(err)

    let $ = x => Array.from(window.document.querySelectorAll(x))
    let text = x => x ? Array.from(x.childNodes).find(y => y.nodeName == '#text').nodeValue : null

    callback(null, window, $, text)
})

exports.get = function(query, callback = () => {}) {
    exports.searchFor(query, (err, list) => {
        if (err || list.length == 0)
            return callback(err || new Error('Nothing found'))

        exports.extractFrom(list[0].url, (err, result) => {
            if (err) return callback(err)

            callback(null, result)
        })
    })
}

exports.searchFor = function(query, callback = () => {}) {
    let url = `https://musixmatch.com/search/${encodeURI(query)}/tracks`

    request(url, (err, response, body) => {
        if (err) return callback(err)

        dom(body, (err, window, $, text) => {
            if (err) return callback(err)

            callback(null, $('.tracks.list li').map(li => li = {
                title: text(li.querySelector('.title span')),
                artist: text(li.querySelector('.artist')),
                url: `https://musixmatch.com${li.querySelector('.title').href}`,
                art: 'https:' + li.querySelector('img').srcset.split(',').splice(-1)[0].trim().split(' ')[0]
            }))
        })
    })
}

exports.extractFrom = function(url, callback = () => {}) {
    request(url, (err, response, body) => {
        if (err) return callback(err)

        dom(body, (err, window, $, text) => {
            if (err) return callback(err)

            callback(null, {
                url,
                title: text($('.mxm-track-title__track')[0]),
                artists: $('.mxm-track-title__artist').map(x => text(x)),
                album: text($('.mxm-track-footer__album h2')[0]),
                art: 'https:' + $('.banner-album-image img')[0].src,
                lyrics: $('.mxm-lyrics__content').map(x => text(x)).join('\n\n')
            })
        })
    })
}
<|endoftext|>


<commit_before>// The main file that is included when you run `require("gpii-express")`.
//
// You can optionally load testing support by assigning a variable to the results of `require` and calling
// `loadTestingSupport`, as in:
//
//      var gpiiExpress = require("gpii-express");
//      gpiiExpress.loadTestingSupport();
//
"use strict";
var fluid = require("infusion");
var gpii  = fluid.registerNamespace("gpii");

var path = require("path");
var resolve = require("resolve");

/*
    Assuming we are starting in `sub-module/node_modules/gpii-express`, check for the existence of `gpii-express`
    in the directory that contains `sub-module`, i.e. `../../..`.

    Adapted from the new "dedupe" handling in infusion:

    https://github.com/fluid-project/infusion/blob/master/src/module/fluid.js
 */

var parentExpress;
try {
    var upPath = path.resolve(__dirname, "../../..");
    var parentExpressPath = resolve.sync("gpii-express", {
        basedir: upPath
    });
    parentExpress = require(parentExpressPath);
} catch (e) {
    // There will always be an exception if we are at the top level.  We choose to ignore it.
}

if (parentExpress) {
    parentExpress.log("Resolved gpii-express from path " + __dirname + " to " + parentExpress.baseDir);
    module.exports = parentExpress;
    return;
}
else {
    fluid.registerNamespace("gpii.express");
    fluid.module.register("gpii-express", __dirname, require);

    // `fluid.loadIncludes` seems to force paths relative to infusion.  TODO:  Discuss with Antranig.
    gpii.express.loadIncludes = function (includeFilePaths) {
        var includeFilePathsArray = fluid.makeArray(includeFilePaths);
        fluid.each(includeFilePathsArray, function (includeFilePath) {
            var filesToInclude = require(includeFilePath);
            fluid.each(filesToInclude, function (fileToInclude) {
                require(fileToInclude);
            });
        });
    };

    gpii.express.loadIncludes("./mainIncludes.json");

    // Provide a function to optionally load test support.
    gpii.express.loadTestingSupport = function () {
        gpii.express.loadIncludes("./testIncludes.json");
    };

    gpii.express.baseDir = __dirname;

    module.exports = gpii.express;

    console.log("Express at path " + gpii.express.baseDir + " is at top level ");
}

<commit_msg>GPII-1788:  Fixed logging for "resolve from parent" block.<commit_after>// The main file that is included when you run `require("gpii-express")`.
//
// You can optionally load testing support by assigning a variable to the results of `require` and calling
// `loadTestingSupport`, as in:
//
//      var gpiiExpress = require("gpii-express");
//      gpiiExpress.loadTestingSupport();
//
"use strict";
var fluid = require("infusion");
var gpii  = fluid.registerNamespace("gpii");

var path = require("path");
var resolve = require("resolve");

/*
    Assuming we are starting in `sub-module/node_modules/gpii-express`, check for the existence of `gpii-express`
    in the directory that contains `sub-module`, i.e. `../../..`.

    Adapted from the new "dedupe" handling in infusion:

    https://github.com/fluid-project/infusion/blob/master/src/module/fluid.js
 */

var parentExpress;
try {
    var upPath = path.resolve(__dirname, "../../..");
    var parentExpressPath = resolve.sync("gpii-express", {
        basedir: upPath
    });
    parentExpress = require(parentExpressPath);
} catch (e) {
    // There will always be an exception if we are at the top level.  We choose to ignore it.
}

if (parentExpress) {
    fluid.log("Resolved gpii-express from path " + __dirname + " to " + parentExpress.baseDir);
    module.exports = parentExpress;
    return;
}
else {
    fluid.registerNamespace("gpii.express");
    fluid.module.register("gpii-express", __dirname, require);

    // `fluid.loadIncludes` seems to force paths relative to infusion.  TODO:  Discuss with Antranig.
    gpii.express.loadIncludes = function (includeFilePaths) {
        var includeFilePathsArray = fluid.makeArray(includeFilePaths);
        fluid.each(includeFilePathsArray, function (includeFilePath) {
            var filesToInclude = require(includeFilePath);
            fluid.each(filesToInclude, function (fileToInclude) {
                require(fileToInclude);
            });
        });
    };

    gpii.express.loadIncludes("./mainIncludes.json");

    // Provide a function to optionally load test support.
    gpii.express.loadTestingSupport = function () {
        gpii.express.loadIncludes("./testIncludes.json");
    };

    gpii.express.baseDir = __dirname;

    module.exports = gpii.express;

    fluid.log("Express at path " + gpii.express.baseDir + " is at top level ");
}

<|endoftext|>


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

'use strict';

var debug = require('debug')('common-middleware');
var utils = require('./utils');

function middleware(options) {
  options = options || {};

  return function plugin(app) {
    if (!utils.isValid(app, 'common-middleware')) return;
    debug('initializing from <%s>', __filename);

    // we'll assume none of the stream handlers exist if `onStream` is not registered
    if (typeof this.onStream !== 'function') {
      this.handler('onStream');
      this.handler('preWrite');
      this.handler('postWrite');
    }

    var opts = utils.merge({}, this.options, options);
    var jsonRegex = opts.jsonRegex || /\.(json|jshintrc)$/;
    var escapeRegex = opts.escapeRegex || '*';
    var extRegex = opts.extRegex || '*';

    /**
     * Parses front-matter on files that match `options.extRegex` and
     * adds the resulting data object to `file.data`. This object is
     * passed as context to the template engine at render time.
     *
     * @name front matter
     * @api public
     */

    this.onLoad(extRegex, utils.mu.series([
      isHandled,
      isBinary,
      utils.matter.parse,
      escape,
      stripPrefixes,
      utils.file.renameFile(this)
    ]));

    this.onLoad(jsonRegex, parseJson);

    /**
     * Registers a `.preWrite` middleware for unescaping escaped delimiters.
     *
     * @name unescape templates
     * @api public
     */

    this.postRender(escapeRegex, utils.mu.series([
      utils.matter.stringify,
      unescape
    ]));

    this.preWrite(escapeRegex, utils.mu.series([
      utils.matter.stringify,
      unescape
    ]));

    this.preWrite(jsonRegex, updateJson);
    return plugin;
  };
};

function isHandled(file, next) {
  file.isHandled = function(name, stage) {
    var current = this.options.method;
    this.handled = this.handled || {};
    if (this.handled.hasOwnProperty(name)) {
      if (typeof stage !== 'string') return true;
      if (this.handled[name].indexOf(stage) !== -1 && stage === current) {
        return true;
      }
    }

    this.handled[name] = this.handled[name] || [];
    if (typeof stage === 'string') {
      this.handled[name].push(stage);
    }
    return false;
  };
  next(null, file);
}

/**
 * strip prefixes from dotfile and config templates
 */

function stripPrefixes(file, next) {
  if (!/templates/.test(file.dirname)) {
    next(null, file);
    return;
  }

  if (file.has('action.stripPrefixes')) {
    next(null, file);
    return;
  }

  file.set('action.stripPrefixes', true);
  file.basename = file.basename.replace(/^_/, '.');
  file.basename = file.basename.replace(/^\$/, '');
  next(null, file);
}

/**
 * Uses C-style macros to escape templates with `{%%= foo %}` or
 * `<%%= foo %>` syntax, so they will not be evaluated by a template
 * engine when `.render` is called.
 *
 * @name escape templates
 * @api public
 */

function escape(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  if (file.has('action.escaped')) {
    next(null, file);
    return;
  }

  file.set('action.escaped', true);
  var str = file.contents.toString();
  str = str.replace(/([{<])(%%[=-]?)/g, '__ESC_$1DELIM$2__');
  file.contents = new Buffer(str);
  next(null, file);
}

/**
 * Expose `unescape`
 */

function unescape(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  if (!file.has('action.escaped')) {
    next(null, file);
    return;
  }

  file.set('action.escaped', false);
  var str = file.contents.toString();
  str = str.replace(/__ESC_([{<])DELIM%(%[=-]?)__/g, '$1$2');
  file.contents = new Buffer(str);
  next();
}

/**
 * Adds a `json` property to the `file` object when the file extension
 * matches `options.jsonRegex`. This allows JSON files to be updated
 * by other middleware or pipeline plugins without having to parse and
 * stringify with each modification.
 *
 * @name JSON on-load
 * @api public
 */

function parseJson(file, next) {
  var str = file.contents.toString();
  utils.define(file, 'originalContent', str);
  var json;

  Object.defineProperty(file, 'json', {
    configurable: true,
    enumerable: true,
    set: function(val) {
      json = val;
    },
    get: function() {
      return json || (json = JSON.parse(str));
    }
  });

  next(null, file);
}

/**
 * If `file.contents` has not already been updated directly, the `file.contents` property
 * is updated with stringified JSON before writing the file back to the file
 * system.
 *
 * @name JSON pre-write
 * @api public
 */

function updateJson(file, next) {
  if (file.contents.toString() !== file.originalContent) {
    next(null, file);
    return;
  }
  if (!utils.isObject(file.json)) {
    next(null, file);
    return;
  }
  file.contents = new Buffer(JSON.stringify(file.json, null, 2) + '\n');
  next(null, file);
}

function isBinary(file, next) {
  file.isBinary = function() {
    if (this.hasOwnProperty('_isBinary')) {
      return this._isBinary;
    }

    if (typeof this.isStream === 'function' && this.isStream()) {
      this._isBinary = false;
      return false;
    }
    if (typeof this.isDirectory === 'function' && this.isDirectory()) {
      this._isBinary = false;
      return false;
    }
    if (this.isNull()) {
      this._isBinary = false;
      return false;
    }

    var len = this.stat ? this.stat.size : this.contents.length;
    this._isBinary = utils.isBinary.sync(this.contents, len);
    return this._isBinary;
  };
  next(null, file);
}

middleware.unescape = unescape;
module.exports = middleware;
<commit_msg>don't parse if not a string<commit_after>/*!
 * common-middleware <https://github.com/jonschlinkert/common-middleware>
 *
 * Copyright (c) 2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

'use strict';

var debug = require('debug')('common-middleware');
var utils = require('./utils');

function middleware(options) {
  options = options || {};

  return function plugin(app) {
    if (!utils.isValid(app, 'common-middleware')) return;
    debug('initializing from <%s>', __filename);

    // we'll assume none of the stream handlers exist if `onStream` is not registered
    if (typeof this.onStream !== 'function') {
      this.handler('onStream');
      this.handler('preWrite');
      this.handler('postWrite');
    }

    var opts = utils.merge({}, this.options, options);
    var jsonRegex = opts.jsonRegex || /\.(json|jshintrc)$/;
    var escapeRegex = opts.escapeRegex || '*';
    var extRegex = opts.extRegex || '*';

    /**
     * Parses front-matter on files that match `options.extRegex` and
     * adds the resulting data object to `file.data`. This object is
     * passed as context to the template engine at render time.
     *
     * @name front matter
     * @api public
     */

    this.onLoad(extRegex, utils.mu.series([
      isHandled,
      isBinary,
      utils.matter.parse,
      escape,
      stripPrefixes,
      utils.file.renameFile(this)
    ]));

    this.onLoad(jsonRegex, parseJson);

    /**
     * Registers a `.preWrite` middleware for unescaping escaped delimiters.
     *
     * @name unescape templates
     * @api public
     */

    this.postRender(escapeRegex, utils.mu.series([
      utils.matter.stringify,
      unescape
    ]));

    this.preWrite(escapeRegex, utils.mu.series([
      utils.matter.stringify,
      unescape
    ]));

    this.preWrite(jsonRegex, updateJson);
    return plugin;
  };
};

function isHandled(file, next) {
  file.isHandled = function(name, stage) {
    var current = this.options.method;
    this.handled = this.handled || {};
    if (this.handled.hasOwnProperty(name)) {
      if (typeof stage !== 'string') return true;
      if (this.handled[name].indexOf(stage) !== -1 && stage === current) {
        return true;
      }
    }

    this.handled[name] = this.handled[name] || [];
    if (typeof stage === 'string') {
      this.handled[name].push(stage);
    }
    return false;
  };
  next(null, file);
}

/**
 * strip prefixes from dotfile and config templates
 */

function stripPrefixes(file, next) {
  if (!/templates/.test(file.dirname)) {
    next(null, file);
    return;
  }

  if (file.has('action.stripPrefixes')) {
    next(null, file);
    return;
  }

  file.set('action.stripPrefixes', true);
  file.basename = file.basename.replace(/^_/, '.');
  file.basename = file.basename.replace(/^\$/, '');
  next(null, file);
}

/**
 * Uses C-style macros to escape templates with `{%%= foo %}` or
 * `<%%= foo %>` syntax, so they will not be evaluated by a template
 * engine when `.render` is called.
 *
 * @name escape templates
 * @api public
 */

function escape(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  if (file.has('action.escaped')) {
    next(null, file);
    return;
  }

  file.set('action.escaped', true);
  var str = file.contents.toString();
  str = str.replace(/([{<])(%%[=-]?)/g, '__ESC_$1DELIM$2__');
  file.contents = new Buffer(str);
  next(null, file);
}

/**
 * Expose `unescape`
 */

function unescape(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  if (!file.has('action.escaped')) {
    next(null, file);
    return;
  }

  file.set('action.escaped', false);
  var str = file.contents.toString();
  str = str.replace(/__ESC_([{<])DELIM%(%[=-]?)__/g, '$1$2');
  file.contents = new Buffer(str);
  next();
}

/**
 * Adds a `json` property to the `file` object when the file extension
 * matches `options.jsonRegex`. This allows JSON files to be updated
 * by other middleware or pipeline plugins without having to parse and
 * stringify with each modification.
 *
 * @name JSON on-load
 * @api public
 */

function parseJson(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  var str = file.contents.toString();
  utils.define(file, 'originalContent', str);
  var json;

  Object.defineProperty(file, 'json', {
    configurable: true,
    enumerable: true,
    set: function(val) {
      json = val;
    },
    get: function() {
      return json || (json = JSON.parse(str));
    }
  });

  next(null, file);
}

/**
 * If `file.contents` has not already been updated directly, the `file.contents` property
 * is updated with stringified JSON before writing the file back to the file
 * system.
 *
 * @name JSON pre-write
 * @api public
 */

function updateJson(file, next) {
  if (file.isNull() || file.isBinary()) {
    next(null, file);
    return;
  }

  if (file.contents.toString() !== file.originalContent) {
    next(null, file);
    return;
  }
  if (!utils.isObject(file.json)) {
    next(null, file);
    return;
  }
  file.contents = new Buffer(JSON.stringify(file.json, null, 2) + '\n');
  next(null, file);
}

function isBinary(file, next) {
  file.isBinary = function() {
    if (this.hasOwnProperty('_isBinary')) {
      return this._isBinary;
    }

    if (typeof this.isStream === 'function' && this.isStream()) {
      this._isBinary = false;
      return false;
    }
    if (typeof this.isDirectory === 'function' && this.isDirectory()) {
      this._isBinary = false;
      return false;
    }
    if (this.isNull()) {
      this._isBinary = false;
      return false;
    }

    var exts = ['.png', '.jpg', '.pdf', '.gif'];
    if (exts.indexOf(this.extname) !== -1) {
      this._isBinary = true;
      return true;
    }

    var len = this.stat ? this.stat.size : this.contents.length;
    this._isBinary = utils.isBinary.sync(this.contents, len);
    return this._isBinary;
  };
  next(null, file);
}

middleware.unescape = unescape;
module.exports = middleware;
<|endoftext|>


<commit_before>/* global FileList */

module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var path = require('path')
var Peer = require('simple-peer')
var randombytes = require('randombytes')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

/**
 * WebTorrent version.
 */
var VERSION = require('./package.json').version

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g)
  .slice(0, 2)
  .map(function (v) { return zeroFill(2, v) })
  .join('')

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object=} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  EventEmitter.call(self)

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + randombytes(6).toString('hex')).toString('hex')
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = randombytes(20).toString('hex')
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc // to support `webtorrent-hybrid` package
    }
    if (global.WRTC && !self.tracker.wrtc) self.tracker.wrtc = global.WRTC
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))

    self.dht.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

  debug('new webtorrent (peerId %s, nodeId %s)', self.peerId, self.nodeId)

  if (typeof loadIPSet === 'function' && opts.blocklist != null) {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://webtorrent.io)'
      }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else {
    process.nextTick(ready)
  }

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

    if (!parsed) return null
    if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent.infoHash === parsed.infoHash) return torrent
    }
  }
  return null
}

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)

  debug('seed')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR
  if (!self.tracker) opts.announce = []

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (isFileList(input)) input = Array.prototype.slice.call(input)
  if (!Array.isArray(input)) input = [ input ]

  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  if (this.destroyed) throw new Error('client already destroyed')
  this._destroy(null, cb)
}

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      torrent.destroy(cb)
    }
  })

  if (self._tcpPool) {
    tasks.push(function (cb) {
      self._tcpPool.destroy(cb)
    })
  }

  if (self.dht) {
    tasks.push(function (cb) {
      self.dht.destroy(cb)
    })
  }

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}

/**
 * Check if `obj` is a W3C `FileList` object
 * @param  {*} obj
 * @return {boolean}
 */
function isFileList (obj) {
  return typeof FileList !== 'undefined' && obj instanceof FileList
}
<commit_msg>print webtorrent debug message in right place<commit_after>/* global FileList */

module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var path = require('path')
var Peer = require('simple-peer')
var randombytes = require('randombytes')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

/**
 * WebTorrent version.
 */
var VERSION = require('./package.json').version

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g)
  .slice(0, 2)
  .map(function (v) { return zeroFill(2, v) })
  .join('')

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object=} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  EventEmitter.call(self)

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + randombytes(6).toString('hex')).toString('hex')
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = randombytes(20).toString('hex')
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  debug(
    'new webtorrent (peerId %s, nodeId %s, port %s)',
    self.peerId, self.nodeId, self.torrentPort
  )

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc // to support `webtorrent-hybrid` package
    }
    if (global.WRTC && !self.tracker.wrtc) self.tracker.wrtc = global.WRTC
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))

    self.dht.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

  if (typeof loadIPSet === 'function' && opts.blocklist != null) {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://webtorrent.io)'
      }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else {
    process.nextTick(ready)
  }

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

    if (!parsed) return null
    if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent.infoHash === parsed.infoHash) return torrent
    }
  }
  return null
}

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)

  debug('seed')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR
  if (!self.tracker) opts.announce = []

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (isFileList(input)) input = Array.prototype.slice.call(input)
  if (!Array.isArray(input)) input = [ input ]

  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  if (this.destroyed) throw new Error('client already destroyed')
  this._destroy(null, cb)
}

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      torrent.destroy(cb)
    }
  })

  if (self._tcpPool) {
    tasks.push(function (cb) {
      self._tcpPool.destroy(cb)
    })
  }

  if (self.dht) {
    tasks.push(function (cb) {
      self.dht.destroy(cb)
    })
  }

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}

/**
 * Check if `obj` is a W3C `FileList` object
 * @param  {*} obj
 * @return {boolean}
 */
function isFileList (obj) {
  return typeof FileList !== 'undefined' && obj instanceof FileList
}
<|endoftext|>


<commit_before>require('./drpx-id.js');
module.exports = angular.module('drpxId');<commit_msg>fix module.exports to satisfy angular standards<commit_after>require('./drpx-id.js');
module.exports = 'drpxId';<|endoftext|>