<commit_before>/**
 * @module driveshare-gui/client
 */

'use strict';

var $ = window.jQuery = require('jquery');
var Vue = require('vue');

require('bootstrap'); // init bootstrap js

var pkginfo = require('./package');
var utils = require('./lib/utils');
var helpers = require('./lib/helpers');
var remote = require('remote');
var app = remote.require('app');
var ipc = require('electron').ipcRenderer;
var shell = require('shell');
var about = require('./package');
var Updater = require('./lib/updater');
var UserData = require('./lib/userdata');
var Tab = require('./lib/tab');
var diskspace = require('./lib/diskspace');
var FarmerFactory = require('storj-farmer').FarmerFactory;
var request = require('request');
var SpeedTest = require('speedofme').Client;
var userdata = new UserData(app.getPath('userData'));

// bootstrap helpers
helpers.ExternalLinkListener().bind(document);

/**
 * About View
 */
var about = new Vue({
  el: '#about',
  data: {
    version: about.version,
    protocol: require('storj-farmer/node_modules/storj').version
  },
  methods: {
    show: function(event) {
      if (event) {
        event.preventDefault();
      }

      $('#about').modal('show');
    }
  },
  created: function() {
    var view = this;

    ipc.on('showAboutDialog', function() {
      view.show();
    });
  }
});

/**
 * Updater View
 */
var updater = new Vue({
  el: '#updater',
  data: {
    update: false,
    releaseURL: '',
    releaseTag: ''
  },
  methods: {
    download: function(event) {
      if (event) {
        event.preventDefault();
      }

      shell.openExternal(this.releaseURL);
    }
  },
  created: function() {
    var view = this;
    var updater = new Updater();

    ipc.on('checkForUpdates', function() {
      updater.check();
      $('#updater').modal('show');
    });

    updater.check();

    updater.on('update_available', function(meta) {
      view.update = true;
      view.releaseTag = meta.releaseTag;
      view.releaseURL = meta.releaseURL;

      $('#updater').modal('show');
    });

    updater.on('error', function(err) {
      console.log(err);
    });
  }
});

/**
 * App Settings View
 */
var appSettings = new Vue({
  el:'#app-settings',
  data: {
    userdata: userdata._parsed
  },
  methods: {
    changeSettings: function() {
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    }
  },
  ready: function() {
    var self = this;
    //check for OS-specific boot launch option
    ipc.send('checkBootSettings');
    ipc.on('checkAutoLaunchOptions', function(ev, isEnabled) {
      self.userdata.appSettings.launchOnBoot = isEnabled;
      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    });
    //remove default bootstrap UI dropdown close behavior
    $('#app-settings > .dropdown-menu input,' +
      '#app-settings > .dropdown-menu label')
      .on('click', function(e) {
        e.stopPropagation();
      }
    );
  },
  beforeDestroy: function() {
    $('#app-settings > .dropdown-menu input,' +
      '#app-settings > .dropdown-menu label').off('click');
  }
});

/**
 * Main View
 */
var main = new Vue({
  el: '#main',
  data: {
    userdata: userdata._parsed,
    current: 0,
    transitioning: false,
    freespace: '',
    balance: {
      sjcx: 0,
      sjct: 0,
      qualified: false
    },
    logwindow: '',
    telemetry: {},
    telemetryWarningDismissed: localStorage.getItem('telemetryWarningDismissed')
  },
  methods: {
    dismissTelemetryWarning: function() {
      this.telemetryWarningDismissed = true;
      localStorage.setItem('telemetryWarningDismissed', true);
    },
    scrollLogs: function() {
      var self = this;

      setInterval(function() {
        var logs = document.getElementById('logs');

        if (!logs) {
          return;
        }

        var end = logs.scrollHeight - logs.clientHeight <= logs.scrollTop + 1;

        self.logwindow = self.userdata.tabs[self.current].logs._output;

        if (end) {
          setTimeout(function() {
            logs.scrollTop = logs.scrollHeight - logs.clientHeight;
          }, 10);
        }
      }, 250);
    },
    addTab: function(event) {
      if (event) {
        event.preventDefault();
      }
      this.showTab(this.userdata.tabs.push(new Tab()) - 1);
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
    },
    showTab: function(index) {
      var self = this;

      createTabIfNoneFound();
      setPreviousTabToInactive();

      self.transitioning = false;

      function createTabIfNoneFound(){
        if (!self.userdata.tabs[index]) {
          return self.addTab();
        }
      }

      function setPreviousTabToInactive(){
        if (self.userdata.tabs[self.current]) {
          self.userdata.tabs[self.current].active = false;
        }
      }

      if (index === -1) {
        this.current = 0;

        if (!this.userdata.tabs[this.current]) {
          this.addTab();
        }
      } else {
        this.current = index;
        this.userdata.tabs[this.current].active = true;
      }

      this.getFreeSpace();
      this.getBalance();
    },
    removeTab: function(event) {
      if (event) {
        event.stopPropagation();
      }

      if (!window.confirm('Are you sure you want to remove this drive?')) {
        return;
      }

      this.stopFarming();
      this.userdata.tabs.splice(this.current, 1);
      this.showTab((this.current - 1) === -1 ? 0 : this.current - 1);

      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    },
    selectStorageDirectory: function() {
      ipc.send('selectStorageDirectory');
    },
    startFarming: function(event, index) {
      var self = this;
      var current = (index) ? index : this.current;
      var tab = this.userdata.tabs[current];

      if (event) {
        event.preventDefault();
      }

      try {
        userdata.validate(current);
      } catch(err) {
        return window.alert(err.message);
      }

      this.transitioning = true;

      tab.telemetry = { enabled: self.userdata.appSettings.reportTelemetry };

      FarmerFactory.create(tab, function(err, farmer) {
        if (err) {
          return window.alert(err.message);
        }

        tab.farmer = function() {
          return farmer.node;
        };

        tab.reporter = function() {
          return farmer.reporter;
        };

        farmer.logger.on('log', function(data) {
          tab.logs.append(
            '
{' + data.type + '} ' +
            '[' + data.timestamp + ']
' +
            '
' + data.message + '
'
          );
        });

        tab.wasRunning = true;
        ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));

        if (self.userdata.appSettings.reportTelemetry) {
          self.startReportingTelemetry(tab);
        }

        userdata.saveConfig(function(err) {
          if (err) {
            self.transitioning = false;
            return window.alert(err.message);
          }

          farmer.node.join(function(err) {
            self.transitioning = false;

            if (err) {
              return window.alert(err.message);
            }
          });
        });
      });
    },
    stopFarming: function(event) {
      var self = this;

      if (event) {
        event.preventDefault();
      }

      var tab = this.userdata.tabs[this.current];

      if (tab.farmer) {
        tab.wasRunning = false;
        tab.farmer().leave();

        if (self.userdata.appSettings.reportTelemetry) {
          self.stopReportingTelemetry(tab);
        }

        tab.farmer = null;
      }

      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));

      userdata.saveConfig(function(err) {
        self.transitioning = false;

        if (err) {
          return window.alert(err.message);
        }
      });
    },
    startReportingTelemetry: function(tab) {
      var farmer = tab.farmer();
      var id = farmer._contact.nodeID;
      var reporter = tab.reporter();

      if (this.telemetry[id]) {
        clearInterval(this.telemetry[id]);
      }

      function report() {
        var bandwidth = localStorage.getItem('telemetry_speedtest');
        var needstest = false;
        var hours25 = 60 * 60 * 25 * 1000;

        function send() {
          utils.getDirectorySize(tab.storage.path, function(err, size) {
            if (err) {
              return console.error('Failed to collect telemetry data');
            }

            reporter.send({
              storage: {
                free: tab.storage.size,
                used: size
              },
              bandwidth: {
                upload: bandwidth.upload,
                download: bandwidth.download
              },
              contact: farmer._contact,
              payment: tab.address
            }, function(err, result) {
              console.log('[telemetry]', err, result);
            });
          });
        }

        if (!bandwidth) {
          needstest = true;
        } else {
          bandwidth = JSON.parse(bandwidth);

          if ((new Date() - new Date(bandwidth.timestamp)) > hours25) {
            needstest = true;
          }
        }

        if (needstest && pkginfo.config.speedTestURL) {
          SpeedTest({
            url: pkginfo.config.speedTestURL
          }).test(function(err, result) {
            if (err) {
              return console.error('[telemetry]', err);
            }

            bandwidth = {
              upload: result.upload,
              download: result.download,
              timestamp: Date.now()
            };

            localStorage.setItem(
              'telemetry_speedtest',
              JSON.stringify(bandwidth)
            );

            send();
          });
        } else {
          send();
        }
      }

      this.telemetry[id] = setInterval(report, 5 * (60 * 1000));

      report();
    },
    stopReportingTelemetry: function(tab) {
      var farmer = tab.farmer();
      var id = farmer._contact.nodeID;

      if (this.telemetry[id]) {
        clearInterval(this.telemetry[id]);
        this.telemetry[id] = null;
      }
    },
    getFreeSpace: function() {
      var self = this;
      var tab = this.userdata.tabs[this.current];
      var freespace = 0;

      this.freespace = '...';

      diskspace.check(tab.storage.path, function(err, total, free) {
        if (err) {
          self.freespace = 'Free Space: ?';
          return;
        }

        switch (tab.storage.unit) {
          case 'MB':
            freespace = (free * 1e-6).toFixed(0);
            break;
          case 'GB':
            freespace = (free * 1e-9).toFixed(1);
            break;
          case 'TB':
            freespace = (free * 1e-12).toFixed(2);
            break;
        }

        self.freespace = 'Free Space: ' + freespace + ' ' + tab.storage.unit;
      });
    },
    getBalance: function() {
      var self = this;
      var tab = this.userdata.tabs[this.current];

      this.balance.sjcx = 0;
      this.balance.sjct = 0;

      if (!tab.address) {
        this.balance.qualified = false;
        return;
      }

      var url = 'http://xcp.blockscan.com/api2';
      var assets = ['SJCX', 'SJCT'];
      var query = {
        module: 'address',
        action: 'balance',
        btc_address: tab.address
      };

      assets.forEach(function(asset) {
        query.asset = asset;

        request({ url: url, qs: query, json: true }, function(err, res, body) {
          if (err || res.statusCode !== 200) {
            return;
          }

          self.balance.qualified = true;

          if (body && body.data.length) {
            self.balance[asset.toLowerCase()] = body.data[0].balance;
          }
        });
      });
    }
  },
  created: function() {
    var self = this;
    $('.container').addClass('visible');

    if (!this.userdata.tabs.length) {
      this.addTab();
    } else {
      handleRunDrivesOnBoot(this.userdata.appSettings.runDrivesOnBoot);
    }

    this.scrollLogs();

    function handleRunDrivesOnBoot(isEnabled){
      //iterate over drives and run or iterate over and remove flags
      self.userdata.tabs.forEach(function(tab, index) {
        if (tab.wasRunning && isEnabled) {
          self.startFarming(null, index);
        } else if(tab.wasRunning && !isEnabled){
          tab.wasRunning = false;
        }
      });

      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    }

    this.showTab(this.current);

    ipc.on('selectDriveFromSysTray', function(ev, tabIndex){
      self.showTab(tabIndex);
    });

    ipc.on('storageDirectorySelected', function(ev, path) {
      self.userdata.tabs[self.current].storage.path = path[0];
      self.getFreeSpace();
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
    });

    ipc.on('toggleFarmer', function() {
      var isRunning = !!self.userdata.tabs[self.current].farmer;

      if (isRunning) {
        self.stopFarming();
      } else {
        self.startFarming();
      }
    });
  }
});

/**
 * Footer View
 */
var footer = new Vue({
  el: '#footer',
  data: {},
  methods: {

  }
});

/**
 * Expose view objects
 * #exports
 */
module.exports = {
  updater: updater,
  about: about,
  appSettings: appSettings,
  main: main,
  footer: footer
};
<commit_msg>add bandwidth test reporter - needs service url in package.json<commit_after>/**
 * @module driveshare-gui/client
 */

'use strict';

var $ = window.jQuery = require('jquery');
var Vue = require('vue');

require('bootstrap'); // init bootstrap js

var pkginfo = require('./package');
var utils = require('./lib/utils');
var helpers = require('./lib/helpers');
var remote = require('remote');
var app = remote.require('app');
var ipc = require('electron').ipcRenderer;
var shell = require('shell');
var about = require('./package');
var Updater = require('./lib/updater');
var UserData = require('./lib/userdata');
var Tab = require('./lib/tab');
var diskspace = require('./lib/diskspace');
var FarmerFactory = require('storj-farmer').FarmerFactory;
var request = require('request');
var SpeedTest = require('speedofme').Client;
var userdata = new UserData(app.getPath('userData'));

// bootstrap helpers
helpers.ExternalLinkListener().bind(document);

/**
 * About View
 */
var about = new Vue({
  el: '#about',
  data: {
    version: about.version,
    protocol: require('storj-farmer/node_modules/storj').version
  },
  methods: {
    show: function(event) {
      if (event) {
        event.preventDefault();
      }

      $('#about').modal('show');
    }
  },
  created: function() {
    var view = this;

    ipc.on('showAboutDialog', function() {
      view.show();
    });
  }
});

/**
 * Updater View
 */
var updater = new Vue({
  el: '#updater',
  data: {
    update: false,
    releaseURL: '',
    releaseTag: ''
  },
  methods: {
    download: function(event) {
      if (event) {
        event.preventDefault();
      }

      shell.openExternal(this.releaseURL);
    }
  },
  created: function() {
    var view = this;
    var updater = new Updater();

    ipc.on('checkForUpdates', function() {
      updater.check();
      $('#updater').modal('show');
    });

    updater.check();

    updater.on('update_available', function(meta) {
      view.update = true;
      view.releaseTag = meta.releaseTag;
      view.releaseURL = meta.releaseURL;

      $('#updater').modal('show');
    });

    updater.on('error', function(err) {
      console.log(err);
    });
  }
});

/**
 * App Settings View
 */
var appSettings = new Vue({
  el:'#app-settings',
  data: {
    userdata: userdata._parsed
  },
  methods: {
    changeSettings: function() {
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    }
  },
  ready: function() {
    var self = this;
    //check for OS-specific boot launch option
    ipc.send('checkBootSettings');
    ipc.on('checkAutoLaunchOptions', function(ev, isEnabled) {
      self.userdata.appSettings.launchOnBoot = isEnabled;
      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    });
    //remove default bootstrap UI dropdown close behavior
    $('#app-settings > .dropdown-menu input,' +
      '#app-settings > .dropdown-menu label')
      .on('click', function(e) {
        e.stopPropagation();
      }
    );
  },
  beforeDestroy: function() {
    $('#app-settings > .dropdown-menu input,' +
      '#app-settings > .dropdown-menu label').off('click');
  }
});

/**
 * Main View
 */
var main = new Vue({
  el: '#main',
  data: {
    userdata: userdata._parsed,
    current: 0,
    transitioning: false,
    freespace: '',
    balance: {
      sjcx: 0,
      sjct: 0,
      qualified: false
    },
    logwindow: '',
    telemetry: {},
    telemetryWarningDismissed: localStorage.getItem('telemetryWarningDismissed')
  },
  methods: {
    dismissTelemetryWarning: function() {
      this.telemetryWarningDismissed = true;
      localStorage.setItem('telemetryWarningDismissed', true);
    },
    scrollLogs: function() {
      var self = this;

      setInterval(function() {
        var logs = document.getElementById('logs');

        if (!logs) {
          return;
        }

        var end = logs.scrollHeight - logs.clientHeight <= logs.scrollTop + 1;

        self.logwindow = self.userdata.tabs[self.current].logs._output;

        if (end) {
          setTimeout(function() {
            logs.scrollTop = logs.scrollHeight - logs.clientHeight;
          }, 10);
        }
      }, 250);
    },
    addTab: function(event) {
      if (event) {
        event.preventDefault();
      }
      this.showTab(this.userdata.tabs.push(new Tab()) - 1);
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
    },
    showTab: function(index) {
      var self = this;

      createTabIfNoneFound();
      setPreviousTabToInactive();

      self.transitioning = false;

      function createTabIfNoneFound(){
        if (!self.userdata.tabs[index]) {
          return self.addTab();
        }
      }

      function setPreviousTabToInactive(){
        if (self.userdata.tabs[self.current]) {
          self.userdata.tabs[self.current].active = false;
        }
      }

      if (index === -1) {
        this.current = 0;

        if (!this.userdata.tabs[this.current]) {
          this.addTab();
        }
      } else {
        this.current = index;
        this.userdata.tabs[this.current].active = true;
      }

      this.getFreeSpace();
      this.getBalance();
    },
    removeTab: function(event) {
      if (event) {
        event.stopPropagation();
      }

      if (!window.confirm('Are you sure you want to remove this drive?')) {
        return;
      }

      this.stopFarming();
      this.userdata.tabs.splice(this.current, 1);
      this.showTab((this.current - 1) === -1 ? 0 : this.current - 1);

      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    },
    selectStorageDirectory: function() {
      ipc.send('selectStorageDirectory');
    },
    startFarming: function(event, index) {
      var self = this;
      var current = (index) ? index : this.current;
      var tab = this.userdata.tabs[current];

      if (event) {
        event.preventDefault();
      }

      try {
        userdata.validate(current);
      } catch(err) {
        return window.alert(err.message);
      }

      this.transitioning = true;

      tab.telemetry = { enabled: self.userdata.appSettings.reportTelemetry };

      FarmerFactory.create(tab, function(err, farmer) {
        if (err) {
          return window.alert(err.message);
        }

        tab.farmer = function() {
          return farmer.node;
        };

        tab.reporter = function() {
          return farmer.reporter;
        };

        farmer.logger.on('log', function(data) {
          tab.logs.append(
            '
{' + data.type + '} ' +
            '[' + data.timestamp + ']
' +
            '
' + data.message + '
'
          );
        });

        tab.wasRunning = true;
        ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));

        if (self.userdata.appSettings.reportTelemetry) {
          self.startReportingTelemetry(tab);
        }

        userdata.saveConfig(function(err) {
          if (err) {
            self.transitioning = false;
            return window.alert(err.message);
          }

          farmer.node.join(function(err) {
            self.transitioning = false;

            if (err) {
              return window.alert(err.message);
            }
          });
        });
      });
    },
    stopFarming: function(event) {
      var self = this;

      if (event) {
        event.preventDefault();
      }

      var tab = this.userdata.tabs[this.current];

      if (tab.farmer) {
        tab.wasRunning = false;
        tab.farmer().leave();

        if (self.userdata.appSettings.reportTelemetry) {
          self.stopReportingTelemetry(tab);
        }

        tab.farmer = null;
      }

      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));

      userdata.saveConfig(function(err) {
        self.transitioning = false;

        if (err) {
          return window.alert(err.message);
        }
      });
    },
    startReportingTelemetry: function(tab) {
      var farmer = tab.farmer();
      var id = farmer._contact.nodeID;
      var reporter = tab.reporter();

      if (this.telemetry[id]) {
        clearInterval(this.telemetry[id]);
      }

      function report() {
        var bandwidth = localStorage.getItem('telemetry_speedtest');
        var needstest = false;
        var hours25 = 60 * 60 * 25 * 1000;

        function send() {
          utils.getDirectorySize(tab.storage.path, function(err, size) {
            if (err) {
              return console.error('Failed to collect telemetry data');
            }

            var totalSpace = Number(tab.storage.size);

            switch (tab.storage.unit) {
              case 'MB':
                totalSpace = totalSpace * Math.pow(1024, 2);
                break;
              case 'GB':
                totalSpace = totalSpace * Math.pow(1024, 3);
                break;
              case 'TB':
                totalSpace = totalSpace * Math.pow(1024, 4);
                break;
              default:
                // NOOP
            }

            var report = {
              storage: {
                free: totalSpace - size,
                used: size
              },
              bandwidth: {
                upload: Number(bandwidth.upload),
                download: Number(bandwidth.download)
              },
              contact: farmer._contact,
              payment: tab.address
            };

            console.log('[telemetry] sending report', report);
            reporter.send(report, function(err, result) {
              console.log('[telemetry]', err, result);
            });
          });
        }

        if (!bandwidth) {
          needstest = true;
        } else {
          bandwidth = JSON.parse(bandwidth);

          if ((new Date() - new Date(bandwidth.timestamp)) > hours25) {
            needstest = true;
          }
        }

        if (needstest && pkginfo.config.speedTestURL) {
          SpeedTest({
            url: pkginfo.config.speedTestURL
          }).test(function(err, result) {
            if (err) {
              return console.error('[telemetry]', err);
            }

            bandwidth = {
              upload: result.upload,
              download: result.download,
              timestamp: Date.now()
            };

            localStorage.setItem(
              'telemetry_speedtest',
              JSON.stringify(bandwidth)
            );

            send();
          });
        } else {
          send();
        }
      }

      this.telemetry[id] = setInterval(report, 5 * (60 * 1000));

      report();
    },
    stopReportingTelemetry: function(tab) {
      var farmer = tab.farmer();
      var id = farmer._contact.nodeID;

      if (this.telemetry[id]) {
        clearInterval(this.telemetry[id]);
        this.telemetry[id] = null;
      }
    },
    getFreeSpace: function() {
      var self = this;
      var tab = this.userdata.tabs[this.current];
      var freespace = 0;

      this.freespace = '...';

      diskspace.check(tab.storage.path, function(err, total, free) {
        if (err) {
          self.freespace = 'Free Space: ?';
          return;
        }

        switch (tab.storage.unit) {
          case 'MB':
            freespace = (free * 1e-6).toFixed(0);
            break;
          case 'GB':
            freespace = (free * 1e-9).toFixed(1);
            break;
          case 'TB':
            freespace = (free * 1e-12).toFixed(2);
            break;
        }

        self.freespace = 'Free Space: ' + freespace + ' ' + tab.storage.unit;
      });
    },
    getBalance: function() {
      var self = this;
      var tab = this.userdata.tabs[this.current];

      this.balance.sjcx = 0;
      this.balance.sjct = 0;

      if (!tab.address) {
        this.balance.qualified = false;
        return;
      }

      var url = 'http://xcp.blockscan.com/api2';
      var assets = ['SJCX', 'SJCT'];
      var query = {
        module: 'address',
        action: 'balance',
        btc_address: tab.address
      };

      assets.forEach(function(asset) {
        query.asset = asset;

        request({ url: url, qs: query, json: true }, function(err, res, body) {
          if (err || res.statusCode !== 200) {
            return;
          }

          self.balance.qualified = true;

          if (body && body.data.length) {
            self.balance[asset.toLowerCase()] = body.data[0].balance;
          }
        });
      });
    }
  },
  created: function() {
    var self = this;
    $('.container').addClass('visible');

    if (!this.userdata.tabs.length) {
      this.addTab();
    } else {
      handleRunDrivesOnBoot(this.userdata.appSettings.runDrivesOnBoot);
    }

    this.scrollLogs();

    function handleRunDrivesOnBoot(isEnabled){
      //iterate over drives and run or iterate over and remove flags
      self.userdata.tabs.forEach(function(tab, index) {
        if (tab.wasRunning && isEnabled) {
          self.startFarming(null, index);
        } else if(tab.wasRunning && !isEnabled){
          tab.wasRunning = false;
        }
      });

      userdata.saveConfig(function(err) {
        if (err) {
          return window.alert(err.message);
        }
      });
    }

    this.showTab(this.current);

    ipc.on('selectDriveFromSysTray', function(ev, tabIndex){
      self.showTab(tabIndex);
    });

    ipc.on('storageDirectorySelected', function(ev, path) {
      self.userdata.tabs[self.current].storage.path = path[0];
      self.getFreeSpace();
      ipc.send('appSettingsChanged', JSON.stringify(userdata.toObject()));
    });

    ipc.on('toggleFarmer', function() {
      var isRunning = !!self.userdata.tabs[self.current].farmer;

      if (isRunning) {
        self.stopFarming();
      } else {
        self.startFarming();
      }
    });
  }
});

/**
 * Footer View
 */
var footer = new Vue({
  el: '#footer',
  data: {},
  methods: {

  }
});

/**
 * Expose view objects
 * #exports
 */
module.exports = {
  updater: updater,
  about: about,
  appSettings: appSettings,
  main: main,
  footer: footer
};
<|endoftext|>


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

var net = require('net')
var tls = require('tls')

exports.connect = function(config)
{
    var connection = undefined
    if(config.ssl)
    {
        connection = tls.connect(config.port, config.host, {rejectUnauthorized:false})
    }
    else
    {
        connection = new net.Socket()
        connection.setNoDelay()
        connection.connect(config.port, config.host)
    }

    connection.setEncoding('utf8')
    return connection
}

exports.Server = function(config)
{
    this.config = config
    this.connection = exports.connect(this.config)
    this.connection.on('connect', function()
    {
        console.log('connected!')
    }.bind(this))

    this.connection.on('close', function(had_error)
    {
        console.log('closed!')
    })

    this.connection.on('error', function(err)
    {
        console.log('error!')
    })

    this.connection.on('data', function(buffer)
    {
        console.log('data received!')
    })
}

var servers = {}

exports.name = 'ibot'
exports.config$load = function(config)
{
    if(config.servers !== undefined)
    {
        for(var k in config.servers)
        {
            if(servers[k] === undefined)
            {
                servers[k] = new exports.Server(config.servers[k])
            }
        }
    }
}
<commit_msg>Wrote recv and send functions and created Message object with message parsing code.<commit_after>/*
 *  Copyright 2013 David Farrell
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

var net = require('net')
var tls = require('tls')

exports.connect = function(config)
{
    var connection = undefined
    if(config.ssl)
    {
        connection = tls.connect(config.port, config.host, {rejectUnauthorized:false})
    }
    else
    {
        connection = new net.Socket()
        connection.setNoDelay()
        connection.connect(config.port, config.host)
    }

    connection.setEncoding('utf8')
    return connection
}

exports.Message = function(data)
{
    var words = data.split(' ')
    var index = 0

    if(words[index][0] === ':')
    {
        this.prefix = {}
        this.prefix.mask = words[index].substr(1)

        if(this.prefix.mask.indexOf('!') !== -1)
        {
            var spl1 = this.prefix.mask.split('!')
            var spl2 = spl1[1].split('@')
            this.prefix.nick = spl1[0]
            this.prefix.user = spl2[0]
            this.prefix.host = spl2[1]
        }

        ++index
    }

    this.opcode = words[index++]
    this.params = []

    var in_string = false
    for(var i=index; i<words.length; ++i)
    {
        if(in_string)
        {
            this.params[this.params.length - 1] += ' ' + words[i]
            continue
        }

        if(words[i][0] === ':')
        {
            in_string = true
            this.params.push(words[i].substr(1))
            continue
        }

        this.params.push(words[i])
    }
}

exports.Server = function(config)
{
    this.config = config
    this.connection = exports.connect(this.config)
    this.connection.on('connect', function()
    {
        console.log('connected!')

        this.send('NICK ' + this.config.nick)
        this.send('USER ' + this.config.user + ' 0 * :iBot')
    }.bind(this))

    this.connection.on('close', function(had_error)
    {
        console.log('closed!')
    })

    this.connection.on('error', function(err)
    {
        console.log('error!')
    })

    var accumulator = ''
    this.connection.on('data', function(buffer)
    {
        var data = accumulator + buffer.toString()
        var lines = data.split('\r\n')

        for(var i=0; i<lines.length-1; ++i)
        {
            this.recv(lines[i])
        }

        accumulator = lines[lines.length - 1]
    }.bind(this))

    this.process = function(message)
    {
        console.log(message)
    }

    this.recv = function(data)
    {
        this.process(new exports.Message(data))
    }

    this.send = function(data, newline)
    {
        console.log('-> ' + data)

        if(newline !== false) data += '\r\n'
        this.connection.write(data)
    }
}

var servers = {}

exports.name = 'ibot'
exports.config$load = function(config)
{
    if(config.servers !== undefined)
    {
        for(var k in config.servers)
        {
            if(servers[k] === undefined)
            {
                servers[k] = new exports.Server(config.servers[k])
            }
        }
    }
}
<|endoftext|>


<commit_before>// This script copies src/index.html into /dist/index.html
// and adds TrackJS error tracking code for use in production
// when useTrackJs is set to true below and a trackJsToken is provided.
// This is a good example of using Node and cheerio to do a simple file transformation.
// In this case, the transformation is useful since we only want to track errors in the built production code.

// Allowing console calls below since this is a build file.
/* eslint-disable no-console */

import fs from 'fs';
import {chalkSuccess, chalkError, chalkWarning} from './chalkConfig';
import cheerio from 'cheerio';

const useTrackJs = true; // If you choose not to use TrackJS, just set this to false and the build warning will go away.
const trackJsToken = ''; // If you choose to use TrackJS, insert your unique token here. To get a token, go to https://trackjs.com

fs.readFile('src/index.html', 'utf8', (readError, markup) => {
  if (readError) {
    return console.log(chalkError(readError));
  }

  const $ = cheerio.load(markup);

  // since a separate spreadsheet is only utilized for the production build, need to dynamically add this here.
  $('head').prepend('<link rel="stylesheet" href="/styles.css">');

  if (useTrackJs) {
    if (trackJsToken) {
      const trackJsCode = `<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '${trackJsToken}' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->`;

      $('head').prepend(trackJsCode); // add TrackJS tracking code to the top of <head>
    } else {
      console.log(chalkWarning('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html on line 10.'));
    }
  }

  fs.writeFile('dist/index.html', $.html(), 'utf8', (writeError) => {
    if (writeError) {
      return console.log(chalkError(writeError));
    }
    console.log(chalkSuccess('index.html written to /dist'));

    return writeError;
  });

  return readError;
});<commit_msg>Load styles last for same beahviour as in dev mode<commit_after>// This script copies src/index.html into /dist/index.html
// and adds TrackJS error tracking code for use in production
// when useTrackJs is set to true below and a trackJsToken is provided.
// This is a good example of using Node and cheerio to do a simple file transformation.
// In this case, the transformation is useful since we only want to track errors in the built production code.

// Allowing console calls below since this is a build file.
/* eslint-disable no-console */

import fs from 'fs';
import {chalkSuccess, chalkError, chalkWarning} from './chalkConfig';
import cheerio from 'cheerio';

const useTrackJs = true; // If you choose not to use TrackJS, just set this to false and the build warning will go away.
const trackJsToken = ''; // If you choose to use TrackJS, insert your unique token here. To get a token, go to https://trackjs.com

fs.readFile('src/index.html', 'utf8', (readError, markup) => {
  if (readError) {
    return console.log(chalkError(readError));
  }

  const $ = cheerio.load(markup);

  // since a separate spreadsheet is only utilized for the production build, need to dynamically add this here.
  $('head').append('<link rel="stylesheet" href="/styles.css">');

  if (useTrackJs) {
    if (trackJsToken) {
      const trackJsCode = `<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '${trackJsToken}' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->`;

      $('head').prepend(trackJsCode); // add TrackJS tracking code to the top of <head>
    } else {
      console.log(chalkWarning('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html on line 10.'));
    }
  }

  fs.writeFile('dist/index.html', $.html(), 'utf8', (writeError) => {
    if (writeError) {
      return console.log(chalkError(writeError));
    }
    console.log(chalkSuccess('index.html written to /dist'));

    return writeError;
  });

  return readError;
});<|endoftext|>


<commit_before>const FooterPun = require("./models/footerPun");
const BreakInfo = require("./models/breakInfo");

module.exports = {
    settings: {
        useRandomFooterPuns: true,
        defaultFooterPun: "heart",
        winterTheme: false,
        nodePort: 3000,
    },
    onBreak: {
        //The dates are only rendered, never used for checking if closed!
        mensa: new BreakInfo("Wir werden im Zeitraum", "1.6", "27.9", "renoviert und sind in Sommerpause.", "fa fa-sun"),
        // mensa: new BreakInfo("Wir befinden uns für den Zeitraum", "28.7", "2.9", "in Sommerpause."),
    },
    cache: {
        // Time in miliseconds -> 20 min.
        intervall: 1200000,
        overallVisitorKey: "overallVisitors",
        dailyVisitorKey: "dailyVisitors"
    },
    scraper: {
        mensaUrl: "https://menu.mensen.at/index/index/locid/45",
        uniwirtUrl: "http://www.uniwirt.at",
        hotspotUrl: "https://www.lakeside-scitec.com/services/gastronomie/hotspot",
        unipizzeriaUrl: "http://www.uni-pizzeria.at/speisen/mittagsteller.html",
        lapastaUrl: "https://lapasta.at/wochenmenue.html",
        princsUrl: "http://www.princs.com/",
        villaLidoUrl: "https://www.villa-lido.at/",
        bitsAndBytesUrl: "https://www.lakeside-scitec.com/services/gastronomie/bits-bytes/"
    },
    userFriendlyUrls: {
        //Usually the same as the scraper ones
        mensa: "http://menu.mensen.at/index/index/locid/45",
        uniwirt: "http://www.uniwirt.at/wp/home/mittag/",
        hotspot: "https://www.lakeside-scitec.com/services/gastronomie/hotspot",
        "uni-pizzeria": "http://www.uni-pizzeria.at/speisen/mittagsteller.html",
        "la-pasta": "https://lapasta.at/wochenmenue.html",
        princs: "http://www.princs.com/",
        "villa-lido": "https://www.villa-lido.at/",
        "bits-and-bytes": "https://www.lakeside-scitec.com/services/gastronomie/bits-bytes/"
    },
    cookie: {
        // 30 days in milliseconds
        maxAge: 2592000000
    },
    externalApis: {
        paramKey: "${param}",
        numbersApi: "http://numbersapi.com/${param}",
        catFactsApi: "https://catfact.ninja/fact",
        placeKittenApi: "https://placekitten.com/${param}/200",
        placeKittenWidth: 700,
        placeKittenWidthSpan: 200
    },
    footerPuns: [
        new FooterPun("heart", "Crafted with", "fa fa-heart"),
        new FooterPun("empire", "Constructed for the", "fab fa-empire", "http://starwars.wikia.com/wiki/Galactic_Empire"),
        new FooterPun("rebellion", "Join the", "fab fa-rebel", "http://starwars.wikia.com/wiki/Alliance_to_Restore_the_Republic"),
        new FooterPun("lizardpaper", ' eats', "fa fa-hand-paper"),
        new FooterPun("print", "I'm also printable!", "fa fa-print", "/print"),
        new FooterPun("got", "Winter is coming", "fa fa-snowflake", "http://gameofthrones.wikia.com/wiki/House_Stark"),
        /*new FooterPun("christmas", "Frohe Weihnachten!", "fa-snowflake-o"),
         new FooterPun("star", "Hey look!", "fa-star fa-spin", null, " It's spinning!"),
         new FooterPun("keyboard", "Use your ", "fa-keyboard-o", null, " arrows on desktop!"),
         new FooterPun("infinity", "To infinity ", "fa-space-shuttle", null, " and beyond!")*/
    ],
    snowFall: {
        particles: 150,
    }
};
<commit_msg>Removed Mensa Pause<commit_after>const FooterPun = require("./models/footerPun");
const BreakInfo = require("./models/breakInfo");

module.exports = {
    settings: {
        useRandomFooterPuns: true,
        defaultFooterPun: "heart",
        winterTheme: false,
        nodePort: 3000,
    },
    onBreak: {
        //The dates are only rendered, never used for checking if closed!
        // mensa: new BreakInfo("Wir werden im Zeitraum", "1.6", "27.9", "renoviert und sind in Sommerpause.", "fa fa-sun"),
        // mensa: new BreakInfo("Wir befinden uns für den Zeitraum", "28.7", "2.9", "in Sommerpause."),
    },
    cache: {
        // Time in miliseconds -> 20 min.
        intervall: 1200000,
        overallVisitorKey: "overallVisitors",
        dailyVisitorKey: "dailyVisitors"
    },
    scraper: {
        mensaUrl: "https://menu.mensen.at/index/index/locid/45",
        uniwirtUrl: "http://www.uniwirt.at",
        hotspotUrl: "https://www.lakeside-scitec.com/services/gastronomie/hotspot",
        unipizzeriaUrl: "http://www.uni-pizzeria.at/speisen/mittagsteller.html",
        lapastaUrl: "https://lapasta.at/wochenmenue.html",
        princsUrl: "http://www.princs.com/",
        villaLidoUrl: "https://www.villa-lido.at/",
        bitsAndBytesUrl: "https://www.lakeside-scitec.com/services/gastronomie/bits-bytes/"
    },
    userFriendlyUrls: {
        //Usually the same as the scraper ones
        mensa: "http://menu.mensen.at/index/index/locid/45",
        uniwirt: "http://www.uniwirt.at/wp/home/mittag/",
        hotspot: "https://www.lakeside-scitec.com/services/gastronomie/hotspot",
        "uni-pizzeria": "http://www.uni-pizzeria.at/speisen/mittagsteller.html",
        "la-pasta": "https://lapasta.at/wochenmenue.html",
        princs: "http://www.princs.com/",
        "villa-lido": "https://www.villa-lido.at/",
        "bits-and-bytes": "https://www.lakeside-scitec.com/services/gastronomie/bits-bytes/"
    },
    cookie: {
        // 30 days in milliseconds
        maxAge: 2592000000
    },
    externalApis: {
        paramKey: "${param}",
        numbersApi: "http://numbersapi.com/${param}",
        catFactsApi: "https://catfact.ninja/fact",
        placeKittenApi: "https://placekitten.com/${param}/200",
        placeKittenWidth: 700,
        placeKittenWidthSpan: 200
    },
    footerPuns: [
        new FooterPun("heart", "Crafted with", "fa fa-heart"),
        new FooterPun("empire", "Constructed for the", "fab fa-empire", "http://starwars.wikia.com/wiki/Galactic_Empire"),
        new FooterPun("rebellion", "Join the", "fab fa-rebel", "http://starwars.wikia.com/wiki/Alliance_to_Restore_the_Republic"),
        new FooterPun("lizardpaper", ' eats', "fa fa-hand-paper"),
        new FooterPun("print", "I'm also printable!", "fa fa-print", "/print"),
        new FooterPun("got", "Winter is coming", "fa fa-snowflake", "http://gameofthrones.wikia.com/wiki/House_Stark"),
        /*new FooterPun("christmas", "Frohe Weihnachten!", "fa-snowflake-o"),
         new FooterPun("star", "Hey look!", "fa-star fa-spin", null, " It's spinning!"),
         new FooterPun("keyboard", "Use your ", "fa-keyboard-o", null, " arrows on desktop!"),
         new FooterPun("infinity", "To infinity ", "fa-space-shuttle", null, " and beyond!")*/
    ],
    snowFall: {
        particles: 150,
    }
};
<|endoftext|>


<commit_before>
// initialize a package.json file

module.exports = init

var read = require("read")
  , path = require("path")
  , readJson = require("./utils/read-json.js")
  , fs = require("graceful-fs")
  , promiseChain = require("./utils/promise-chain.js")
  , exec = require("./utils/exec.js")
  , semver = require("semver")
  , log = require("./utils/log.js")
  , npm = require("./npm.js")
  , output = require("./utils/output.js")

init.usage = "npm init [folder]"

function init (args, cb) {
  var folder = args[0] || "."
    , ll = npm.config.get("loglevel")
  npm.config.set("loglevel", "paused")
  if (folder.charAt(0) !== "/") folder = path.join(process.cwd(), folder)

  readJson(path.join(folder, "package.json"), function (er, data) {
    if (er) data = {}

    data.author = data.author ||
      { name: npm.config.get("init.author.name")
      , email: npm.config.get("init.author.email")
      , url: npm.config.get("init.author.url") }

    init_(data, folder, function (er) {
      npm.config.set("loglevel", ll)
      if (!er) log(path.resolve(folder, "package.json"), "written")
      cb(er)
    })
  })
}

function init_ (data, folder, cb) {
  var nv = npm.config.get("node-version")
    , p = semver.parse(nv)
    , eng = ""

  if (!p[5]) eng = "~" + nv
  else eng = "~" + [p[1], p[2], p[3]].join(".") + " || " + nv

  // node version 0.n is api-compatible with 0.(n+1) when n is odd.
  if (p[2] % 2) {
    eng += " || " + [p[1], +(p[2]) + 1].join(".")
  }

  output.write(
    ["This utility will walk you through creating a package.json file."
    ,"It only covers the most common items, and tries to guess sane defaults."
    ,""
    ,"See `npm help json` for definitive documentation on these fields"
    ,"and exactly what they do."
    ,""
    ,"Use `npm install <pkg> --save` afterwards to install a package and"
    ,"save it as a dependency in the package.json file."
    ,""
    ,"Press ^C at any time to quit."
    ,""
    ].join("\n"))
  promiseChain(cb)
    ( read
    , [{prompt: "Package name: ", default: defaultName(folder, data)}]
    , function (n) { data.name = n }
    )
    ( read
    , [{prompt: "Description: ", default: data.description}]
    , function (d) { data.description = d }
    )
    ( defaultVersion, [folder, data], function (v) { data.version = v } )
    (function (cb) {
      read( { prompt: "Package version: ", default: data.version }
          , function (er, v) {
        if (er) return cb(er)
        data.version = v
        cb()
      })
    }, [])
    ( read
    , [ { prompt: "Project homepage: "
        , default: data.homepage || data.url || "none" } ]
    , function (u) {
        if (u === "none") return
        data.homepage = u
        delete data.url
      }
    )
    ( defaultRepo, [folder, data], function (r) { data.repository = r } )
    (function (cb) {
      read( { prompt: "Project git repository: "
            , default: data.repository && data.repository.url || "none" }
          , function (er, r) {
              if (er) return cb(er)
              if (r !== "none") {
                data.repository = (data.repository || {}).url = r
              }
              cb()
            }
          )
    }, [])
    ( read
    , [{ prompt: "Author name: ", default: data.author && data.author.name }]
    , function (n) {
        if (!n) return
        (data.author = data.author || {}).name = n
      }
    )
    ( read
    , [ { prompt: "Author email: "
        , default: data.author && data.author.email || "none" } ]
    , function (n) {
        if (n === "none") return
        (data.author = data.author || {}).email = n
      }
    )
    ( read
    , [ { prompt: "Author url: "
        , default: data.author && data.author.url || "none" } ]
    , function (n) {
        if (n === "none") return
        (data.author = data.author || {}).url = n
      }
    )
    ( read
    , [ { prompt: "Main module/entry point: ", default: data.main || "none" } ]
    , function (m) {
        if (m === "none") {
          delete data.main
          return
        }
        data.main = m
      }
    )
    ( read
    , [ { prompt: "Test command: "
        , default: data.scripts && data.scripts.test || "none" } ]
    , function (t) {
        if (t === "none") return
        (data.scripts = data.scripts || {}).test = t
      }
    )
    (cleanupPaths, [data, folder])
    (function (cb) {
      try { data = readJson.processJson(data) }
      catch (er) { return cb(er) }
      Object.keys(data)
        .filter(function (k) { return k.match(/^_/) })
        .forEach(function (k) { delete data[k] })
      readJson.unParsePeople(data)
      var str = JSON.stringify(data, null, 2)
        , msg = "About to write to "
              + path.join(folder, "package.json")
              + "\n\n"
              + str
              + "\n\n"
      output.write(msg, cb)
    })
    (function (cb) {
      read({ prompt: "\nIs this ok? ", default: "yes" }, function (er, ok) {
        if (er) return cb(er)
        if (ok.toLowerCase().charAt(0) !== "y") {
          return cb(new Error("cancelled"))
        }
        return cb()
      })
    })
    (function (cb) {
      fs.writeFile( path.join(folder, "package.json")
                  , JSON.stringify(data, null, 2) + "\n"
                  , cb )
    })
    ()
}

// sync - no io
function defaultName (folder, data) {
  if (data.name) return data.name
  return path.basename(folder)
             .replace(/^node[-\._]?|([-\._]node)[-\._]?(js)?$/g, "")
}

function defaultVersion (folder, data, cb) {
  if (data.version) return cb(null, data.version)
  exec(npm.config.get("git"), ["describe", "--tags"], process.env, false, folder,
       function (er, code, out) {
    out = (out || "").trim()
    if (semver.valid(out)) return cb(null, out)
    out = npm.config.get("init.version")
    if (semver.valid(out)) return cb(null, out)
    return cb(null, "0.0.0")
  })
}

function defaultRepo (folder, data, cb) {
  if (data.repository) return cb(null, data.repository)
  exec( npm.config.get("git"), ["remote", "-v"], process.env, false, folder
      , function (er, code, out) {
    out = (out || "")
      .trim()
      .split("\n").filter(function (line) {
        return line.search(/^origin/) !== -1
      })[0]
    if (!out) return cb(null, {})
    var repo =
      { type: "git"
      , url: out.split(/\s/)[1]
                .replace("git@github.com:", "git://github.com/")
      }
    return cb(null, repo)
  })
}

function cleanupPaths (data, folder, cb) {
  if (data.main) {
    data.main = cleanupPath(data.main, folder)
  }
  var dirs = data.directories
  if (dirs) {
    Object.keys(dirs).forEach(function (dir) {
      dirs[dir] = cleanupPath(dirs[dir], folder)
    })
  }
  cb()
}

function cleanupPath (m, folder) {
  if (m.indexOf(folder) === 0) m = path.join(".", m.substr(folder.length))
  return m
}
<commit_msg>Don't clobber the repo object with a string.<commit_after>
// initialize a package.json file

module.exports = init

var read = require("read")
  , path = require("path")
  , readJson = require("./utils/read-json.js")
  , fs = require("graceful-fs")
  , promiseChain = require("./utils/promise-chain.js")
  , exec = require("./utils/exec.js")
  , semver = require("semver")
  , log = require("./utils/log.js")
  , npm = require("./npm.js")
  , output = require("./utils/output.js")

init.usage = "npm init [folder]"

function init (args, cb) {
  var folder = args[0] || "."
    , ll = npm.config.get("loglevel")
  npm.config.set("loglevel", "paused")
  if (folder.charAt(0) !== "/") folder = path.join(process.cwd(), folder)

  readJson(path.join(folder, "package.json"), function (er, data) {
    if (er) data = {}

    data.author = data.author ||
      { name: npm.config.get("init.author.name")
      , email: npm.config.get("init.author.email")
      , url: npm.config.get("init.author.url") }

    init_(data, folder, function (er) {
      npm.config.set("loglevel", ll)
      if (!er) log(path.resolve(folder, "package.json"), "written")
      cb(er)
    })
  })
}

function init_ (data, folder, cb) {
  var nv = npm.config.get("node-version")
    , p = semver.parse(nv)
    , eng = ""

  if (!p[5]) eng = "~" + nv
  else eng = "~" + [p[1], p[2], p[3]].join(".") + " || " + nv

  // node version 0.n is api-compatible with 0.(n+1) when n is odd.
  if (p[2] % 2) {
    eng += " || " + [p[1], +(p[2]) + 1].join(".")
  }

  output.write(
    ["This utility will walk you through creating a package.json file."
    ,"It only covers the most common items, and tries to guess sane defaults."
    ,""
    ,"See `npm help json` for definitive documentation on these fields"
    ,"and exactly what they do."
    ,""
    ,"Use `npm install <pkg> --save` afterwards to install a package and"
    ,"save it as a dependency in the package.json file."
    ,""
    ,"Press ^C at any time to quit."
    ,""
    ].join("\n"))
  promiseChain(cb)
    ( read
    , [{prompt: "Package name: ", default: defaultName(folder, data)}]
    , function (n) { data.name = n }
    )
    ( read
    , [{prompt: "Description: ", default: data.description}]
    , function (d) { data.description = d }
    )
    ( defaultVersion, [folder, data], function (v) { data.version = v } )
    (function (cb) {
      read( { prompt: "Package version: ", default: data.version }
          , function (er, v) {
        if (er) return cb(er)
        data.version = v
        cb()
      })
    }, [])
    ( read
    , [ { prompt: "Project homepage: "
        , default: data.homepage || data.url || "none" } ]
    , function (u) {
        if (u === "none") return
        data.homepage = u
        delete data.url
      }
    )
    ( defaultRepo, [folder, data], function (r) { data.repository = r } )
    (function (cb) {
      read( { prompt: "Project git repository: "
            , default: data.repository && data.repository.url || "none" }
          , function (er, r) {
              if (er) return cb(er)
              if (r !== "none") {
                (data.repository = (data.repository || {})).url = r
              }
              cb()
            }
          )
    }, [])
    ( read
    , [{ prompt: "Author name: ", default: data.author && data.author.name }]
    , function (n) {
        if (!n) return
        (data.author = data.author || {}).name = n
      }
    )
    ( read
    , [ { prompt: "Author email: "
        , default: data.author && data.author.email || "none" } ]
    , function (n) {
        if (n === "none") return
        (data.author = data.author || {}).email = n
      }
    )
    ( read
    , [ { prompt: "Author url: "
        , default: data.author && data.author.url || "none" } ]
    , function (n) {
        if (n === "none") return
        (data.author = data.author || {}).url = n
      }
    )
    ( read
    , [ { prompt: "Main module/entry point: ", default: data.main || "none" } ]
    , function (m) {
        if (m === "none") {
          delete data.main
          return
        }
        data.main = m
      }
    )
    ( read
    , [ { prompt: "Test command: "
        , default: data.scripts && data.scripts.test || "none" } ]
    , function (t) {
        if (t === "none") return
        (data.scripts = data.scripts || {}).test = t
      }
    )
    (cleanupPaths, [data, folder])
    (function (cb) {
      try { data = readJson.processJson(data) }
      catch (er) { return cb(er) }
      Object.keys(data)
        .filter(function (k) { return k.match(/^_/) })
        .forEach(function (k) { delete data[k] })
      readJson.unParsePeople(data)
      var str = JSON.stringify(data, null, 2)
        , msg = "About to write to "
              + path.join(folder, "package.json")
              + "\n\n"
              + str
              + "\n\n"
      output.write(msg, cb)
    })
    (function (cb) {
      read({ prompt: "\nIs this ok? ", default: "yes" }, function (er, ok) {
        if (er) return cb(er)
        if (ok.toLowerCase().charAt(0) !== "y") {
          return cb(new Error("cancelled"))
        }
        return cb()
      })
    })
    (function (cb) {
      fs.writeFile( path.join(folder, "package.json")
                  , JSON.stringify(data, null, 2) + "\n"
                  , cb )
    })
    ()
}

// sync - no io
function defaultName (folder, data) {
  if (data.name) return data.name
  return path.basename(folder)
             .replace(/^node[-\._]?|([-\._]node)[-\._]?(js)?$/g, "")
}

function defaultVersion (folder, data, cb) {
  if (data.version) return cb(null, data.version)
  exec(npm.config.get("git"), ["describe", "--tags"], process.env, false, folder,
       function (er, code, out) {
    out = (out || "").trim()
    if (semver.valid(out)) return cb(null, out)
    out = npm.config.get("init.version")
    if (semver.valid(out)) return cb(null, out)
    return cb(null, "0.0.0")
  })
}

function defaultRepo (folder, data, cb) {
  if (data.repository) return cb(null, data.repository)
  exec( npm.config.get("git"), ["remote", "-v"], process.env, false, folder
      , function (er, code, out) {
    out = (out || "")
      .trim()
      .split("\n").filter(function (line) {
        return line.search(/^origin/) !== -1
      })[0]
    if (!out) return cb(null, {})
    var repo =
      { type: "git"
      , url: out.split(/\s/)[1]
                .replace("git@github.com:", "git://github.com/")
      }
    return cb(null, repo)
  })
}

function cleanupPaths (data, folder, cb) {
  if (data.main) {
    data.main = cleanupPath(data.main, folder)
  }
  var dirs = data.directories
  if (dirs) {
    Object.keys(dirs).forEach(function (dir) {
      dirs[dir] = cleanupPath(dirs[dir], folder)
    })
  }
  cb()
}

function cleanupPath (m, folder) {
  if (m.indexOf(folder) === 0) m = path.join(".", m.substr(folder.length))
  return m
}
<|endoftext|>


<commit_before>"use strict";
var Vendor = (function () {
    function Vendor() {
        this.imageURL = null;
    }
    Vendor.prototype.toJSON = function () {
        return {
            name: this.name,
            image: { url: this.imageURL }
        };
    };
    return Vendor;
}());
var Item = (function () {
    function Item(id, name, cost, quantity) {
        this.description = null;
        this.imageURL = null;
        this.dataMatrixImageURL = null;
        this.quantity = 0;
        this.vendor = new Vendor();
        this.id = id;
        this.name = name;
        this.cost = cost;
        this.quantity = quantity;
    }
    Item.prototype.toJSON = function () {
        return {
            id: this.id,
            name: this.name,
            vendor: this.vendor.toJSON(),
            image: { url: this.imageURL },
            dataMatrixImage: { url: this.dataMatrixImageURL }
        };
    };
    return Item;
}());
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Item;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pdGVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFFQTtJQUFBO1FBRUUsYUFBUSxHQUFXLElBQUksQ0FBQztJQVMxQixDQUFDO0lBTkMsdUJBQU0sR0FBTjtRQUNFLE1BQU0sQ0FBQztZQUNMLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtZQUNmLEtBQUssRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFO1NBQzlCLENBQUM7SUFDSixDQUFDO0lBQ0gsYUFBQztBQUFELENBQUMsQUFYRCxJQVdDO0FBSUQ7SUFVRSxjQUFZLEVBQVUsRUFBRSxJQUFZLEVBQUUsSUFBWSxFQUFFLFFBQWdCO1FBTnBFLGdCQUFXLEdBQVEsSUFBSSxDQUFDO1FBRXhCLGFBQVEsR0FBVyxJQUFJLENBQUM7UUFDeEIsdUJBQWtCLEdBQVcsSUFBSSxDQUFDO1FBQ2xDLGFBQVEsR0FBVyxDQUFDLENBQUM7UUFDckIsV0FBTSxHQUFHLElBQUksTUFBTSxFQUFFLENBQUM7UUFFcEIsSUFBSSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUM7UUFDYixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztJQUMzQixDQUFDO0lBR0QscUJBQU0sR0FBTjtRQUNFLE1BQU0sQ0FBQztZQUNMLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtZQUNYLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtZQUNmLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRTtZQUM1QixLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUM3QixlQUFlLEVBQUUsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1NBQ2xELENBQUM7SUFDSixDQUFDO0lBQ0gsV0FBQztBQUFELENBQUMsQUEzQkQsSUEyQkM7QUEzQkQ7c0JBMkJDLENBQUEifQ==<commit_msg>Chore<commit_after>"use strict";
var Vendor = (function () {
    function Vendor() {
        this.imageURL = null;
    }
    Vendor.prototype.toJSON = function () {
        return {
            name: this.name,
            image: { url: this.imageURL }
        };
    };
    return Vendor;
}());
var Item = (function () {
    function Item(id, name, cost, quantity) {
        this.description = null;
        this.imageURL = null;
        this.dataMatrixImageURL = null;
        this.quantity = 0;
        this.vendor = new Vendor();
        this.id = id;
        this.name = name;
        this.cost = cost;
        this.quantity = quantity;
    }
    Item.prototype.toJSON = function () {
        return {
            id: this.id,
            name: this.name,
            cost: this.cost,
            quantity: this.quantity,
            vendor: this.vendor.toJSON(),
            image: { url: this.imageURL },
            dataMatrixImage: { url: this.dataMatrixImageURL }
        };
    };
    return Item;
}());
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Item;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pdGVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFFQTtJQUFBO1FBRUUsYUFBUSxHQUFXLElBQUksQ0FBQztJQVExQixDQUFDO0lBTkMsdUJBQU0sR0FBTjtRQUNFLE1BQU0sQ0FBQztZQUNMLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtZQUNmLEtBQUssRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFO1NBQzlCLENBQUM7SUFDSixDQUFDO0lBQ0gsYUFBQztBQUFELENBQUMsQUFWRCxJQVVDO0FBSUQ7SUFVRSxjQUFZLEVBQVUsRUFBRSxJQUFZLEVBQUUsSUFBWSxFQUFFLFFBQWdCO1FBTnBFLGdCQUFXLEdBQVEsSUFBSSxDQUFDO1FBRXhCLGFBQVEsR0FBVyxJQUFJLENBQUM7UUFDeEIsdUJBQWtCLEdBQVcsSUFBSSxDQUFDO1FBQ2xDLGFBQVEsR0FBVyxDQUFDLENBQUM7UUFDckIsV0FBTSxHQUFHLElBQUksTUFBTSxFQUFFLENBQUM7UUFFcEIsSUFBSSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUM7UUFDYixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztJQUMzQixDQUFDO0lBRUQscUJBQU0sR0FBTjtRQUNFLE1BQU0sQ0FBQztZQUNMLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtZQUNYLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtZQUNmLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtZQUNmLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUU7WUFDNUIsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUU7WUFDN0IsZUFBZSxFQUFFLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxrQkFBa0IsRUFBRTtTQUNsRCxDQUFDO0lBQ0osQ0FBQztJQUNILFdBQUM7QUFBRCxDQUFDLEFBNUJELElBNEJDO0FBNUJEO3NCQTRCQyxDQUFBIn0=<|endoftext|>


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

if (!global.jake) {

  let EventEmitter = require('events').EventEmitter;
  // And so it begins
  global.jake = new EventEmitter();

  let fs = require('fs');
  let chalk = require('chalk');
  let taskNs = require('./task');
  let Task = taskNs.Task;
  let FileTask = taskNs.FileTask;
  let DirectoryTask = taskNs.DirectoryTask;
  let Rule = require('./rule').Rule;
  let Namespace = require('./namespace').Namespace;
  let api = require('./api');
  let utils = require('./utils');
  let Program = require('./program').Program;
  let Loader = require('./loader').Loader;
  let pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());

  let MAX_RULE_RECURSION_LEVEL = 16;

  // Globalize jake and top-level API methods (e.g., `task`, `desc`)
  utils.mixin(global, api);

  // Copy utils onto base jake
  utils.mixin(jake, utils);
  // File utils should be aliased directly on base jake as well
  utils.mixin(jake, utils.file);

  // Also add top-level API methods to exported object for those who don't want to
  //   use the globals (`file` here will overwrite the 'file' utils namespace)
  utils.mixin(jake, api);

  utils.mixin(jake, new (function () {

    this._invocationChain = [];
    this._taskTimeout = 30000;

    // Public properties
    // =================
    this.version = pkg.version;
    // Used when Jake exits with a specific error-code
    this.errorCode = undefined;
    // Loads Jakefiles/jakelibdirs
    this.loader = new Loader();
    // Name/value map of all the various tasks defined in a Jakefile.
    // Non-namespaced tasks are placed into 'default.'
    this.defaultNamespace = new Namespace('default', null);
    // For namespaced tasks -- tasks with no namespace are put into the
    // 'default' namespace so lookup code can work the same for both
    // namespaced and non-namespaced.
    this.currentNamespace = this.defaultNamespace;
    // Saves the description created by a 'desc' call that prefaces a
    // 'task' call that defines a task.
    this.currentTaskDescription = null;
    this.program = new Program();
    this.FileList = require('filelist').FileList;
    this.PackageTask = require('./package_task').PackageTask;
    this.PublishTask = require('./publish_task').PublishTask;
    this.WatchTask = require('./watch_task').WatchTask;
    this.TestTask = require('./test_task').TestTask;
    this.Task = Task;
    this.FileTask = FileTask;
    this.DirectoryTask = DirectoryTask;
    this.Namespace = Namespace;
    this.Rule = Rule;

    this.parseAllTasks = function () {
      let _parseNs = function (name, ns) {
        let nsTasks = ns.tasks;
           let task;
           let nsNamespaces = ns.childNamespaces;
           let fullName;
        // Iterate through the tasks in each namespace
        for (let q in nsTasks) {
          task = nsTasks[q];
          // Prefix namespaced tasks
          fullName = name == 'default' ? q : name + ':' + q;
          // Save with 'taskname' or 'namespace:taskname' key
          task.fullName = fullName;
          jake.Task[fullName] = task;
        }
        for (let p in nsNamespaces) {
          fullName = (name == 'default') ? p : name + ':' + p;
          _parseNs(fullName, nsNamespaces[p]);
        }
      };

      _parseNs('default', jake.defaultNamespace);
    };

    /**
     * Displays the list of descriptions avaliable for tasks defined in
     * a Jakefile
     */
    this.showAllTaskDescriptions = function (f) {
      let p;
         let maxTaskNameLength = 0;
         let task;
         let padding;
         let name;
         let descr;
         let filter = typeof f == 'string' ? f : null;

      for (p in jake.Task) {
        if (filter && p.indexOf(filter) == -1) {
          continue;
        }
        task = jake.Task[p];
        // Record the length of the longest task name -- used for
        // pretty alignment of the task descriptions
        if (task.description) {
          maxTaskNameLength = p.length > maxTaskNameLength ?
            p.length : maxTaskNameLength;
        }
      }
      // Print out each entry with descriptions neatly aligned
      for (p in jake.Task) {
        if (filter && p.indexOf(filter) == -1) {
          continue;
        }
        task = jake.Task[p];

        //name = '\033[32m' + p + '\033[39m ';
        name = chalk.green(p);

        descr = task.description;
        if (descr) {
          descr = chalk.gray(descr);

          // Create padding-string with calculated length
          padding = (new Array(maxTaskNameLength - p.length + 2)).join(' ');

          console.log('jake ' + name + padding + descr);
        }
      }
    };

    this.createTask = function () {
      let args = Array.prototype.slice.call(arguments);
         let arg;
         let obj;
         let task;
         let type;
         let name;
         let action;
         let opts = {};
         let prereqs = [];

        type = args.shift();

      // name, [deps], [action]
      // Name (string) + deps (array) format
      if (typeof args[0] == 'string') {
        name = args.shift();
        if (Array.isArray(args[0])) {
          prereqs = args.shift();
        }
      }
      // name:deps, [action]
      // Legacy object-literal syntax, e.g.: {'name': ['depA', 'depB']}
      else {
        obj = args.shift();
        for (let p in obj) {
          prereqs = prereqs.concat(obj[p]);
          name = p;
        }
      }

      // Optional opts/callback or callback/opts
      while ((arg = args.shift())) {
        if (typeof arg == 'function') {
          action = arg;
        }
        else {
          opts = arg;
        }
      }

      task = jake.currentNamespace.resolveTask(name);
      if (task && !action) {
        // Task already exists and no action, just update prereqs, and return it.
        task.prereqs = task.prereqs.concat(prereqs);
        return task;
      }

      switch (type) {
        case 'directory':
          action = function () {
            jake.mkdirP(name);
          };
          task = new DirectoryTask(name, prereqs, action, opts);
          break;
        case 'file':
          task = new FileTask(name, prereqs, action, opts);
          break;
        default:
          task = new Task(name, prereqs, action, opts);
      }

      if (jake.currentTaskDescription) {
        task.description = jake.currentTaskDescription;
        jake.currentTaskDescription = null;
      }
      jake.currentNamespace.tasks[name] = task;
      task.namespace = jake.currentNamespace;

      // FIXME: Should only need to add a new entry for the current
      // task-definition, not reparse the entire structure
      jake.parseAllTasks();

      return task;
    };

    this.attemptRule = function (name, ns, level) {
      let prereqRule;
         let prereq;
      if (level > MAX_RULE_RECURSION_LEVEL) {
        return null;
      }
      // Check Rule
      prereqRule = ns.matchRule(name);
      if (prereqRule) {
        prereq = prereqRule.createTask(name, level);
      }
      return prereq || null;
    };

    this.createPlaceholderFileTask = function (name, namespace) {
      let nsPath = '';
         let filePath = name.split(':').pop(); // Strip any namespace
         let parts;
         let fileTaskName;
         let task;
         let stats;

      if (namespace) {
        if (typeof namespace == 'string') {
          nsPath = namespace;
        }
        else {
          nsPath = namespace.path;
        }
      }

      parts = nsPath.length ? nsPath.split(':') : [];
      parts.push(filePath);
      fileTaskName = parts.join(':');

      task = jake.Task[fileTaskName];

      // If there's not already an existing dummy FileTask for it,
      // create one
      if (!task) {
        // Create a dummy FileTask only if file actually exists
        if (fs.existsSync(filePath)) {
          stats = fs.statSync(filePath);
          task = new jake.FileTask(filePath);
          task.fullName = fileTaskName;
          task.modTime = stats.mtime;
          task.dummy = true;
          // Put this dummy Task in the global Tasks list so
          // modTime will be eval'd correctly
          jake.Task[fileTaskName] = task;
        }
      }

      return task || null;
    };

    this.run = function () {
      let args = Array.prototype.slice.call(arguments);
         let program = this.program;
         let loader = this.loader;
         let preempt;
         let opts;

      program.parseArgs(args);
      program.init();

      preempt = program.firstPreemptiveOption();
      if (preempt) {
        preempt();
      }
      else {
        opts = program.opts;
        // jakefile flag set but no jakefile yet
        if (opts.autocomplete && opts.jakefile === true) {
          process.stdout.write('no-complete');
          return;
        }
        // Load Jakefile and jakelibdir files
        let jakefileLoaded = loader.loadFile(opts.jakefile);
        let jakelibdirLoaded = loader.loadDirectory(opts.jakelibdir);

        if(!jakefileLoaded && !jakelibdirLoaded && !opts.autocomplete) {
          fail('No Jakefile. Specify a valid path with -f/--jakefile, ' +
              'or place one in the current directory.');
        }

        program.run();
      }
    };

  })());
}

module.exports = jake;
<commit_msg>Added pound sign before task desc to allow C/P in shell<commit_after>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

if (!global.jake) {

  let EventEmitter = require('events').EventEmitter;
  // And so it begins
  global.jake = new EventEmitter();

  let fs = require('fs');
  let chalk = require('chalk');
  let taskNs = require('./task');
  let Task = taskNs.Task;
  let FileTask = taskNs.FileTask;
  let DirectoryTask = taskNs.DirectoryTask;
  let Rule = require('./rule').Rule;
  let Namespace = require('./namespace').Namespace;
  let api = require('./api');
  let utils = require('./utils');
  let Program = require('./program').Program;
  let Loader = require('./loader').Loader;
  let pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());

  let MAX_RULE_RECURSION_LEVEL = 16;

  // Globalize jake and top-level API methods (e.g., `task`, `desc`)
  utils.mixin(global, api);

  // Copy utils onto base jake
  utils.mixin(jake, utils);
  // File utils should be aliased directly on base jake as well
  utils.mixin(jake, utils.file);

  // Also add top-level API methods to exported object for those who don't want to
  //   use the globals (`file` here will overwrite the 'file' utils namespace)
  utils.mixin(jake, api);

  utils.mixin(jake, new (function () {

    this._invocationChain = [];
    this._taskTimeout = 30000;

    // Public properties
    // =================
    this.version = pkg.version;
    // Used when Jake exits with a specific error-code
    this.errorCode = undefined;
    // Loads Jakefiles/jakelibdirs
    this.loader = new Loader();
    // Name/value map of all the various tasks defined in a Jakefile.
    // Non-namespaced tasks are placed into 'default.'
    this.defaultNamespace = new Namespace('default', null);
    // For namespaced tasks -- tasks with no namespace are put into the
    // 'default' namespace so lookup code can work the same for both
    // namespaced and non-namespaced.
    this.currentNamespace = this.defaultNamespace;
    // Saves the description created by a 'desc' call that prefaces a
    // 'task' call that defines a task.
    this.currentTaskDescription = null;
    this.program = new Program();
    this.FileList = require('filelist').FileList;
    this.PackageTask = require('./package_task').PackageTask;
    this.PublishTask = require('./publish_task').PublishTask;
    this.WatchTask = require('./watch_task').WatchTask;
    this.TestTask = require('./test_task').TestTask;
    this.Task = Task;
    this.FileTask = FileTask;
    this.DirectoryTask = DirectoryTask;
    this.Namespace = Namespace;
    this.Rule = Rule;

    this.parseAllTasks = function () {
      let _parseNs = function (name, ns) {
        let nsTasks = ns.tasks;
           let task;
           let nsNamespaces = ns.childNamespaces;
           let fullName;
        // Iterate through the tasks in each namespace
        for (let q in nsTasks) {
          task = nsTasks[q];
          // Prefix namespaced tasks
          fullName = name == 'default' ? q : name + ':' + q;
          // Save with 'taskname' or 'namespace:taskname' key
          task.fullName = fullName;
          jake.Task[fullName] = task;
        }
        for (let p in nsNamespaces) {
          fullName = (name == 'default') ? p : name + ':' + p;
          _parseNs(fullName, nsNamespaces[p]);
        }
      };

      _parseNs('default', jake.defaultNamespace);
    };

    /**
     * Displays the list of descriptions avaliable for tasks defined in
     * a Jakefile
     */
    this.showAllTaskDescriptions = function (f) {
      let p;
         let maxTaskNameLength = 0;
         let task;
         let padding;
         let name;
         let descr;
         let filter = typeof f == 'string' ? f : null;

      for (p in jake.Task) {
        if (filter && p.indexOf(filter) == -1) {
          continue;
        }
        task = jake.Task[p];
        // Record the length of the longest task name -- used for
        // pretty alignment of the task descriptions
        if (task.description) {
          maxTaskNameLength = p.length > maxTaskNameLength ?
            p.length : maxTaskNameLength;
        }
      }
      // Print out each entry with descriptions neatly aligned
      for (p in jake.Task) {
        if (filter && p.indexOf(filter) == -1) {
          continue;
        }
        task = jake.Task[p];

        //name = '\033[32m' + p + '\033[39m ';
        name = chalk.green(p);

        descr = task.description;
        if (descr) {
          descr = chalk.gray('# ' + descr);

          // Create padding-string with calculated length
          padding = (new Array(maxTaskNameLength - p.length + 2)).join(' ');

          console.log('jake ' + name + padding + descr);
        }
      }
    };

    this.createTask = function () {
      let args = Array.prototype.slice.call(arguments);
         let arg;
         let obj;
         let task;
         let type;
         let name;
         let action;
         let opts = {};
         let prereqs = [];

        type = args.shift();

      // name, [deps], [action]
      // Name (string) + deps (array) format
      if (typeof args[0] == 'string') {
        name = args.shift();
        if (Array.isArray(args[0])) {
          prereqs = args.shift();
        }
      }
      // name:deps, [action]
      // Legacy object-literal syntax, e.g.: {'name': ['depA', 'depB']}
      else {
        obj = args.shift();
        for (let p in obj) {
          prereqs = prereqs.concat(obj[p]);
          name = p;
        }
      }

      // Optional opts/callback or callback/opts
      while ((arg = args.shift())) {
        if (typeof arg == 'function') {
          action = arg;
        }
        else {
          opts = arg;
        }
      }

      task = jake.currentNamespace.resolveTask(name);
      if (task && !action) {
        // Task already exists and no action, just update prereqs, and return it.
        task.prereqs = task.prereqs.concat(prereqs);
        return task;
      }

      switch (type) {
        case 'directory':
          action = function () {
            jake.mkdirP(name);
          };
          task = new DirectoryTask(name, prereqs, action, opts);
          break;
        case 'file':
          task = new FileTask(name, prereqs, action, opts);
          break;
        default:
          task = new Task(name, prereqs, action, opts);
      }

      if (jake.currentTaskDescription) {
        task.description = jake.currentTaskDescription;
        jake.currentTaskDescription = null;
      }
      jake.currentNamespace.tasks[name] = task;
      task.namespace = jake.currentNamespace;

      // FIXME: Should only need to add a new entry for the current
      // task-definition, not reparse the entire structure
      jake.parseAllTasks();

      return task;
    };

    this.attemptRule = function (name, ns, level) {
      let prereqRule;
         let prereq;
      if (level > MAX_RULE_RECURSION_LEVEL) {
        return null;
      }
      // Check Rule
      prereqRule = ns.matchRule(name);
      if (prereqRule) {
        prereq = prereqRule.createTask(name, level);
      }
      return prereq || null;
    };

    this.createPlaceholderFileTask = function (name, namespace) {
      let nsPath = '';
         let filePath = name.split(':').pop(); // Strip any namespace
         let parts;
         let fileTaskName;
         let task;
         let stats;

      if (namespace) {
        if (typeof namespace == 'string') {
          nsPath = namespace;
        }
        else {
          nsPath = namespace.path;
        }
      }

      parts = nsPath.length ? nsPath.split(':') : [];
      parts.push(filePath);
      fileTaskName = parts.join(':');

      task = jake.Task[fileTaskName];

      // If there's not already an existing dummy FileTask for it,
      // create one
      if (!task) {
        // Create a dummy FileTask only if file actually exists
        if (fs.existsSync(filePath)) {
          stats = fs.statSync(filePath);
          task = new jake.FileTask(filePath);
          task.fullName = fileTaskName;
          task.modTime = stats.mtime;
          task.dummy = true;
          // Put this dummy Task in the global Tasks list so
          // modTime will be eval'd correctly
          jake.Task[fileTaskName] = task;
        }
      }

      return task || null;
    };

    this.run = function () {
      let args = Array.prototype.slice.call(arguments);
         let program = this.program;
         let loader = this.loader;
         let preempt;
         let opts;

      program.parseArgs(args);
      program.init();

      preempt = program.firstPreemptiveOption();
      if (preempt) {
        preempt();
      }
      else {
        opts = program.opts;
        // jakefile flag set but no jakefile yet
        if (opts.autocomplete && opts.jakefile === true) {
          process.stdout.write('no-complete');
          return;
        }
        // Load Jakefile and jakelibdir files
        let jakefileLoaded = loader.loadFile(opts.jakefile);
        let jakelibdirLoaded = loader.loadDirectory(opts.jakelibdir);

        if(!jakefileLoaded && !jakelibdirLoaded && !opts.autocomplete) {
          fail('No Jakefile. Specify a valid path with -f/--jakefile, ' +
              'or place one in the current directory.');
        }

        program.run();
      }
    };

  })());
}

module.exports = jake;
<|endoftext|>


<commit_before>var koan={};

var ansuz=require("ansuz");
var marked=require("marked");
var fs=require("fs");

koan.main=function(opt){
  opt=opt||{};
  opt.home=opt.home||"index";

  opt.path=opt.path||(process.env.PWD+"/koan");
  opt.debug=opt.debug||false;
  opt.template=opt.template||'template';
  opt.wait=opt.wait||false;
  var parseURL=function(url){
    var parts={
      url:url
    };
    parts.root=url
      .replace(/\?.*$/,function(frag){
        parts.fragment=frag;
        return '';
      })
      .replace(/\/[^\/]*$/,function(file){
        parts.file=file.slice(1)||opt.home;
        return "/";
      });
    if(opt.debug)console.log(JSON.stringify(parts,undefined,1));
    return parts;
  };

  return function(req,res,next){
    // look in opt.path+Koan.url.root for your template
      // if does not exist, look back a directory or next(req,res)
    // look in opt.path+Koan.url.root for Koan.url.file
      // if does not exist, next(req,res)

    var Koan={};
    Koan.url=parseURL(req.url);

    (function(){
      var race=2;
      var ifDone=function(){
        // once you've found both, swap the file into the template
        // then replace <?js js?> tags
        // then replace <?md md?> tags
        // then send it
        var content=ansuz.swap(Koan.template,{
          '{CONTENT}':Koan.file
        });

/* evaluate scripts */

        (function(){
          var php={
            wait:opt.wait
            ,count:0
            ,body:''
            ,response:{}
            ,end:function(){
              php.body=ansuz.swap(php.body,php.response);
              res.end(php.body.replace(/<\?md(.|\s)*?md\?>/mg,function(x){
                return marked(x.slice(4,-5));
              }));
            }
          };
          php.body+=content.replace(/<\?js(.|\s)*?js\?>/mg,
            function(x){
              var id='{PHPJS'+(php.count++)+'}';
              php.response[id]='';
              var echo=function(x){
                php.response[id]+=x;
                if(opt.debug)console.log(x);
                return x;
              };
              try{
                eval(x.slice(4,-5));
              }catch(err){
                console.log('ERR: '+err);
              }
              return id;
            });
          if(!php.wait){
            php.end();
          }
        })();
      };

      var doStuff=function(path,target,recurse){
        var root=Koan.url.root;

        var getIt=function(){
          fs.readFile(opt.path+root+path,'utf-8',function(err,str){
            if(err){
              console.log(err);
              return next(req,res);
            }else{
              Koan[target]=str;
              race--;
              if(race===0){
                ifDone();
              }
            }
          });
        };

        var checkIt=function(){
          fs.exists(opt.path+root+path,function(e){
            // if the file does not exist
            if(!e){
              // if you're looking for the template
              // and you're not at the base path yet...
              if(recurse&&(root!=='/')){
                // look back a directory
                root=root.replace(/\/[^\/]*\/$/,'/')
                return checkIt(); // check again
              }else{
                // if you still don't find a template..
                return next(req,res);
              }
            }else{
              getIt();
            }
          });
        };
        checkIt();
      };
      doStuff('template','template','true');
      doStuff(Koan.url.file,'file');
    })();
  };
};

module.exports=koan.main;
<commit_msg>deprecate php variable, all internal variables are now accessible via the 'Koan' object<commit_after>var ansuz=require("ansuz");
var marked=require("marked");
var fs=require("fs");

module.exports=function(opt){
  opt=opt||{};
  opt.home=opt.home||"index";

  opt.path=opt.path||(process.env.PWD+"/koan");
  opt.debug=opt.debug||false;
  opt.template=opt.template||'template';
  opt.wait=opt.wait||false;
  var parseURL=function(url){
    var parts={
      url:url
    };
    parts.root=url
      .replace(/\?.*$/,function(frag){
        parts.fragment=frag;
        return '';
      })
      .replace(/\/[^\/]*$/,function(file){
        parts.file=file.slice(1)||opt.home;
        return "/";
      });
    if(opt.debug)console.log(JSON.stringify(parts,undefined,1));
    return parts;
  };

  return function(req,res,next){
    // look in opt.path+Koan.url.root for your template
      // if does not exist, look back a directory or next(req,res)
    // look in opt.path+Koan.url.root for Koan.url.file
      // if does not exist, next(req,res)

    var Koan={};
    Koan.url=parseURL(req.url);

    (function(){
      var race=2;
      var ifDone=function(){
        // once you've found both, swap the file into the template
        // then replace <?js js?> tags
        // then replace <?md md?> tags
        // then send it
        var content=ansuz.swap(Koan.template,{
          '{CONTENT}':Koan.file
        });

/* evaluate scripts */

        (function(){
          // deprecate php object
          var php=Koan;
          Koan.wait=opt.wait;
          Koan.count=0;
          Koan.body='';
          Koan.response={};
          Koan.opt=opt;
          Koan.end=function(){
            Koan.body=ansuz.swap(Koan.body,Koan.response);
            res.end(Koan.body.replace(/<\?md(.|\s)*?md\?>/mg,function(x){
              return marked(x.slice(4,-5));
            }));
          };

          Koan.body+=content.replace(/<\?js(.|\s)*?js\?>/mg,
            function(x){
              var id='{PHPJS'+(Koan.count++)+'}';
              Koan.response[id]='';
              var echo=function(x){
                Koan.response[id]+=x;
                if(opt.debug)console.log(x);
                return x;
              };
              try{
                eval(x.slice(4,-5));
              }catch(err){
                console.log('ERR: '+err);
              }
              return id;
            });
          if(!Koan.wait){
            Koan.end();
          }
        })();
      };

      var doStuff=function(path,target,recurse){
        var root=Koan.url.root;

        var getIt=function(){
          fs.readFile(opt.path+root+path,'utf-8',function(err,str){
            if(err){
              console.log(err);
              return next(req,res);
            }else{
              Koan[target]=str;
              race--;
              if(race===0){
                ifDone();
              }
            }
          });
        };

        var checkIt=function(){
          fs.exists(opt.path+root+path,function(e){
            // if the file does not exist
            if(!e){
              // if you're looking for the template
              // and you're not at the base path yet...
              if(recurse&&(root!=='/')){
                // look back a directory
                root=root.replace(/\/[^\/]*\/$/,'/')
                return checkIt(); // check again
              }else{
                // if you still don't find a template..
                return next(req,res);
              }
            }else{
              getIt();
            }
          });
        };
        checkIt();
      };
      doStuff('template','template','true');
      doStuff(Koan.url.file,'file');
    })();
  };
};
<|endoftext|>