<commit_before>/**
* Copyright 2011 Microsoft Corporation
*
* 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 assert = require('assert');
var fs = require('fs');
var path = require('path');
var util = require('util');
var xml2js = require('xml2js');
var azureUtils = require('../../util/util');
var common = require('../common');
var utils = require('../utils');
var constants = require('../constants');
var pfx2pem = require('../../util/certificates/pkcs').pfx2pem;
var keyFiles = require('../keyFiles');
var cacheUtils = require('../cacheUtils');

exports.init = function (cli) {
  var log = cli.output;
  var azureDirectory = utils.azureDir();
  var pemPath = path.join(azureDirectory, 'managementCertificate.pem');
  var publishSettingsFilePath = path.join(azureDirectory, 'publishSettings.xml');

  var account = cli.category('account')
        .description('Manage your account information and publish settings');

  account.command('download')
        .whiteListPowershell()
        .description('Launch a browser to download your publishsettings file')
        .execute(function (options, callback) {
          try {
            var url = 'https://windows.azure-preview.com/download/publishprofile.aspx?client=nodejsxplat';
            common.launchBrowser(url);
            log.help('Save the downloaded file, then execute the command');
            log.help('  account import <file>');
            callback();
          }
          catch (err) {
            callback(err);
          }
        });

  account.command('import <file>')
        .whiteListPowershell()
        .description('Import a publishsettings file or certificate for your account')
        .execute(function (file, options, callback) {
          log.verbose('Importing file ' + file);

          // Is it a .pem file?
          var keyCertValues = keyFiles.readFromFile(file);
          var keyPresent = !!keyCertValues.key;
          var certPresent = !!keyCertValues.cert;
          if (keyPresent + certPresent === 1) {
            // Exactly one of them present.  Tell the user about the error.
            // Do not try this file as xml or pfx
            callback('File ' + file + ' needs to contain both private key and cert, but only ' +
                    (keyCertValues.key ? 'key' : 'certificate') + ' was found.');
          } else if (keyCertValues.key && keyCertValues.cert) {
            // Both key and cert are present.
            keyFiles.writeToFile(pemPath, keyCertValues);
            log.verbose('Key and cert have been written to ' + pemPath);
          } else {
            // Try to open as publishsettings or pfx.
            log.silly(file + ' does not appear to be a PEM file. Reading as publish settings file.');
            var parser = new xml2js.Parser();
            var publishSettings = null;
            parser.on('end', function (settings) { publishSettings = settings; });
            var readBuffer = fs.readFileSync(file);
            try {
              parser.parseString(readBuffer);
            } catch (err) {
              if (err.toString().indexOf('Non-whitespace before first tag') === -1) {
                // This looks like an xml parsing error, not PFX.
                callback(err);
              }

              log.silly('Unable to read file as xml publish settings file. Assuming it is pfx');
              publishSettings = null;
            }

            if (publishSettings) {
              processSettings(file, publishSettings);
            } else {
              convertPfx(readBuffer);
            }
          }

          cacheUtils.clear();
          if (publishSettings && publishSettings.PublishProfile.Subscription['@']) {
            return cli.category('site').doSpacesGet({ subscription: publishSettings.PublishProfile.Subscription['@'].Id },
                function(error) {
              if (error) {
                var message = error.Message;
                if (typeof message === 'string' && message.indexOf('The subscription is not authorized for this feature.') >= 0) {
                  log.silly('Error getting site locations - ignored: ', error);
                  error = undefined;
                }
              }
              callback(error);
            });
          }
          return callback();

          function convertPfx(pfx) {
            var pem = pfx2pem(pfx);
            utils.writeFileSyncMode(pemPath, pem.toString(), 'utf8');
            log.verbose('Converted PFX data to ' + pemPath);
          }

          function processSettings(file, settings) {
            var attribs = settings.PublishProfile['@'];
            var subs = settings.PublishProfile.Subscription;
            if (typeof subs === 'undefined' || subs === undefined) {
              subs = [];
            } else if (typeof (subs[0]) === 'undefined') {
              subs = [subs];
            }

            if (attribs.Url) {
              var endpointInfo = utils.validateEndpoint(attribs.Url);
              var config = account.readConfig();
              config.endpoint = endpointInfo.endpointHost;
              config.port = endpointInfo.endpointPort;
              account.writeConfig(config);
              log.info('Setting service endpoint to:', config.endpoint);
              log.info('Setting service port to:', config.port);
            }

            if (subs.length === 0) {
              log.warning('Importing profile with no subscriptions');
            }
            else {
              for (var index in subs) {
                log.info('Found subscription:', subs[index]['@'].Name);
                log.verbose('  Id:', subs[index]['@'].Id);
              }
            }

            log.verbose('Parsing management certificate');
            var pfx = new Buffer(attribs.ManagementCertificate, 'base64');
            convertPfx(pfx);

            log.verbose('Storing account information at', publishSettingsFilePath);
            utils.writeFileSyncMode(publishSettingsFilePath, readBuffer); // folder already created by convertPfx()
            if (subs.length !== 0) {
              log.info('Setting default subscription to:', subs[0]['@'].Name);
              var config = account.readConfig();
              config.subscription = subs[0]['@'].Id;
              account.writeConfig(config);
            }
            log.warn('The \'' + file + '\' file contains sensitive information.');
            log.warn('Remember to delete it now that it has been imported.');
            log.info('Account publish settings imported successfully');
          }
        });

  account.command('clear')
        .whiteListPowershell()
        .description('Remove any of the stored account info stored by import or config set')

        .action(function () {
          function deleteIfExists(file, isDir) {
            if (path.existsSync(file)) {
              log.silly('Removing ' + file);
              (isDir ? fs.rmdirSync : fs.unlinkSync)(file);
              return true;
            } else {
              log.silly(file + ' does not exist');
            }
          }
          var isDeleted = deleteIfExists(pemPath);
          isDeleted = deleteIfExists(publishSettingsFilePath) || isDeleted; // in this order only
          isDeleted = account.clearConfig() || isDeleted;
          isDeleted = cacheUtils.clear() || isDeleted;
          try {
            deleteIfExists(azureDirectory, true);
          } catch (err) {
            log.warn('Couldn\'t remove ' + azureDirectory);
          }
          log.info(isDeleted ? 'Account settings cleared successfully'
              : 'Account settings are already clear');
        });

  var affinityGroup = account.category('affinity-group')
        .description('Commands to manage your Azure affinity groups');

  affinityGroup.command('list')
        .description('List locations available for your account')
        .execute(function (options, callback) {
          listLAG('AffinityGroups', options, callback);
        });

  function listLAG(what, options, callback) {
    var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
        cli.category('account'), log);

    var textName = what.replace(/([A-Z])/g, ' $1').toLowerCase();
    var progress = cli.progress('Fetching' + textName);
    utils.doServiceManagementOperation(channel, 'list' + what, function (error, response) {
      progress.end();
      if (!error) {
        if (response.body.length > 0) {
          log.table(response.body, function (row, item) {
            row.cell('Name', item.Name);
            if ('DisplayName' in item) { // for location
              row.cell('Display Name', item.DisplayName);
            }
            if ('Label' in item) {
              row.cell('Label', Buffer(item.Label, 'base64').toString());
            }
            if ('Location' in item) {
              row.cell('Location', item.Location || '');
            }
          });
        } else {
          if (log.format().json) {
            log.json([]);
          } else {
            log.info('No' + textName + ' found');
          }
        }
      }
      callback(error);
    });
  }
  account.listLAG = listLAG;

  var storage = account.category('storage')
        .description('Commands to manage your Azure storage account');

  storage.command('list')
        .description('List storage accounts available for your account')
        .execute(function (options, callback) {
          var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
              cli.category('account'), log);

          var progress = cli.progress('Fetching storage accounts');
          utils.doServiceManagementOperation(channel, 'listStorageAccounts', function (error, response) {
            progress.end();
            if (!error) {
              if (response.body.length > 0) {
                log.table(response.body, function (row, item) {
                  row.cell('Name', item.ServiceName);
                  var storageServiceProperties = item.StorageServiceProperties;
                  if ('Label' in storageServiceProperties) {
                    row.cell('Label', Buffer(storageServiceProperties.Label, 'base64').toString());
                  }
                  if ('Location' in storageServiceProperties) {
                    row.cell('Location', storageServiceProperties.Location);
                  }
                });
              } else {
                if (log.format().json) {
                  log.json([]);
                } else {
                  log.info('No storage accounts found');
                }
              }
            }
            callback(error);
          });
        });

  account.readPublishSettings = function () {
    var publishSettings = {};

    var parser = new xml2js.Parser();
    parser.on('end', function (result) { publishSettings = result; });
    try {
      log.silly('Reading publish settings', publishSettingsFilePath);
      var readBuffer = fs.readFileSync(publishSettingsFilePath);
      parser.parseString(readBuffer);
    } catch (err) {
      // publish settings file is not expected for all scenarios
    }

    return publishSettings;
  };

  account.defaultSubscriptionId = function () {
    return account.readConfig().subscription;
  };

  account.lookupSubscriptionId = function (subscription) {
    // use default subscription if not passed as an argument
    if (subscription === undefined) {
      subscription = account.readConfig().subscription;
    }

    // load and normalize publish settings
    var publishSettings = account.readPublishSettings();

    if (publishSettings && publishSettings.PublishProfile) {
      var subs = publishSettings.PublishProfile.Subscription;
      if (subs === 'undefined') {
        subs = [];
      } else if (typeof (subs[0]) === 'undefined') {
        subs = [subs];
      }

      // use subscription id when the subscription name matches
      for (var index in subs) {
        if (subs[index]['@'].Name === subscription) {
          return subs[index]['@'].Id;
        }
      }
    }

    return subscription;
  };

  account.managementCertificate = function () {
    var pemFile = path.join(utils.azureDir(), 'managementCertificate.pem');
    log.silly('Reading pem', pemFile);
    return keyFiles.readFromFile(pemFile);
  };

  account.endpointHost = function () {
    return account.readConfig().endpoint;
  };

  account.endpointPort = function () {
    return account.readConfig().port;
  };

  account.hostNameSuffix = function () {
    return process.env.AZURE_HOSTNAME_SUFFIX || constants.DEFAULT_HOSTNAME_SUFFIX;
  };
};

<commit_msg>#350: showing friendly error in case wrong version of publishsettings file is used.<commit_after>/**
* Copyright 2011 Microsoft Corporation
*
* 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 assert = require('assert');
var fs = require('fs');
var path = require('path');
var util = require('util');
var xml2js = require('xml2js');
var azureUtils = require('../../util/util');
var common = require('../common');
var utils = require('../utils');
var constants = require('../constants');
var pfx2pem = require('../../util/certificates/pkcs').pfx2pem;
var keyFiles = require('../keyFiles');
var cacheUtils = require('../cacheUtils');

exports.init = function (cli) {
  var log = cli.output;
  var azureDirectory = utils.azureDir();
  var pemPath = path.join(azureDirectory, 'managementCertificate.pem');
  var publishSettingsFilePath = path.join(azureDirectory, 'publishSettings.xml');

  var account = cli.category('account')
        .description('Manage your account information and publish settings');

  account.command('download')
        .whiteListPowershell()
        .description('Launch a browser to download your publishsettings file')
        .execute(function (options, callback) {
          try {
            var url = 'https://windows.azure-preview.com/download/publishprofile.aspx?client=nodejsxplat';
            common.launchBrowser(url);
            log.help('Save the downloaded file, then execute the command');
            log.help('  account import <file>');
            callback();
          }
          catch (err) {
            callback(err);
          }
        });

  account.command('import <file>')
        .whiteListPowershell()
        .description('Import a publishsettings file or certificate for your account')
        .execute(function (file, options, callback) {
          log.verbose('Importing file ' + file);

          // Is it a .pem file?
          var keyCertValues = keyFiles.readFromFile(file);
          var keyPresent = !!keyCertValues.key;
          var certPresent = !!keyCertValues.cert;
          if (keyPresent + certPresent === 1) {
            // Exactly one of them present.  Tell the user about the error.
            // Do not try this file as xml or pfx
            callback('File ' + file + ' needs to contain both private key and cert, but only ' +
                    (keyCertValues.key ? 'key' : 'certificate') + ' was found.');
          } else if (keyCertValues.key && keyCertValues.cert) {
            // Both key and cert are present.
            keyFiles.writeToFile(pemPath, keyCertValues);
            log.verbose('Key and cert have been written to ' + pemPath);
          } else {
            // Try to open as publishsettings or pfx.
            log.silly(file + ' does not appear to be a PEM file. Reading as publish settings file.');
            var parser = new xml2js.Parser();
            var publishSettings = null;
            parser.on('end', function (settings) { publishSettings = settings; });
            var readBuffer = fs.readFileSync(file);
            try {
              parser.parseString(readBuffer);
            } catch (err) {
              if (err.toString().indexOf('Non-whitespace before first tag') === -1) {
                // This looks like an xml parsing error, not PFX.
                callback(err);
              }

              log.silly('Unable to read file as xml publish settings file. Assuming it is pfx');
              publishSettings = null;
            }

            if (publishSettings) {
              processSettings(file, publishSettings);
            } else {
              convertPfx(readBuffer);
            }
          }

          cacheUtils.clear();
          if (publishSettings && publishSettings.PublishProfile.Subscription['@']) {
            return cli.category('site').doSpacesGet({ subscription: publishSettings.PublishProfile.Subscription['@'].Id },
                function(error) {
              if (error) {
                var message = error.Message;
                if (typeof message === 'string' && message.indexOf('The subscription is not authorized for this feature.') >= 0) {
                  log.silly('Error getting site locations - ignored: ', error);
                  error = undefined;
                }
              }
              callback(error);
            });
          }
          return callback();

          function convertPfx(pfx) {
            var pem = pfx2pem(pfx);
            utils.writeFileSyncMode(pemPath, pem.toString(), 'utf8');
            log.verbose('Converted PFX data to ' + pemPath);
          }

          function processSettings(file, settings) {
            if (!settings.PublishProfile || !settings.PublishProfile.ManagementCertificate) {
              throw new Error('Wrong format of the publishSettings file. Make sure you use "azure account download" to get your publish settings file instead of getting it directly from the portal.');
            }

            var attribs = settings.PublishProfile['@'];
            var subs = settings.PublishProfile.Subscription;
            if (typeof subs === 'undefined' || subs === undefined) {
              subs = [];
            } else if (typeof (subs[0]) === 'undefined') {
              subs = [subs];
            }

            if (attribs.Url) {
              var endpointInfo = utils.validateEndpoint(attribs.Url);
              var config = account.readConfig();
              config.endpoint = endpointInfo.endpointHost;
              config.port = endpointInfo.endpointPort;
              account.writeConfig(config);
              log.info('Setting service endpoint to:', config.endpoint);
              log.info('Setting service port to:', config.port);
            }

            if (subs.length === 0) {
              log.warning('Importing profile with no subscriptions');
            }
            else {
              for (var index in subs) {
                log.info('Found subscription:', subs[index]['@'].Name);
                log.verbose('  Id:', subs[index]['@'].Id);
              }
            }

            log.verbose('Parsing management certificate');
            var pfx = new Buffer(attribs.ManagementCertificate, 'base64');
            convertPfx(pfx);

            log.verbose('Storing account information at', publishSettingsFilePath);
            utils.writeFileSyncMode(publishSettingsFilePath, readBuffer); // folder already created by convertPfx()
            if (subs.length !== 0) {
              log.info('Setting default subscription to:', subs[0]['@'].Name);
              var config = account.readConfig();
              config.subscription = subs[0]['@'].Id;
              account.writeConfig(config);
            }
            log.warn('The \'' + file + '\' file contains sensitive information.');
            log.warn('Remember to delete it now that it has been imported.');
            log.info('Account publish settings imported successfully');
          }
        });

  account.command('clear')
        .whiteListPowershell()
        .description('Remove any of the stored account info stored by import or config set')

        .action(function () {
          function deleteIfExists(file, isDir) {
            if (path.existsSync(file)) {
              log.silly('Removing ' + file);
              (isDir ? fs.rmdirSync : fs.unlinkSync)(file);
              return true;
            } else {
              log.silly(file + ' does not exist');
            }
          }
          var isDeleted = deleteIfExists(pemPath);
          isDeleted = deleteIfExists(publishSettingsFilePath) || isDeleted; // in this order only
          isDeleted = account.clearConfig() || isDeleted;
          isDeleted = cacheUtils.clear() || isDeleted;
          try {
            deleteIfExists(azureDirectory, true);
          } catch (err) {
            log.warn('Couldn\'t remove ' + azureDirectory);
          }
          log.info(isDeleted ? 'Account settings cleared successfully'
              : 'Account settings are already clear');
        });

  var affinityGroup = account.category('affinity-group')
        .description('Commands to manage your Azure affinity groups');

  affinityGroup.command('list')
        .description('List locations available for your account')
        .execute(function (options, callback) {
          listLAG('AffinityGroups', options, callback);
        });

  function listLAG(what, options, callback) {
    var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
        cli.category('account'), log);

    var textName = what.replace(/([A-Z])/g, ' $1').toLowerCase();
    var progress = cli.progress('Fetching' + textName);
    utils.doServiceManagementOperation(channel, 'list' + what, function (error, response) {
      progress.end();
      if (!error) {
        if (response.body.length > 0) {
          log.table(response.body, function (row, item) {
            row.cell('Name', item.Name);
            if ('DisplayName' in item) { // for location
              row.cell('Display Name', item.DisplayName);
            }
            if ('Label' in item) {
              row.cell('Label', Buffer(item.Label, 'base64').toString());
            }
            if ('Location' in item) {
              row.cell('Location', item.Location || '');
            }
          });
        } else {
          if (log.format().json) {
            log.json([]);
          } else {
            log.info('No' + textName + ' found');
          }
        }
      }
      callback(error);
    });
  }
  account.listLAG = listLAG;

  var storage = account.category('storage')
        .description('Commands to manage your Azure storage account');

  storage.command('list')
        .description('List storage accounts available for your account')
        .execute(function (options, callback) {
          var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
              cli.category('account'), log);

          var progress = cli.progress('Fetching storage accounts');
          utils.doServiceManagementOperation(channel, 'listStorageAccounts', function (error, response) {
            progress.end();
            if (!error) {
              if (response.body.length > 0) {
                log.table(response.body, function (row, item) {
                  row.cell('Name', item.ServiceName);
                  var storageServiceProperties = item.StorageServiceProperties;
                  if ('Label' in storageServiceProperties) {
                    row.cell('Label', Buffer(storageServiceProperties.Label, 'base64').toString());
                  }
                  if ('Location' in storageServiceProperties) {
                    row.cell('Location', storageServiceProperties.Location);
                  }
                });
              } else {
                if (log.format().json) {
                  log.json([]);
                } else {
                  log.info('No storage accounts found');
                }
              }
            }
            callback(error);
          });
        });

  account.readPublishSettings = function () {
    var publishSettings = {};

    var parser = new xml2js.Parser();
    parser.on('end', function (result) { publishSettings = result; });
    try {
      log.silly('Reading publish settings', publishSettingsFilePath);
      var readBuffer = fs.readFileSync(publishSettingsFilePath);
      parser.parseString(readBuffer);
    } catch (err) {
      // publish settings file is not expected for all scenarios
    }

    return publishSettings;
  };

  account.defaultSubscriptionId = function () {
    return account.readConfig().subscription;
  };

  account.lookupSubscriptionId = function (subscription) {
    // use default subscription if not passed as an argument
    if (subscription === undefined) {
      subscription = account.readConfig().subscription;
    }

    // load and normalize publish settings
    var publishSettings = account.readPublishSettings();

    if (publishSettings && publishSettings.PublishProfile) {
      var subs = publishSettings.PublishProfile.Subscription;
      if (subs === 'undefined') {
        subs = [];
      } else if (typeof (subs[0]) === 'undefined') {
        subs = [subs];
      }

      // use subscription id when the subscription name matches
      for (var index in subs) {
        if (subs[index]['@'].Name === subscription) {
          return subs[index]['@'].Id;
        }
      }
    }

    return subscription;
  };

  account.managementCertificate = function () {
    var pemFile = path.join(utils.azureDir(), 'managementCertificate.pem');
    log.silly('Reading pem', pemFile);
    return keyFiles.readFromFile(pemFile);
  };

  account.endpointHost = function () {
    return account.readConfig().endpoint;
  };

  account.endpointPort = function () {
    return account.readConfig().port;
  };

  account.hostNameSuffix = function () {
    return process.env.AZURE_HOSTNAME_SUFFIX || constants.DEFAULT_HOSTNAME_SUFFIX;
  };
};

<|endoftext|>


<commit_before>'use strict';

var lastOpenedDrawer = null;

function init ($) {

  // Tooltips can remain in the way on touch screens.
  if (!isTouch()) {
    $('.tip').tipsy();
  } else {
    // Drawer info tips should be displayed on touchscreens.
    $('#drawer').find('.tip').tipsy();
  }
  $.fn.tipsy.defaults = {
    fade: true,
    gravity: 'n',
    opacity: 0.75
  };

  var querystring = queryParms();

  if (querystring.notify) {
    showNotification(querystring.notify, querystring.notifytype);
  }

  if (querystring.drawer) {
    openDrawer('#drawer');
  }

  $('#drawerToggle').click(function(event) {
    toggleDrawer('#drawer');
    event.preventDefault();
  });

  $('#notification').click(function(event) {
    closeNotification();
    event.preventDefault();
  });

  function reload() {
    //strip '#' so form submission does not fail
    var url = window.location.href;
    url = url.replace(/#$/, '');
    window.location = url;
  }

  function queryParms() {
    var params = {};
    if (location.search) {
      location.search.substr(1).split('&').forEach(function(item) {
        params[item.split('=')[0]] = item.split('=')[1].replace(/[_\+]/g, ' ');
      });
    }
    return params;
  }

  function isTouch() {
    try { document.createEvent('TouchEvent'); return true; }
    catch (e) { return false; }
  }

  function closeDrawer(id, callback) {
    lastOpenedDrawer = null;
    $('html, body').animate({ scrollTop: 0 });
    $(id).animate({right: '-300px'}, 300, function () {
      $(id).css('display', 'none');
      if (callback) { callback(); }
    });
  }

  function openDrawer(id, callback) {
    function closeOpenDraw(callback) {
      if (lastOpenedDrawer) {
        closeDrawer(lastOpenedDrawer, callback);
      } else {
        callback();
      }
    }

    closeOpenDraw(function () {
      lastOpenedDrawer = id;
      $(id).css('display', 'block').animate({right: '0'}, 300, function () {
        if (callback) { callback(); }
      });
    });

  }

  function toggleDrawer(id, openCallback, closeCallback) {
    if (lastOpenedDrawer === id) {
      closeDrawer(id, closeCallback);
    } else {
      openDrawer(id, openCallback);
    }
  }

  function closeNotification() {
    var notify = $('#notification');
    notify.hide();
    notify.find('span').html('');
  }

  function showNotification(note, type)  {
    var notify = $('#notification');
    notify.hide();

    // Notification types: 'info', 'warn', 'success', 'urgent'.
    // - default: 'urgent'
    notify.removeClass('info warn urgent');
    notify.addClass(type ? type : 'urgent');

    notify.find('span').html(note);
    notify.css('left', 'calc(50% - ' + (notify.width() / 2) + 'px)');
    notify.show();
  }

  return {
    reload: reload
    , queryParms: queryParms
    , closeDrawer: closeDrawer
    , toggleDrawer: toggleDrawer
    , closeNotification: closeNotification
    , showNotification: showNotification
  };
}

module.exports = init;
<commit_msg>close drawer when menu links are clicked and prepare form before opening<commit_after>'use strict';

var lastOpenedDrawer = null;

function init ($) {

  // Tooltips can remain in the way on touch screens.
  if (!isTouch()) {
    $('.tip').tipsy();
  } else {
    // Drawer info tips should be displayed on touchscreens.
    $('#drawer').find('.tip').tipsy();
  }
  $.fn.tipsy.defaults = {
    fade: true,
    gravity: 'n',
    opacity: 0.75
  };

  var querystring = queryParms();

  if (querystring.notify) {
    showNotification(querystring.notify, querystring.notifytype);
  }

  if (querystring.drawer) {
    openDrawer('#drawer');
  }

  $('#drawerToggle').click(function(event) {
    toggleDrawer('#drawer');
    event.preventDefault();
  });

  $('#notification').click(function(event) {
    closeNotification();
    event.preventDefault();
  });

  $('.navigation a').click(function navigationClick ( ) {
    closeDrawer('#drawer');
  });

  function reload() {
    //strip '#' so form submission does not fail
    var url = window.location.href;
    url = url.replace(/#$/, '');
    window.location = url;
  }

  function queryParms() {
    var params = {};
    if (location.search) {
      location.search.substr(1).split('&').forEach(function(item) {
        params[item.split('=')[0]] = item.split('=')[1].replace(/[_\+]/g, ' ');
      });
    }
    return params;
  }

  function isTouch() {
    try { document.createEvent('TouchEvent'); return true; }
    catch (e) { return false; }
  }

  function closeDrawer(id, callback) {
    lastOpenedDrawer = null;
    $('html, body').animate({ scrollTop: 0 });
    $(id).animate({right: '-300px'}, 300, function () {
      $(id).css('display', 'none');
      if (callback) { callback(); }
    });
  }

  function openDrawer(id, callback) {
    function closeOpenDraw(callback) {
      if (lastOpenedDrawer) {
        closeDrawer(lastOpenedDrawer, callback);
      } else {
        callback();
      }
    }

    closeOpenDraw(function () {
      lastOpenedDrawer = id;
      if (callback) { callback(); }
      $(id).css('display', 'block').animate({right: '0'}, 300);
    });

  }

  function toggleDrawer(id, openCallback, closeCallback) {
    if (lastOpenedDrawer === id) {
      closeDrawer(id, closeCallback);
    } else {
      openDrawer(id, openCallback);
    }
  }

  function closeNotification() {
    var notify = $('#notification');
    notify.hide();
    notify.find('span').html('');
  }

  function showNotification(note, type)  {
    var notify = $('#notification');
    notify.hide();

    // Notification types: 'info', 'warn', 'success', 'urgent'.
    // - default: 'urgent'
    notify.removeClass('info warn urgent');
    notify.addClass(type ? type : 'urgent');

    notify.find('span').html(note);
    notify.css('left', 'calc(50% - ' + (notify.width() / 2) + 'px)');
    notify.show();
  }

  return {
    reload: reload
    , queryParms: queryParms
    , closeDrawer: closeDrawer
    , toggleDrawer: toggleDrawer
    , closeNotification: closeNotification
    , showNotification: showNotification
  };
}

module.exports = init;
<|endoftext|>


<commit_before>var path = require("path");
var fs = require("fs");

// Used for debugging. If this method is not called, it means this module is
// entirely bug free.
function die(message) {
	console.log.apply(console, [ message ]);
	process.exit(1);
}

function run(options) {
	var transformer = options.fibers ? "streamline/lib/fibers/transform"
																	 : "streamline/lib/callbacks/transform";
	var streamline = require(transformer).transform;

	var extension = "." + path.basename(process.argv[0]);
	var filename = process.argv[1];

	process.execPath = require.main.filename;

	// If CoffeeScript is used, we load it and duck.
	if (! require.extensions['.coffee']) {
		require.extensions['.coffee'] = function (module, filename) {
			try {
				require("coffee-script")
			} catch (e) {
				throw new Error("Cannot load CoffeeScript to compile: '" + filename + "'");
			}
			require.extensions['.coffee'](module, filename);
		}
	}

	var streamliners = {
		node: function (module, filename, code) {
			var cachedTransform = require("streamline/lib/compiler/compile").cachedTransformSync;
			streamlined = cachedTransform(code, filename, streamline, options);
			module._compile(streamlined, filename)
		},
		coffee: function (module, filename, code) {
			try {
				coffee = require("coffee-script");
			} catch (e) {
				throw new Error("Cannot load CoffeeScript to compile: '" + filename + "'");
			}
			var code = coffee.compile(fs.readFileSync(filename, "utf8"), { filename: filename });
			streamliners.node(module, filename, code, options);
		}
	};

	// If Streamlined CoffeeScript is used, load CoffeeScript and register a new
	// handler for the `._coffee` extension.
	require.extensions['._coffee'] = function (module, filename) {
		var code = fs.readFileSync(filename, "utf8");
		streamliners.coffee(module, filename, code);
	}

	// Streamlined JavaScript extension handler.
	require.extensions['._js'] = function (module, filename) {
		var code = fs.readFileSync(filename, "utf8");
		streamliners.node(module, filename, code);
	}

	if (options.registerOnly) return;

	var mainModule = require.main;

	// Clear th emain module cache.
	if (mainModule.moduleCache) {
		mainModule.moduleCache = {};
	}

	// Set the module paths.
	mainModule.paths = require("module")._nodeModulePaths(path.dirname(filename));

	mainModule.filename = filename ? fs.realpathSync(filename) : '.';
	process.argv.splice(1, 1, mainModule.filename)

	// Load the target file and evaluate it.
	fs.readFile(filename, "utf8", function (error, code) {
		var streamlined, javacript, match;
		// Strip the shebang line, while preserving line count.
		if (match = /^#!.*([^\u0000]*)$/.exec(code)) code = match[1];
		// Execute the code.
		streamliners[extension.substring(2)](mainModule, filename, code);
	});
}

module.exports.run = run;
<commit_msg>introduced cache option to control caching of transformed files<commit_after>var path = require("path");
var fs = require("fs");

// Used for debugging. If this method is not called, it means this module is
// entirely bug free.
function die(message) {
	console.log.apply(console, [ message ]);
	process.exit(1);
}

function run(options) {
	var transformer = options.fibers ? "streamline/lib/fibers/transform"
																	 : "streamline/lib/callbacks/transform";
	var streamline = require(transformer).transform;

	var extension = "." + path.basename(process.argv[0]);
	var filename = process.argv[1];

	process.execPath = require.main.filename;

	// If CoffeeScript is used, we load it and duck.
	if (! require.extensions['.coffee']) {
		require.extensions['.coffee'] = function (module, filename) {
			try {
				require("coffee-script")
			} catch (e) {
				throw new Error("Cannot load CoffeeScript to compile: '" + filename + "'");
			}
			require.extensions['.coffee'](module, filename);
		}
	}

	var streamliners = {
		node: function (module, filename, code) {
			var cachedTransform = require("streamline/lib/compiler/compile").cachedTransformSync;
			streamlined = options.cache ? cachedTransform(code, filename, streamline, options) : streamline(code, options);
			module._compile(streamlined, filename)
		},
		coffee: function (module, filename, code) {
			try {
				coffee = require("coffee-script");
			} catch (e) {
				throw new Error("Cannot load CoffeeScript to compile: '" + filename + "'");
			}
			var code = coffee.compile(fs.readFileSync(filename, "utf8"), { filename: filename });
			streamliners.node(module, filename, code, options);
		}
	};

	// If Streamlined CoffeeScript is used, load CoffeeScript and register a new
	// handler for the `._coffee` extension.
	require.extensions['._coffee'] = function (module, filename) {
		var code = fs.readFileSync(filename, "utf8");
		streamliners.coffee(module, filename, code);
	}

	// Streamlined JavaScript extension handler.
	require.extensions['._js'] = function (module, filename) {
		var code = fs.readFileSync(filename, "utf8");
		streamliners.node(module, filename, code);
	}

	if (options.registerOnly) return;

	var mainModule = require.main;

	// Clear th emain module cache.
	if (mainModule.moduleCache) {
		mainModule.moduleCache = {};
	}

	// Set the module paths.
	mainModule.paths = require("module")._nodeModulePaths(path.dirname(filename));

	mainModule.filename = filename ? fs.realpathSync(filename) : '.';
	process.argv.splice(1, 1, mainModule.filename)

	// Load the target file and evaluate it.
	fs.readFile(filename, "utf8", function (error, code) {
		var streamlined, javacript, match;
		// Strip the shebang line, while preserving line count.
		if (match = /^#!.*([^\u0000]*)$/.exec(code)) code = match[1];
		// Execute the code.
		streamliners[extension.substring(2)](mainModule, filename, code);
	});
}

module.exports.run = run;
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
  Message = mongoose.model('Message'),
  Broadcast = mongoose.model('Broadcast'),
  User = mongoose.model('User'),
  passport = require('passport');

var twilio = require('twilio');

/**
 *
 * @param resp
 * @param res
 */
function twilioResponse(res, resp) {
  res.writeHead(200, {
    'Content-Type': 'text/xml'
  });
  res.end(resp.toString());
}

/**
 * Incoming Voice
 */
exports.voice = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content:'call!'
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.say({voice: 'alice'}, 'Hello, this is PartyBot.  Please send me a text message.  Party on!');
    twilioResponse(res, resp);
  });

};

/**
 * Incoming SMS
 */
exports.sms = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content:req.body.Body
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.message('Party! Broadcast id: ' + broadcast._id);
    twilioResponse(res, resp);
  });

};<commit_msg>updates to incoming sms and voice handling<commit_after>'use strict';

var mongoose = require('mongoose'),
  moment = require('moment'),
  Message = mongoose.model('Message'),
  Broadcast = mongoose.model('Broadcast'),
  User = mongoose.model('User'),
  passport = require('passport');

var twilio = require('twilio');

/**
 *
 * @param resp
 * @param res
 */
function twilioResponse(res, resp) {
  res.writeHead(200, {
    'Content-Type': 'text/xml'
  });
  res.end(resp.toString());
}

/**
 * Incoming Voice
 */
exports.voice = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content:'Hello world. Let\'s party!'
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.say({voice: 'alice'}, 'Hello world. Let\'s party!');
    twilioResponse(res, resp);
  });

};

/**
 * Incoming SMS
 */
exports.sms = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content:req.body.Body
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.message('Party on.. I broadcast your message [' + broadcast._id +'} at '+moment.parse(broadcast.runAt).format('YYYY MMMM Do, h:mm:ss a')+' Zulu for '+broadcast.runSeconds+' seconds!');
    twilioResponse(res, resp);
  });

};<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 fs = require('fs');
var path = require('path');

var deploy_templates = require('deployment/templates');
var deploy_services = require('deployment/services');
var deploy_files = require('deployment/files');

var service_management = require('service_management');
var manifest = require('manifest');
var manifest_constants = require('manifest/constants');
var config = require('util/config');
var fsutil = require('util/fs');
var flowctrl = require('util/flow_control');

var async = require('extern/async');
var sprintf = require('extern/sprintf').sprintf;

var INSTANCE_NAME_RE = /^[a-zA-Z0-9_\-]+$/;

function Instance(name) {
  this.name = name;
  this.root = path.join(config.get().app_dir, name);
  this._bundlename = null;
}

/**
 * Check if an instance exists
 *
 * @param {String} instance_name The name of the instance
 * @param {Function} callback Callback which is called with a boolean 'exists'
 */
Instance.prototype.exists = function(callback) {
  path.exists(this.root, callback);
};

/**
 * Get the name of the bundle used by an instance. This is probably the
 * nastiest part of this whole instance filesystem layout because it requires
 * reading a link. To keep things fast this gets cached on the Instance object.
 *
 * @param {Function} callback Callback which is called with (err, bundlename)
 */
Instance.prototype.get_bundlename = function(callback) {
  var self = this;
  if (self._bundlename) {
    process.nextTick(function() {
      callback(null, self._bundlename);
    });
  }
  else {
    var bundle_link_path = path.join(self.root, 'bundle');
    fs.readlink(bundle_link_path, function(err, target) {
      if (!err) {
        self._bundlename = path.basename(target);
      }
      return callback(err, self._bundlename);
    });
  }
};

/**
 * Get the path at which a version of this instance would reside. This does
 * not guarantee that this version exists, merely returns the hypothetical
 * path at which it would reside.
 *
 * @param {String} version    The version to get the path to
 * @param {Function} callback A callback that takes (err, version_path)
 */
Instance.prototype.get_version_path = function(version, callback) {
  var self = this;
  self.get_bundlename(function(err, bundle_name) {
    var bundle_name_full = bundle_name + '@' + version;
    var version_path = path.join(self.root, 'versions', bundle_name_full);
    return callback(err, version_path);
  });
};

/**
 * Get the path at which the extracted bundle for the specified version would
 * reside. Again, this doesn't verify that the path actually exists.
 *
 * @param {String} version    The version to get the path to
 * @param {Function} callback A callback that takes (err, bundle_version_path)
 */
Instance.prototype.get_bundle_version_path = function(version, callback) {
  var self = this;
  self.get_bundlename(function(err, bundle_name) {
    var bundle_name_full = bundle_name + '@' + version;
    var bundle_version_path = path.join(config.get().extracted_dir, bundle_name, bundle_name_full);
    return callback(err, bundle_version_path);
  });
};

/**
 * Remove all data related to an instance, including the instance, its data,
 * and any services. This performs very little validation (doesn't even check
 * that the instance exists first), it just starts deleting stuff.
 *
 * @param {Function} callback Callback which is called when the cleanup process has finished
 */
Instance.prototype.destroy = function(callback) {
  var manager = service_management.get_default_manager().get_manager();

  var ops = [
    async.apply(flowctrl.call_ignoring_error, fsutil.rmtree, null, this.root)
  ];

  manager.get_service(this.name, function (err, service) {
    if (!err) {
      ops.push(async.apply(flowctrl.call_ignoring_error, service.destroy, service));
    }

    async.parallel(ops, function(err) {
      callback();
    });
  });
};

/**
 * Given a bundle version, hard hard link all the files into place, render
 * any templates and resolve all data files.
 *
 * @param {String} version    The version of the bundle to prepare
 * @param {Function} callback A callback fired with (err)
 */
Instance.prototype.prepare_version = function(version, callback) {
  var self = this;
  var extracted_bundle_path, bundle_name, bundle_name_full, manifest_obj;
  var instance_version_path, ignored_paths;

  var bundle_link_path = path.join(self.root, 'bundle');

  async.series([
    // Resolve the path at which the version will reside
    function(callback) {
      self.get_version_path(version, function(err, vp) {
        instance_version_path = vp;
        return callback(err);
      });
    },

    // Resolve the path to the extracted bundle
    function(callback) {
      self.get_bundle_version_path(version, function(err, bvp) {
        extracted_bundle_path = bvp;
        return callback(err);
      });
    },

    // Make sure this version doesn't exist
    function(callback) {
      path.exists(instance_version_path, function(exists) {
        var err = null;
        if (exists) {
          err = new Error(sprintf('Instance \'%s\' already has version \'%s\'', self.name, version));
        }
        return callback(err);
      });
    },

    // Make sure an extracted bundle exists for the requested version
    function(callback) {
      fs.stat(extracted_bundle_path, function(err, stats) {
        if (!err && !stats.isDirectory()) {
          err = new Error('No bundle for version \'' + version + '\'');
        }
        return callback(err);
      });
    },

    // Create the directory for the target version
    function(callback) {
      fsutil.ensure_directory(instance_version_path, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifest_path = path.join(extracted_bundle_path, manifest_constants.MANIFEST_FILENAME);
      manifest.get_manifest_object(manifest_path, true, function(err, manifest_obj_) {
        manifest_obj = manifest_obj_;
        ignored_paths = manifest_obj.template_files.concat(manifest_obj.data_files);
        return callback(err);
      });
    },

    // Mirror the directory structure from the extracted bundle in the instance directory
    function(callback) {
      fsutil.tree_to_template(extracted_bundle_path, ignored_paths, function(err, template_object) {
        if (err) {
          return callback(err);
        }

        // No directories
        if (Object.keys(template_object).length === 0) {
          return callback();
        }

        fsutil.template_to_tree(instance_version_path, template_object, true, callback);
      });
    },

    // Realize (render and save) templates
    function(callback) {
      var instance_data = deploy_templates.get_instance_template_object(self.name, instance_version_path);
      deploy_templates.realize_application_templates(manifest_obj, instance_data, extracted_bundle_path,
                                                     instance_version_path, callback);
    },

    // Hard-link all the files (except template and data files)
    function(callback) {
      fsutil.hard_link_files(extracted_bundle_path, instance_version_path, ignored_paths, callback);
    },

    // Resolve the data files
    function(callback) {
      var instance_data_root = path.join(self.root, 'data');
      deploy_files.resolve_data_files(extracted_bundle_path, instance_data_root, instance_version_path,
                                      manifest_obj.data_files, callback);
    }
  ], callback);
};

/**
 * Point the 'current' symlink to the specified version. Will verify
 * that the specified version exists before taking action.
 *
 * @param {String} version    The version to activate
 * @param {Function} callback A callback fired with (err)
 */
Instance.prototype.activate_version = function(version, callback) {
  var self = this;
  var new_version_path;
  var new_version_link = path.join(self.root, 'new');
  var current_version_link = path.join(self.root, 'current');

  async.series([
    // Get the path to the specified version
    function(callback) {
      self.get_version_path(version, function(err, vp) {
        new_version_path = vp;
        return callback(err);
      });
    },

    // Make sure the version exists
    function(callback) {
      path.exists(new_version_path, function(exists) {
        var err = null;
        if (!exists) {
          err = new Error('Cannot activate nonexistent version \'' + version + '\'');
        }
        return callback(err);
      });
    },

    // Create the new link
    async.apply(fs.symlink, new_version_path, new_version_link),

    // Atomically move it into place
    async.apply(fs.rename, new_version_link, current_version_link)
  ], callback);
};

/**
 * Create an instance with a specified name for the specified bundle. The
 * instance will initially use the specified version of the bundle.
 *
 * @param {String} instance_name  A name to give to the instance
 * @param {String} bundle_name    The name of the bundle to use
 * @param {String} bundle_version The version of the bundle to use
 * @param {Function} callback     A callback fired with (err)
 */
exports.create_instance = function(instance_name, bundle_name, bundle_version, callback) {
  if (!INSTANCE_NAME_RE.exec(instance_name)) {
    return callback(new Error('Invalid instance name'));
  }

  var manifest_obj, ignored_paths, instance;
  var bundle_name_full = bundle_name + '@' + bundle_version;

  // Get the paths to the extracted bundle and the manifest
  var extracted_bundle_root = path.join(config.get().extracted_dir, bundle_name);
  var extracted_bundle_path = path.join(extracted_bundle_root, bundle_name_full);

  var instance_path = path.join(config.get().app_dir, instance_name);
  var instance_data_root = path.join(instance_path, 'data');
  var instance_versions_root = path.join(instance_path, 'versions');
  var instance_bundle_link = path.join(instance_path, 'bundle');
  var instance_current_link = path.join(instance_path, 'current');
  var instance_version_path = path.join(instance_versions_root, bundle_name_full);

  async.series([
    // Make sure that the extracted bundle path exists
    function(callback) {
      path.exists(extracted_bundle_path, function(exists) {
        if (!exists) {
          return callback(new Error('Invalid bundle name or version'));
        }

        callback();
      });
    },

    // Make sure there isn't already an instance with this name
    function(callback) {
      path.exists(instance_path, function(exists) {
        if (exists) {
          return callback(new Error('Instance name already in use'));
        }
        callback();
      });
    },

    // Create the instance directory
    function(callback) {
      fsutil.ensure_directory(instance_path, function(err) {
        if (!err) {
          instance = new Instance(instance_name);
        }
        callback(err);
      });
    },

    // Create the data and versions directories
    async.apply(fsutil.ensure_directory, instance_data_root),
    async.apply(fsutil.ensure_directory, instance_versions_root),

    // Create the bundle symlink
    async.apply(fs.symlink, extracted_bundle_root, instance_bundle_link),

    // Prepare the specified version
    function(callback) {
      instance.prepare_version(bundle_version, callback);
    },

    // Symlink it
    function(callback) {
      instance.activate_version(bundle_version, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifest_path = path.join(extracted_bundle_path, manifest_constants.MANIFEST_FILENAME);
      manifest.get_manifest_object(manifest_path, true, function(err, manifest_obj_) {
        manifest_obj = manifest_obj_;
        return callback(err);
      });
    },

    // Create a runit service
    function(callback) {
      deploy_services.create_service(instance_name, instance_current_link, manifest_obj, callback);
    }
  ],

  function(error) {
    if (error && instance) {
      instance.destroy(function() {
        return callback(error);
      });
    }
    else {
      callback(error);
    }
  });
};

/**
 * Get an Instance object for an instance whose existence is verified.
 *
 * @param {String} instance_name  The name of the instance to retrieve
 * @param {Function} callback     A callback that takes (err, instance)
 */
exports.get_instance = function(instance_name, callback) {
  var instance = new Instance(instance_name);
  instance.exists(function(exists) {
    if (!exists) {
      return callback(new Error('Instance doesn\'t exist'));
    }
    else {
      return callback(null, instance);
    }
  });
};
<commit_msg>Fix bug in async call<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 fs = require('fs');
var path = require('path');

var deploy_templates = require('deployment/templates');
var deploy_services = require('deployment/services');
var deploy_files = require('deployment/files');

var service_management = require('service_management');
var manifest = require('manifest');
var manifest_constants = require('manifest/constants');
var config = require('util/config');
var fsutil = require('util/fs');
var flowctrl = require('util/flow_control');

var async = require('extern/async');
var sprintf = require('extern/sprintf').sprintf;

var INSTANCE_NAME_RE = /^[a-zA-Z0-9_\-]+$/;

function Instance(name) {
  this.name = name;
  this.root = path.join(config.get().app_dir, name);
  this._bundlename = null;
}

/**
 * Check if an instance exists
 *
 * @param {String} instance_name The name of the instance
 * @param {Function} callback Callback which is called with a boolean 'exists'
 */
Instance.prototype.exists = function(callback) {
  path.exists(this.root, callback);
};

/**
 * Get the name of the bundle used by an instance. This is probably the
 * nastiest part of this whole instance filesystem layout because it requires
 * reading a link. To keep things fast this gets cached on the Instance object.
 *
 * @param {Function} callback Callback which is called with (err, bundlename)
 */
Instance.prototype.get_bundlename = function(callback) {
  var self = this;
  if (self._bundlename) {
    process.nextTick(function() {
      callback(null, self._bundlename);
    });
  }
  else {
    var bundle_link_path = path.join(self.root, 'bundle');
    fs.readlink(bundle_link_path, function(err, target) {
      if (!err) {
        self._bundlename = path.basename(target);
      }
      return callback(err, self._bundlename);
    });
  }
};

/**
 * Get the path at which a version of this instance would reside. This does
 * not guarantee that this version exists, merely returns the hypothetical
 * path at which it would reside.
 *
 * @param {String} version    The version to get the path to
 * @param {Function} callback A callback that takes (err, version_path)
 */
Instance.prototype.get_version_path = function(version, callback) {
  var self = this;
  self.get_bundlename(function(err, bundle_name) {
    var bundle_name_full = bundle_name + '@' + version;
    var version_path = path.join(self.root, 'versions', bundle_name_full);
    return callback(err, version_path);
  });
};

/**
 * Get the path at which the extracted bundle for the specified version would
 * reside. Again, this doesn't verify that the path actually exists.
 *
 * @param {String} version    The version to get the path to
 * @param {Function} callback A callback that takes (err, bundle_version_path)
 */
Instance.prototype.get_bundle_version_path = function(version, callback) {
  var self = this;
  self.get_bundlename(function(err, bundle_name) {
    var bundle_name_full = bundle_name + '@' + version;
    var bundle_version_path = path.join(config.get().extracted_dir, bundle_name, bundle_name_full);
    return callback(err, bundle_version_path);
  });
};

/**
 * Remove all data related to an instance, including the instance, its data,
 * and any services. This performs very little validation (doesn't even check
 * that the instance exists first), it just starts deleting stuff.
 *
 * @param {Function} callback Callback which is called when the cleanup process has finished
 */
Instance.prototype.destroy = function(callback) {
  var manager = service_management.get_default_manager().get_manager();

  var ops = [
    async.apply(flowctrl.call_ignoring_error, fsutil.rmtree, null, this.root)
  ];

  manager.get_service(this.name, function (err, service) {
    if (!err) {
      ops.push(async.apply(flowctrl.call_ignoring_error, service.destroy, service));
    }

    async.parallel(ops, function(err) {
      callback();
    });
  });
};

/**
 * Given a bundle version, hard hard link all the files into place, render
 * any templates and resolve all data files.
 *
 * @param {String} version    The version of the bundle to prepare
 * @param {Function} callback A callback fired with (err)
 */
Instance.prototype.prepare_version = function(version, callback) {
  var self = this;
  var extracted_bundle_path, bundle_name, bundle_name_full, manifest_obj;
  var instance_version_path, ignored_paths;

  var bundle_link_path = path.join(self.root, 'bundle');

  async.series([
    // Resolve the path at which the version will reside
    function(callback) {
      self.get_version_path(version, function(err, vp) {
        instance_version_path = vp;
        return callback(err);
      });
    },

    // Resolve the path to the extracted bundle
    function(callback) {
      self.get_bundle_version_path(version, function(err, bvp) {
        extracted_bundle_path = bvp;
        return callback(err);
      });
    },

    // Make sure this version doesn't exist
    function(callback) {
      path.exists(instance_version_path, function(exists) {
        var err = null;
        if (exists) {
          err = new Error(sprintf('Instance \'%s\' already has version \'%s\'', self.name, version));
        }
        return callback(err);
      });
    },

    // Make sure an extracted bundle exists for the requested version
    function(callback) {
      fs.stat(extracted_bundle_path, function(err, stats) {
        if (!err && !stats.isDirectory()) {
          err = new Error('No bundle for version \'' + version + '\'');
        }
        return callback(err);
      });
    },

    // Create the directory for the target version
    function(callback) {
      fsutil.ensure_directory(instance_version_path, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifest_path = path.join(extracted_bundle_path, manifest_constants.MANIFEST_FILENAME);
      manifest.get_manifest_object(manifest_path, true, function(err, manifest_obj_) {
        manifest_obj = manifest_obj_;
        ignored_paths = manifest_obj.template_files.concat(manifest_obj.data_files);
        return callback(err);
      });
    },

    // Mirror the directory structure from the extracted bundle in the instance directory
    function(callback) {
      fsutil.tree_to_template(extracted_bundle_path, ignored_paths, function(err, template_object) {
        if (err) {
          return callback(err);
        }

        // No directories
        if (Object.keys(template_object).length === 0) {
          return callback();
        }

        fsutil.template_to_tree(instance_version_path, template_object, true, callback);
      });
    },

    // Realize (render and save) templates
    function(callback) {
      var instance_data = deploy_templates.get_instance_template_object(self.name, instance_version_path);
      deploy_templates.realize_application_templates(manifest_obj, instance_data, extracted_bundle_path,
                                                     instance_version_path, callback);
    },

    // Hard-link all the files (except template and data files)
    function(callback) {
      fsutil.hard_link_files(extracted_bundle_path, instance_version_path, ignored_paths, callback);
    },

    // Resolve the data files
    function(callback) {
      var instance_data_root = path.join(self.root, 'data');
      deploy_files.resolve_data_files(extracted_bundle_path, instance_data_root, instance_version_path,
                                      manifest_obj.data_files, callback);
    }
  ], callback);
};

/**
 * Point the 'current' symlink to the specified version. Will verify
 * that the specified version exists before taking action.
 *
 * @param {String} version    The version to activate
 * @param {Function} callback A callback fired with (err)
 */
Instance.prototype.activate_version = function(version, callback) {
  var self = this;
  var new_version_path;
  var new_version_link = path.join(self.root, 'new');
  var current_version_link = path.join(self.root, 'current');

  async.series([
    // Get the path to the specified version
    function(callback) {
      self.get_version_path(version, function(err, vp) {
        new_version_path = vp;
        return callback(err);
      });
    },

    // Make sure the version exists
    function(callback) {
      path.exists(new_version_path, function(exists) {
        var err = null;
        if (!exists) {
          err = new Error('Cannot activate nonexistent version \'' + version + '\'');
        }
        return callback(err);
      });
    },

    // Create the new link
    function(callback) {
      fs.symlink(new_version_path, new_version_link, callback);
    },

    // Atomically move it into place
    async.apply(fs.rename, new_version_link, current_version_link)
  ], callback);
};

/**
 * Create an instance with a specified name for the specified bundle. The
 * instance will initially use the specified version of the bundle.
 *
 * @param {String} instance_name  A name to give to the instance
 * @param {String} bundle_name    The name of the bundle to use
 * @param {String} bundle_version The version of the bundle to use
 * @param {Function} callback     A callback fired with (err)
 */
exports.create_instance = function(instance_name, bundle_name, bundle_version, callback) {
  if (!INSTANCE_NAME_RE.exec(instance_name)) {
    return callback(new Error('Invalid instance name'));
  }

  var manifest_obj, ignored_paths, instance;
  var bundle_name_full = bundle_name + '@' + bundle_version;

  // Get the paths to the extracted bundle and the manifest
  var extracted_bundle_root = path.join(config.get().extracted_dir, bundle_name);
  var extracted_bundle_path = path.join(extracted_bundle_root, bundle_name_full);

  var instance_path = path.join(config.get().app_dir, instance_name);
  var instance_data_root = path.join(instance_path, 'data');
  var instance_versions_root = path.join(instance_path, 'versions');
  var instance_bundle_link = path.join(instance_path, 'bundle');
  var instance_current_link = path.join(instance_path, 'current');
  var instance_version_path = path.join(instance_versions_root, bundle_name_full);

  async.series([
    // Make sure that the extracted bundle path exists
    function(callback) {
      path.exists(extracted_bundle_path, function(exists) {
        if (!exists) {
          return callback(new Error('Invalid bundle name or version'));
        }

        callback();
      });
    },

    // Make sure there isn't already an instance with this name
    function(callback) {
      path.exists(instance_path, function(exists) {
        if (exists) {
          return callback(new Error('Instance name already in use'));
        }
        callback();
      });
    },

    // Create the instance directory
    function(callback) {
      fsutil.ensure_directory(instance_path, function(err) {
        if (!err) {
          instance = new Instance(instance_name);
        }
        callback(err);
      });
    },

    // Create the data and versions directories
    async.apply(fsutil.ensure_directory, instance_data_root),
    async.apply(fsutil.ensure_directory, instance_versions_root),

    // Create the bundle symlink
    async.apply(fs.symlink, extracted_bundle_root, instance_bundle_link),

    // Prepare the specified version
    function(callback) {
      instance.prepare_version(bundle_version, callback);
    },

    // Symlink it
    function(callback) {
      instance.activate_version(bundle_version, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifest_path = path.join(extracted_bundle_path, manifest_constants.MANIFEST_FILENAME);
      manifest.get_manifest_object(manifest_path, true, function(err, manifest_obj_) {
        manifest_obj = manifest_obj_;
        return callback(err);
      });
    },

    // Create a runit service
    function(callback) {
      deploy_services.create_service(instance_name, instance_current_link, manifest_obj, callback);
    }
  ],

  function(error) {
    if (error && instance) {
      instance.destroy(function() {
        return callback(error);
      });
    }
    else {
      callback(error);
    }
  });
};

/**
 * Get an Instance object for an instance whose existence is verified.
 *
 * @param {String} instance_name  The name of the instance to retrieve
 * @param {Function} callback     A callback that takes (err, instance)
 */
exports.get_instance = function(instance_name, callback) {
  var instance = new Instance(instance_name);
  instance.exists(function(exists) {
    if (!exists) {
      return callback(new Error('Instance doesn\'t exist'));
    }
    else {
      return callback(null, instance);
    }
  });
};
<|endoftext|>


<commit_before>mini.Module(
	'editor/script/config'
)
.requires(

)
.defines(function(){ "use strict";

wm.config = {
	project: {
		'modulePath': 'lib/',
		'entityFiles': 'lib/bloob/entities/*.js',
		'levelPath': 'data/maps/',
		'outputFormat': 'module', // 'module' or 'json'
		'prettyPrint': true
	},

	'layerDefaults': {
		'width': 30,
		'height': 20,
		'tilesize': 8
	},

	'askBeforeClose': true,
	'loadLastLevel': true,

	'entityGrid': 4,
	'undoLevels': 50,

	'binds': {
		'MOUSE1': 'draw',
		'MOUSE2': 'drag',
		'SHIFT': 'select',
		'CTRL': 'drag',
		'SPACE': 'menu',
		'DELETE': 'delete',
		'BACKSPACE': 'delete',
		'G': 'grid',
		'C': 'clone',
		'Z': 'undo',
		'Y': 'redo',
		'MWHEEL_UP': 'zoomin',
		'PLUS': 'zoomin',
		'MWHEEL_DOWN': 'zoomout',
		'MINUS': 'zoomout'
	},

	'view': {
		'zoom': 1,
		'zoomMax': 4,
		'zoomMin': 0.125,
		'grid': false
	},

	// Labels for rendering the x- and y-axis.
	'labels': {
		'draw': true,
		'step': 5,
		'font': '10px Bitstream Vera Sans Mono, Monaco, sans-serif'
	},

	'colors': {
		'clear': '#000000',
		'highlight': '#ceff36',
		'primary': '#ffffff',
		'secondary': '#555555',
		'selection': '#ff9933'
	},

	'collisionTiles': {
		'path': 'lib/weltmeister/collisiontiles-64.png',
		'tilesize': 64
	},

	'api': {
		'save': 'lib/weltmeister/api/save.php',
		'browse': 'lib/weltmeister/api/browse.php',
		'glob': 'lib/weltmeister/api/glob.php'
	}
};

});<commit_msg>added gridstep property<commit_after>mini.Module(
	'editor/script/config'
)
.requires(

)
.defines(function(){ "use strict";

wm.config = {
	project: {
		'modulePath': 'lib/',
		'entityFiles': 'lib/bloob/entities/*.js',
		'levelPath': 'data/maps/',
		'outputFormat': 'module', // 'module' or 'json'
		'prettyPrint': true
	},

	'layerDefaults': {
		'width': 30,
		'height': 20,
		'tilesize': 8
	},

	'askBeforeClose': true,
	'loadLastLevel': true,

	'entityGrid': 4,
	'undoLevels': 50,

	'binds': {
		'MOUSE1': 'draw',
		'MOUSE2': 'drag',
		'SHIFT': 'select',
		'CTRL': 'drag',
		'SPACE': 'menu',
		'DELETE': 'delete',
		'BACKSPACE': 'delete',
		'G': 'grid',
		'C': 'clone',
		'Z': 'undo',
		'Y': 'redo',
		'MWHEEL_UP': 'zoomin',
		'PLUS': 'zoomin',
		'MWHEEL_DOWN': 'zoomout',
		'MINUS': 'zoomout'
	},

	'view': {
		'zoom': 1,
		'zoomMax': 4,
		'zoomMin': 0.125,
		'grid': false,
		'gridStep': 10
	},

	// Labels for rendering the x- and y-axis.
	'labels': {
		'draw': true,
		'step': 5,
		'font': '10px Bitstream Vera Sans Mono, Monaco, sans-serif'
	},

	'colors': {
		'clear': '#000000',
		'highlight': '#ceff36',
		'primary': '#ffffff',
		'secondary': '#555555',
		'selection': '#ff9933'
	},

	'collisionTiles': {
		'path': 'lib/weltmeister/collisiontiles-64.png',
		'tilesize': 64
	},

	'api': {
		'save': 'lib/weltmeister/api/save.php',
		'browse': 'lib/weltmeister/api/browse.php',
		'glob': 'lib/weltmeister/api/glob.php'
	}
};

});<|endoftext|>


<commit_before>
/**
*/
XT.Model = Backbone.RelationalModel.extend(
  /** @scope XT.Model.prototype */ {

  idAttribute: "guid",

  /*
  Reset changed after fetch.
  */
  fetch: function(key, value, options) {
    if (options === undefined) options = {};
    var model = this;
    var success = options.success;
    options.success = function(resp, status, xhr) {
      model.resetChanged();
      if (success) success(model, resp, options);
    };

    return Backbone.Model.prototype.fetch.call(this, options);
  },

  /*
  Sync to xtuple datasource.
  */
  sync: function(method, model, options) {
    var recordType = model.recordType;
    var id = options.id || this.get('id');
    var data = new XT.Data();
    var success = options.success;
    var error = options.error;

    if (method === 'read' && recordType && id && success) {
      return data.retrieveRecord(recordType, id, success, error);
    }

    return false;
  },

  /*
  Map idAttribute
  */
  get: function(key, value, options) {
    if (key === 'id' && this.idAttribute) key = this.idAttribute;

    return Backbone.Model.prototype.get.call(this, key, value, options);
  },

  /*
  Validate all required fields if no attributes specified.
  */
  validate: function(attributes, options) {
    if (!attributes) { // all
      var required = this.required || [];
      var i;

      for (i = 0; i < required.length; i++) {
        if (!this.has(required[i])) {
          return "'" + required[i] + " is required.";
        }
      }
    }
  },

  /*
  Reset model so that it reports no changes.
  */
  resetChanged: function() {
    this.changed = {};
    this._silent = {};
    this._pending = {};
    this._previousAttributes = _.clone(this.attributes);
  }

});<commit_msg>fix signiture<commit_after>
/**
*/
XT.Model = Backbone.RelationalModel.extend(
  /** @scope XT.Model.prototype */ {

  idAttribute: "guid",

  /*
  Reset changed after fetch.
  */
  fetch: function(options) {
    if (options === undefined) options = {};
    var model = this;
    var success = options.success;
    options.success = function(resp, status, xhr) {
      model.resetChanged();
      if (success) success(model, resp, options);
    };

    return Backbone.Model.prototype.fetch.call(this, options);
  },

  /*
  Sync to xtuple datasource.
  */
  sync: function(method, model, options) {
    var recordType = model.recordType;
    var id = options.id || this.get('id');
    var data = new XT.Data();
    var success = options.success;
    var error = options.error;

    if (method === 'read' && recordType && id && success) {
      return data.retrieveRecord(recordType, id, success, error);
    }

    return false;
  },

  /*
  Map idAttribute
  */
  get: function(key, value, options) {
    if (key === 'id' && this.idAttribute) key = this.idAttribute;

    return Backbone.Model.prototype.get.call(this, key, value, options);
  },

  /*
  Validate all required fields if no attributes specified.
  */
  validate: function(attributes, options) {
    if (!attributes) { // all
      var required = this.required || [];
      var i;

      for (i = 0; i < required.length; i++) {
        if (!this.has(required[i])) {
          return "'" + required[i] + " is required.";
        }
      }
    }
  },

  /*
  Reset model so that it reports no changes.
  */
  resetChanged: function() {
    this.changed = {};
    this._silent = {};
    this._pending = {};
    this._previousAttributes = _.clone(this.attributes);
  }

});<|endoftext|>


<commit_before>/* @flow */

import {Emitter, CompositeDisposable} from 'atom'
import GitService from './git-service'
import FileDiffViewModel from './file-diff-view-model'
import CommitBoxViewModel from './commit-box-view-model'

import type {Disposable} from 'atom'
import type FileListStore from './file-list-store'
import type FileDiff from './file-diff'
import type HunkLine from './hunk-line'
import type {ObjectMap} from './common'

export default class FileListViewModel {
  gitService: GitService;
  fileListStore: FileListStore;
  selectedIndex: number;
  emitter: Emitter;
  commitBoxViewModel: CommitBoxViewModel;
  subscriptions: CompositeDisposable;

  constructor (fileListStore: FileListStore, gitService: GitService) {
    this.gitService = gitService
    this.fileListStore = fileListStore
    this.selectedIndex = 0
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()
    this.commitBoxViewModel = new CommitBoxViewModel(gitService)
    this.subscriptions.add(this.commitBoxViewModel.onDidCommit(() => this.emitDidCommitEvent()))
  }

  destroy () {
    this.subscriptions.dispose()
  }

  update (): Promise<void> {
    return this.commitBoxViewModel.update()
  }

  onDidStage (callback: Function): Disposable {
    return this.emitter.on('did-stage', callback)
  }

  emitStagedEvent () {
    this.emitter.emit('did-stage')
  }

  onSelectionChanged (callback: Function): Disposable {
    return this.emitter.on('selection-changed', callback)
  }

  emitSelectionChangedEvent () {
    this.emitter.emit('selection-changed')
  }

  onDidCommit (callback: Function): Disposable {
    return this.emitter.on('did-commit', callback)
  }

  emitDidCommitEvent () {
    this.emitter.emit('did-commit')
  }

  getFileListStore (): FileListStore {
    return this.fileListStore
  }

  getSelectedIndex (): number {
    return this.selectedIndex
  }

  setSelectedIndex (index: number) {
    this.selectedIndex = index
    this.emitSelectionChangedEvent()
  }

  getSelectedFile (): FileDiff {
    return this.getFileAtIndex(this.selectedIndex)
  }

  openSelectedFileDiff (): Promise<void> {
    return this.fileListStore.openFileDiff(this.getSelectedFile())
  }

  getFileAtIndex (index: number): FileDiff {
    return this.fileListStore.getFiles()[index]
  }

  getDiffForPathName (name: string): FileDiff {
    return this.getFileListStore().getOrCreateFileFromPathName(name)
  }

  openFile (): Promise<void> {
    return this.fileListStore.openFile(this.getSelectedFile())
  }

  moveSelectionUp () {
    this.setSelectedIndex(Math.max(this.selectedIndex - 1, 0))
  }

  moveSelectionDown () {
    const filesLengthIndex = this.fileListStore.getFiles().length - 1
    this.setSelectedIndex(Math.min(this.selectedIndex + 1, filesLengthIndex))
  }

  toggleFileStageStatus (file: FileDiff): Promise<void> {
    let stagePromise = null
    if (file.getStageStatus() === 'unstaged') {
      stagePromise = this.gitService.stageFile(file)
    } else {
      stagePromise = this.gitService.unstageFile(file)
    }
    // TODO: Handle errors.

    file.toggleStageStatus()

    this.emitStagedEvent()
    return stagePromise.then(_ => { return })
  }

  toggleSelectedFilesStageStatus (): Promise<void> {
    const file = this.getSelectedFile()
    return this.toggleFileStageStatus(file)
  }

  async stageLines (lines: Array<HunkLine>, stage: boolean): Promise<void> {
    if (lines.length === 0) return Promise.resolve()

    const groupedLines = this.groupedLinesByHunk(lines)
    for (const line of lines) {
      line.setIsStaged(stage)
    }

    const patches = await this.gitService.calculatePatchTexts(groupedLines, stage)
    const fileDiff = lines[0].hunk.diff
    if (stage) {
      await this.gitService.stagePatches(fileDiff, patches)
    } else {
      await this.gitService.unstagePatches(fileDiff, patches)
    }

    // TODO: handle errors

    this.emitStagedEvent()
  }

  groupedLinesByHunk (lines: Array<HunkLine>): ObjectMap<Array<HunkLine>> {
    return lines.reduce((linesByHunk, line) => {
      let lines = linesByHunk[line.hunk.toString()]
      if (!lines) {
        lines = []
        linesByHunk[line.hunk.toString()] = lines
      }

      lines.push(line)
      return linesByHunk
    }, {})
  }

  getFileDiffViewModels (): Array<FileDiffViewModel> {
    return this.fileListStore.getFiles().map(fileDiff => new FileDiffViewModel(fileDiff))
  }
}
<commit_msg>Extract stageFile a bit more.<commit_after>/* @flow */

import {Emitter, CompositeDisposable} from 'atom'
import GitService from './git-service'
import FileDiffViewModel from './file-diff-view-model'
import CommitBoxViewModel from './commit-box-view-model'

import type {Disposable} from 'atom'
import type FileListStore from './file-list-store'
import type FileDiff from './file-diff'
import type HunkLine from './hunk-line'
import type {ObjectMap} from './common'

export default class FileListViewModel {
  gitService: GitService;
  fileListStore: FileListStore;
  selectedIndex: number;
  emitter: Emitter;
  commitBoxViewModel: CommitBoxViewModel;
  subscriptions: CompositeDisposable;

  constructor (fileListStore: FileListStore, gitService: GitService) {
    this.gitService = gitService
    this.fileListStore = fileListStore
    this.selectedIndex = 0
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()
    this.commitBoxViewModel = new CommitBoxViewModel(gitService)
    this.subscriptions.add(this.commitBoxViewModel.onDidCommit(() => this.emitDidCommitEvent()))
  }

  destroy () {
    this.subscriptions.dispose()
  }

  update (): Promise<void> {
    return this.commitBoxViewModel.update()
  }

  onDidStage (callback: Function): Disposable {
    return this.emitter.on('did-stage', callback)
  }

  emitStagedEvent () {
    this.emitter.emit('did-stage')
  }

  onSelectionChanged (callback: Function): Disposable {
    return this.emitter.on('selection-changed', callback)
  }

  emitSelectionChangedEvent () {
    this.emitter.emit('selection-changed')
  }

  onDidCommit (callback: Function): Disposable {
    return this.emitter.on('did-commit', callback)
  }

  emitDidCommitEvent () {
    this.emitter.emit('did-commit')
  }

  getFileListStore (): FileListStore {
    return this.fileListStore
  }

  getSelectedIndex (): number {
    return this.selectedIndex
  }

  setSelectedIndex (index: number) {
    this.selectedIndex = index
    this.emitSelectionChangedEvent()
  }

  getSelectedFile (): FileDiff {
    return this.getFileAtIndex(this.selectedIndex)
  }

  openSelectedFileDiff (): Promise<void> {
    return this.fileListStore.openFileDiff(this.getSelectedFile())
  }

  getFileAtIndex (index: number): FileDiff {
    return this.fileListStore.getFiles()[index]
  }

  getDiffForPathName (name: string): FileDiff {
    return this.getFileListStore().getOrCreateFileFromPathName(name)
  }

  openFile (): Promise<void> {
    return this.fileListStore.openFile(this.getSelectedFile())
  }

  moveSelectionUp () {
    this.setSelectedIndex(Math.max(this.selectedIndex - 1, 0))
  }

  moveSelectionDown () {
    const filesLengthIndex = this.fileListStore.getFiles().length - 1
    this.setSelectedIndex(Math.min(this.selectedIndex + 1, filesLengthIndex))
  }

  toggleFileStageStatus (file: FileDiff): Promise<void> {
    const stage = file.getStageStatus() === 'unstaged'
    return this.stageFile(file, stage)
  }

  async stageFile (file: FileDiff, stage: boolean): Promise<void> {
    file.setIsStaged(stage)

    try {
      if (stage) {
        await this.gitService.stageFile(file)
      } else {
        await this.gitService.unstageFile(file)
      }
    } catch (e) {
      console.error(e)
    }

    // TODO: Handle errors.

    this.emitStagedEvent()
  }

  toggleSelectedFilesStageStatus (): Promise<void> {
    const file = this.getSelectedFile()
    return this.toggleFileStageStatus(file)
  }

  async stageLines (lines: Array<HunkLine>, stage: boolean): Promise<void> {
    if (lines.length === 0) return Promise.resolve()

    const groupedLines = this.groupedLinesByHunk(lines)
    for (const line of lines) {
      line.setIsStaged(stage)
    }

    try {
      const patches = await this.gitService.calculatePatchTexts(groupedLines, stage)
      const fileDiff = lines[0].hunk.diff
      if (stage) {
        await this.gitService.stagePatches(fileDiff, patches)
      } else {
        await this.gitService.unstagePatches(fileDiff, patches)
      }
    } catch (e) {
      console.error(e)
    }

    // TODO: handle errors

    this.emitStagedEvent()
  }

  groupedLinesByHunk (lines: Array<HunkLine>): ObjectMap<Array<HunkLine>> {
    return lines.reduce((linesByHunk, line) => {
      let lines = linesByHunk[line.hunk.toString()]
      if (!lines) {
        lines = []
        linesByHunk[line.hunk.toString()] = lines
      }

      lines.push(line)
      return linesByHunk
    }, {})
  }

  getFileDiffViewModels (): Array<FileDiffViewModel> {
    return this.fileListStore.getFiles().map(fileDiff => new FileDiffViewModel(fileDiff))
  }
}
<|endoftext|>


<commit_before>function register_huddle_onclick(zephyr_row, sender) {
    zephyr_row.find(".zephyr_sender").click(function (e) {
        prepare_huddle(sender);
        // The sender span is inside the messagebox, which also has an
        // onclick handler. We don't want to trigger the messagebox
        // handler.
        e.stopPropagation();

        // switch to the replybox hotkey handler
        set_keydown_in_input(true);
    });
}

function register_onclick(zephyr_row, zephyr_id) {
    zephyr_row.find(".messagebox").click(function (e) {
        if (!(clicking && mouse_moved)) {
            // Was a click (not a click-and-drag).
            select_zephyr_by_id(zephyr_id);
            respond_to_zephyr();
        }
        mouse_moved = false;
        clicking = false;
    });
}

/* We use 'visibility' rather than 'display' and jQuery's show() / hide(),
   because we want to reserve space for the email address.  This avoids
   things jumping around slightly when the email address is shown. */

function hide_email() {
    $('.zephyr_sender_email').addClass('invisible');
}

function show_email(zephyr_id) {
    hide_email();
    get_zephyr_row(zephyr_id).find('.zephyr_sender_email').removeClass('invisible');
}

function report_error(response, xhr, status_box) {
    if (xhr.status.toString().charAt(0) === "4") {
        // Only display the error response for 4XX, where we've crafted
        // a nice response.
        response += ": " + $.parseJSON(xhr.responseText).msg;
    }

    status_box.removeClass(status_classes).addClass('alert-error')
              .text(response).stop(true).fadeTo(0, 1);
    status_box.show();
}

var clicking = false;
var mouse_moved = false;

function zephyr_mousedown() {
    mouse_moved = false;
    clicking = true;
}

function zephyr_mousemove() {
    if (clicking) {
        mouse_moved = true;
    }
}

var autocomplete_needs_update = false;

function update_autocomplete() {
    class_list.sort();
    instance_list.sort();
    people_list.sort();

    // limit number of items so the list doesn't fall off the screen
    $( "#class" ).typeahead({
        source: class_list,
        items: 3
    });
    $( "#instance" ).typeahead({
        source: instance_list,
        items: 2
    });
    $( "#recipient" ).typeahead({
        source: people_list,
        items: 4
    });

    autocomplete_needs_update = false;
}

$(function () {
    // NB: This just binds to current elements, and won't bind to elements
    // created after ready() is called.
    $('input, textarea, button').focus(function () {
        set_keydown_in_input(true);
    });
    $('input, textarea, button').blur(function () {
        set_keydown_in_input(false);
    });

    $('#zephyr-type-tabs a[href="#class-message"]').on('shown', function (e) {
        $('#class-message input:not(:hidden):first').focus().select();
    });
    $('#zephyr-type-tabs a[href="#personal-message"]').on('shown', function (e) {
        $('#personal-message input:not(:hidden):first').focus().select();
    });

    // Prepare the click handler for subbing to a new class to which
    // you have composed a zephyr.
    $('#create-it').click(function () {
        sub_from_home(compose_class_name(), $('#class-dne'));
    });

    // Prepare the click handler for subbing to an existing class.
    $('#sub-it').click(function () {
        sub_from_home(compose_class_name(), $('#class-nosub'));
    });

    var last_mousewheel_or_scroll = 0;
    function do_mousewheel_or_scroll () {
        var time = $.now();
        if (time - last_mousewheel_or_scroll > 50) {
            keep_pointer_in_view();
            last_mousewheel_or_scroll = time;
        }
    }
    $(window).mousewheel(do_mousewheel_or_scroll);
    $(window).scroll(do_mousewheel_or_scroll);

    $('.button-slide').click(function () {
        show_compose('class', $("#class"));
    });

    $('#sidebar a[href="#subscriptions"]').click(fetch_subs);

    var settings_status = $('#settings-status');
    settings_status.hide();

    $("#current_settings form").ajaxForm({
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        success: function (resp, statusText, xhr, form) {
            var message = "Updated settings!";
            var result = $.parseJSON(xhr.responseText);
            if ((result.full_name !== undefined) || (result.short_name !== undefined)) {
                message = "Updated settings!  You will need to reload the page for your changes to take effect.";
            }
            settings_status.removeClass(status_classes)
                .addClass('alert-success')
                .text(message).stop(true).fadeTo(0,1);
            // TODO: In theory we should auto-reload or something if
            // you changed the email address or other fields that show
            // up on all screens
        },
        error: function (xhr, error_type, xhn) {
            var response = "Error changing settings";
            if (xhr.status.toString().charAt(0) === "4") {
                // Only display the error response for 4XX, where we've crafted
                // a nice response.
                response += ": " + $.parseJSON(xhr.responseText).msg;
            }
            settings_status.removeClass(status_classes)
                .addClass('alert-error')
                .text(response).stop(true).fadeTo(0,1);
        }
    });
});
<commit_msg>Clicking on a zsender should populate the stream box, too.<commit_after>function register_huddle_onclick(zephyr_row, sender) {
    zephyr_row.find(".zephyr_sender").click(function (e) {
        select_zephyr_by_id(zephyr_row.attr('zid'));
        // populate the stream box
        respond_to_zephyr();
        // but we really want to be in huddle mode
        prepare_huddle(sender);
        // The sender span is inside the messagebox, which also has an
        // onclick handler. We don't want to trigger the messagebox
        // handler.
        e.stopPropagation();

        // switch to the replybox hotkey handler
        set_keydown_in_input(true);
    });
}

function register_onclick(zephyr_row, zephyr_id) {
    zephyr_row.find(".messagebox").click(function (e) {
        if (!(clicking && mouse_moved)) {
            // Was a click (not a click-and-drag).
            select_zephyr_by_id(zephyr_id);
            respond_to_zephyr();
        }
        mouse_moved = false;
        clicking = false;
    });
}

/* We use 'visibility' rather than 'display' and jQuery's show() / hide(),
   because we want to reserve space for the email address.  This avoids
   things jumping around slightly when the email address is shown. */

function hide_email() {
    $('.zephyr_sender_email').addClass('invisible');
}

function show_email(zephyr_id) {
    hide_email();
    get_zephyr_row(zephyr_id).find('.zephyr_sender_email').removeClass('invisible');
}

function report_error(response, xhr, status_box) {
    if (xhr.status.toString().charAt(0) === "4") {
        // Only display the error response for 4XX, where we've crafted
        // a nice response.
        response += ": " + $.parseJSON(xhr.responseText).msg;
    }

    status_box.removeClass(status_classes).addClass('alert-error')
              .text(response).stop(true).fadeTo(0, 1);
    status_box.show();
}

var clicking = false;
var mouse_moved = false;

function zephyr_mousedown() {
    mouse_moved = false;
    clicking = true;
}

function zephyr_mousemove() {
    if (clicking) {
        mouse_moved = true;
    }
}

var autocomplete_needs_update = false;

function update_autocomplete() {
    class_list.sort();
    instance_list.sort();
    people_list.sort();

    // limit number of items so the list doesn't fall off the screen
    $( "#class" ).typeahead({
        source: class_list,
        items: 3
    });
    $( "#instance" ).typeahead({
        source: instance_list,
        items: 2
    });
    $( "#recipient" ).typeahead({
        source: people_list,
        items: 4
    });

    autocomplete_needs_update = false;
}

$(function () {
    // NB: This just binds to current elements, and won't bind to elements
    // created after ready() is called.
    $('input, textarea, button').focus(function () {
        set_keydown_in_input(true);
    });
    $('input, textarea, button').blur(function () {
        set_keydown_in_input(false);
    });

    $('#zephyr-type-tabs a[href="#class-message"]').on('shown', function (e) {
        $('#class-message input:not(:hidden):first').focus().select();
    });
    $('#zephyr-type-tabs a[href="#personal-message"]').on('shown', function (e) {
        $('#personal-message input:not(:hidden):first').focus().select();
    });

    // Prepare the click handler for subbing to a new class to which
    // you have composed a zephyr.
    $('#create-it').click(function () {
        sub_from_home(compose_class_name(), $('#class-dne'));
    });

    // Prepare the click handler for subbing to an existing class.
    $('#sub-it').click(function () {
        sub_from_home(compose_class_name(), $('#class-nosub'));
    });

    var last_mousewheel_or_scroll = 0;
    function do_mousewheel_or_scroll () {
        var time = $.now();
        if (time - last_mousewheel_or_scroll > 50) {
            keep_pointer_in_view();
            last_mousewheel_or_scroll = time;
        }
    }
    $(window).mousewheel(do_mousewheel_or_scroll);
    $(window).scroll(do_mousewheel_or_scroll);

    $('.button-slide').click(function () {
        show_compose('class', $("#class"));
    });

    $('#sidebar a[href="#subscriptions"]').click(fetch_subs);

    var settings_status = $('#settings-status');
    settings_status.hide();

    $("#current_settings form").ajaxForm({
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        success: function (resp, statusText, xhr, form) {
            var message = "Updated settings!";
            var result = $.parseJSON(xhr.responseText);
            if ((result.full_name !== undefined) || (result.short_name !== undefined)) {
                message = "Updated settings!  You will need to reload the page for your changes to take effect.";
            }
            settings_status.removeClass(status_classes)
                .addClass('alert-success')
                .text(message).stop(true).fadeTo(0,1);
            // TODO: In theory we should auto-reload or something if
            // you changed the email address or other fields that show
            // up on all screens
        },
        error: function (xhr, error_type, xhn) {
            var response = "Error changing settings";
            if (xhr.status.toString().charAt(0) === "4") {
                // Only display the error response for 4XX, where we've crafted
                // a nice response.
                response += ": " + $.parseJSON(xhr.responseText).msg;
            }
            settings_status.removeClass(status_classes)
                .addClass('alert-error')
                .text(response).stop(true).fadeTo(0,1);
        }
    });
});
<|endoftext|>