<commit_before>var __ = require('underscore');
var util = require('util');

var utils = require('../../../util/utils');
var VNetUtil = require('../../../util/vnet.util');
var TagUtils = require('../tag/tagUtils');
var ResourceUtils = require('../resource/resourceUtils');

var $ = utils.getLocaleString;

function VNet(cli, networkResourceProviderClient) {
  this.cli = cli;
  this.log = cli.output;
  this.networkResourceProviderClient = networkResourceProviderClient;
}

__.extend(VNet.prototype, {
  create: function (resourceGroup, name, location, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);

    if (vNet) {
      throw new Error(util.format($('Virtual network "%s" already exists in resource group "%s"'), name, resourceGroup));
    }

    var requestBody = {
      name: name,
      location: location,
      addressSpace: {
        addressPrefixes: []
      },
      dhcpOptions: {
        dnsServers: []
      }
    };

    var vNetUtil = new VNetUtil();

    // handling address prefixes
    if (options.addressPrefixes) {
      this._handleAddressPrefixes(options, requestBody, vNetUtil);
    } else {
      var defaultAddressPrefix = vNetUtil.defaultAddressSpaceInfo().ipv4Cidr;
      this.log.verbose(util.format($('Using default address prefix: %s'), defaultAddressPrefix));
      requestBody.addressSpace.addressPrefixes.push(defaultAddressPrefix);
    }

    // handling dns servers
    if (options.dnsServers) {
      this._handleDnsServers(options, requestBody, vNetUtil);
    } else {
      this.log.verbose($('No DNS server specified'));
    }

    // handling tags
    if (options.tags) {
      var tags = TagUtils.buildTagsParameter(null, options);
      requestBody.tags = tags;
    } else {
      this.log.verbose($('No tags specified'));
    }

    var progress = this.cli.interaction.progress(util.format($('Creating virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.createOrUpdate(resourceGroup, name, requestBody, _);
    } catch (e) {
      throw e;
    } finally {
      progress.end();
    }

    this.show(resourceGroup, name, 'Loading virtual network state', _);
  },

  set: function (resourceGroup, name, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);

    if (!vNet) {
      throw new Error(util.format($('Virtual network "%s" not found in resource group "%s"'), name, resourceGroup));
    }

    var vNetUtil = new VNetUtil();

    // handling address prefixes
    if (options.addressPrefixes) {
      vNet.addressSpace.addressPrefixes = [];
      this._handleAddressPrefixes(options, vNet, vNetUtil);
    }

    // handling dns servers
    if (options.dnsServers) {
      vNet.dhcpOptions.dnsServers = [];
      if (options.dnsServers !== true) {
        this.log.verbose($('Clearing DNS servers'));
        this._handleDnsServers(options, vNet, vNetUtil);
      }
    } else {
      this.log.verbose($('No DNS server specified'));
    }

    if (options.tags === false) {
      vNet.tags = {};
    }

    if (options.tags) {
      var tags = TagUtils.buildTagsParameter(vNet.tags, options);
      for (var key in tags) {
        vNet.tags[key] = tags[key];
      }
    }

    var progress = this.cli.interaction.progress(util.format($('Updating virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.createOrUpdate(resourceGroup, name, vNet, _);
    } catch (e) {
      throw e;
    } finally {
      progress.end();
    }

    this.show(resourceGroup, name, 'Loading virtual network state', _);
  },

  delete: function (resourceGroup, name, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);
    if (!vNet) {
      this.log.error(util.format('Virtual network "%s" not found', name));
      return;
    }

    if (!options.quiet && !this.cli.interaction.confirm(util.format($('Delete virtual network %s? [y/n] '), name), _)) {
      return;
    }

    var progress = this.cli.interaction.progress(util.format($('Deleting virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.deleteMethod(resourceGroup, name, _);
    } catch(e) {
      throw e;
    } finally {
      progress.end();
    }
  },

  show: function (resourceGroup, name, message, _) {
    var vNet = this.get(resourceGroup, name, message, _);
    var output = this.cli.output;

    if (!vNet) {
      if (output.format().json) {
        output.json({});
      } else {
        output.info(util.format($('Virtual network "%s" not found'), name));
      }
      return;
    }

    this._showVnet(vNet);
  },

  list: function (resourceGroup, _) {
    var progress = this.cli.interaction.progress('Listing virtual networks');
    var vNets = null;
    try {
      vNets = this.networkResourceProviderClient.virtualNetworks.list(resourceGroup, _);
    } finally {
      progress.end();
    }

    var output = this.cli.output;
    this.cli.interaction.formatOutput(vNets.virtualNetworks, function (outputData) {
      if (outputData.length === 0) {
        output.warn($('No virtual networks found'));
      } else {
        output.table(outputData, function (row, item) {
          row.cell($('ID'), item.id);
          row.cell($('Name'), item.name);
          row.cell($('Location'), item.location);
          row.cell($('Address prefixes'), item.addressSpace.addressPrefixes);
          var dnsServers = '';
          if (item.dhcpOptions) {
            dnsServers = item.dhcpOptions.dnsServers;
          }
          row.cell($('DNS servers'), dnsServers);
        });
      }
    });
  },

  get: function (resourceGroup, name, message, _) {
    message = message || util.format($('Looking up virtual network "%s"'), name);
    var progress = this.cli.interaction.progress(message);
    var vNet = null;
    try {
      vNet = this.networkResourceProviderClient.virtualNetworks.get(resourceGroup, name, _);
    } catch (e) {
      if (e.code === 'ResourceNotFound') {
        return null;
      }
      throw e;
    } finally {
      progress.end();
    }

    return vNet.virtualNetwork;
  },

  _showVnet: function (resource) {
    var resourceInformation = ResourceUtils.getResourceInformation(resource.id);
    var log = this.log;
    this.cli.interaction.formatOutput(resource, function (resource) {
      log.data($('Id:                  '), resource.id);
      log.data($('Name:                '), resourceInformation.resourceName || resource.name);
      log.data($('Type:                '), resourceInformation.resourceType || resource.type);
      log.data($('Location:            '), resource.location);
      log.data($('Tags:                '), TagUtils.getTagsInfo(resource.tags));
      log.data($('Provisioning state:  '), resource.provisioningState);
      if (resource.addressSpace && resource.addressSpace.addressPrefixes && resource.addressSpace.addressPrefixes.length > 0) {
        log.data($('Address prefixes:'));
        for (var apNum in resource.addressSpace.addressPrefixes) {
          var addressPrefix = resource.addressSpace.addressPrefixes[apNum];
          log.data('', addressPrefix);
        }
      } else {
        log.data($('Address prefixes:  '), '');
      }
      if (resource.dhcpOptions && resource.dhcpOptions.dnsServers && resource.dhcpOptions.dnsServers.length > 0) {
        log.data($('DNS servers:'));
        for (var dnsNum in resource.dhcpOptions.dnsServers) {
          var dns = resource.dhcpOptions.dnsServers[dnsNum];
          log.data('', dns);
        }
      } else {
        log.data($('DNS servers:       '), '');
      }
      if (resource.subnets && resource.subnets.length > 0) {
        log.data($('Subnets:'));
        for (var subnetNum in resource.subnets) {
          var subnet = resource.subnets[subnetNum];
          log.data('', subnet.name + ' ' + subnet.addressPrefix);
        }
      } else {
        log.data($('Subnets:       '), '');
      }
      log.data('');
    });
  },

  _handleDnsServers: function(options, vNet, vNetUtil) {
    var dnsServers = options.dnsServers.split(',');
    for (var dnsNum in dnsServers) {
      var dnsServer = dnsServers[dnsNum];
      var parsedDnsIp = vNetUtil.parseIPv4(dnsServer);
      if (parsedDnsIp.error) {
        throw new Error(parsedDnsIp.error);
      }

      vNet.dhcpOptions.dnsServers.push(dnsServer);
    }
  },

  _handleAddressPrefixes: function(options, vNet, vNetUtil) {
    var addressPrefixes = options.addressPrefixes.split(',');
    for (var addNum in addressPrefixes) {
      var addressPrefix = addressPrefixes[addNum];
      var parsedAddressPrefix = vNetUtil.parseIPv4Cidr(addressPrefix);
      if (parsedAddressPrefix.error) {
        throw new Error(parsedAddressPrefix.error);
      }

      vNet.addressSpace.addressPrefixes.push(addressPrefix);
    }

    for (var i = 0; i < addressPrefixes.length; i++) {
      var addPrefixToCheck = addressPrefixes[i];
      for (var j = 0; j < addressPrefixes.length; j++) {
        var addPrefixToCompare = addressPrefixes[j];
        if (i === j) {
          continue;
        }

        var overlapped = vNetUtil.isCidrsOverlapping(addPrefixToCheck, addPrefixToCompare);
        if (overlapped) {
          throw new Error(util.format($('Address prefix "%s" overlaps address prefix "%s"'), addPrefixToCheck, addPrefixToCompare));
        }
      }
    }
  }
});

module.exports = VNet;<commit_msg>Network VNet: fixed set command (appending multipe DNS addressses)<commit_after>var __ = require('underscore');
var util = require('util');

var utils = require('../../../util/utils');
var VNetUtil = require('../../../util/vnet.util');
var TagUtils = require('../tag/tagUtils');
var ResourceUtils = require('../resource/resourceUtils');

var $ = utils.getLocaleString;

function VNet(cli, networkResourceProviderClient) {
  this.cli = cli;
  this.log = cli.output;
  this.networkResourceProviderClient = networkResourceProviderClient;
}

__.extend(VNet.prototype, {
  create: function (resourceGroup, name, location, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);

    if (vNet) {
      throw new Error(util.format($('Virtual network "%s" already exists in resource group "%s"'), name, resourceGroup));
    }

    var requestBody = {
      name: name,
      location: location,
      addressSpace: {
        addressPrefixes: []
      },
      dhcpOptions: {
        dnsServers: []
      }
    };

    var vNetUtil = new VNetUtil();

    // handling address prefixes
    if (options.addressPrefixes) {
      this._handleAddressPrefixes(options, requestBody, vNetUtil);
    } else {
      var defaultAddressPrefix = vNetUtil.defaultAddressSpaceInfo().ipv4Cidr;
      this.log.verbose(util.format($('Using default address prefix: %s'), defaultAddressPrefix));
      requestBody.addressSpace.addressPrefixes.push(defaultAddressPrefix);
    }

    // handling dns servers
    if (options.dnsServers) {
      this._handleDnsServers(options, requestBody, vNetUtil);
    } else {
      this.log.verbose($('No DNS server specified'));
    }

    // handling tags
    if (options.tags) {
      var tags = TagUtils.buildTagsParameter(null, options);
      requestBody.tags = tags;
    } else {
      this.log.verbose($('No tags specified'));
    }

    var progress = this.cli.interaction.progress(util.format($('Creating virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.createOrUpdate(resourceGroup, name, requestBody, _);
    } catch (e) {
      throw e;
    } finally {
      progress.end();
    }

    this.show(resourceGroup, name, 'Loading virtual network state', _);
  },

  set: function (resourceGroup, name, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);

    if (!vNet) {
      throw new Error(util.format($('Virtual network "%s" not found in resource group "%s"'), name, resourceGroup));
    }

    var vNetUtil = new VNetUtil();

    // handling address prefixes
    if (options.addressPrefixes) {
      vNet.addressSpace.addressPrefixes = [];
      this._handleAddressPrefixes(options, vNet, vNetUtil);
    }

    // handling dns servers
    if (options.dnsServers) {
      if (options.dnsServers !== true) {
        this._handleDnsServers(options, vNet, vNetUtil);
      } else {
        this.log.verbose($('Clearing DNS servers'));
        vNet.dhcpOptions.dnsServers = [];
      }
    }

    if (options.tags === false) {
      vNet.tags = {};
    }

    if (options.tags) {
      var tags = TagUtils.buildTagsParameter(vNet.tags, options);
      for (var key in tags) {
        vNet.tags[key] = tags[key];
      }
    }

    var progress = this.cli.interaction.progress(util.format($('Updating virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.createOrUpdate(resourceGroup, name, vNet, _);
    } catch (e) {
      throw e;
    } finally {
      progress.end();
    }

    this.show(resourceGroup, name, 'Loading virtual network state', _);
  },

  delete: function (resourceGroup, name, options, _) {
    var vNet = this.get(resourceGroup, name, null, _);
    if (!vNet) {
      this.log.error(util.format('Virtual network "%s" not found', name));
      return;
    }

    if (!options.quiet && !this.cli.interaction.confirm(util.format($('Delete virtual network %s? [y/n] '), name), _)) {
      return;
    }

    var progress = this.cli.interaction.progress(util.format($('Deleting virtual network "%s"'), name));
    try {
      this.networkResourceProviderClient.virtualNetworks.deleteMethod(resourceGroup, name, _);
    } catch(e) {
      throw e;
    } finally {
      progress.end();
    }
  },

  show: function (resourceGroup, name, message, _) {
    var vNet = this.get(resourceGroup, name, message, _);
    var output = this.cli.output;

    if (!vNet) {
      if (output.format().json) {
        output.json({});
      } else {
        output.info(util.format($('Virtual network "%s" not found'), name));
      }
      return;
    }

    this._showVnet(vNet);
  },

  list: function (resourceGroup, _) {
    var progress = this.cli.interaction.progress('Listing virtual networks');
    var vNets = null;
    try {
      vNets = this.networkResourceProviderClient.virtualNetworks.list(resourceGroup, _);
    } finally {
      progress.end();
    }

    var output = this.cli.output;
    this.cli.interaction.formatOutput(vNets.virtualNetworks, function (outputData) {
      if (outputData.length === 0) {
        output.warn($('No virtual networks found'));
      } else {
        output.table(outputData, function (row, item) {
          row.cell($('ID'), item.id);
          row.cell($('Name'), item.name);
          row.cell($('Location'), item.location);
          row.cell($('Address prefixes'), item.addressSpace.addressPrefixes);
          var dnsServers = '';
          if (item.dhcpOptions) {
            dnsServers = item.dhcpOptions.dnsServers;
          }
          row.cell($('DNS servers'), dnsServers);
        });
      }
    });
  },

  get: function (resourceGroup, name, message, _) {
    message = message || util.format($('Looking up virtual network "%s"'), name);
    var progress = this.cli.interaction.progress(message);
    var vNet = null;
    try {
      vNet = this.networkResourceProviderClient.virtualNetworks.get(resourceGroup, name, _);
    } catch (e) {
      if (e.code === 'ResourceNotFound') {
        return null;
      }
      throw e;
    } finally {
      progress.end();
    }

    return vNet.virtualNetwork;
  },

  _showVnet: function (resource) {
    var resourceInformation = ResourceUtils.getResourceInformation(resource.id);
    var log = this.log;
    this.cli.interaction.formatOutput(resource, function (resource) {
      log.data($('Id:                  '), resource.id);
      log.data($('Name:                '), resourceInformation.resourceName || resource.name);
      log.data($('Type:                '), resourceInformation.resourceType || resource.type);
      log.data($('Location:            '), resource.location);
      log.data($('Tags:                '), TagUtils.getTagsInfo(resource.tags));
      log.data($('Provisioning state:  '), resource.provisioningState);
      if (resource.addressSpace && resource.addressSpace.addressPrefixes && resource.addressSpace.addressPrefixes.length > 0) {
        log.data($('Address prefixes:'));
        for (var apNum in resource.addressSpace.addressPrefixes) {
          var addressPrefix = resource.addressSpace.addressPrefixes[apNum];
          log.data('', addressPrefix);
        }
      } else {
        log.data($('Address prefixes:  '), '');
      }
      if (resource.dhcpOptions && resource.dhcpOptions.dnsServers && resource.dhcpOptions.dnsServers.length > 0) {
        log.data($('DNS servers:'));
        for (var dnsNum in resource.dhcpOptions.dnsServers) {
          var dns = resource.dhcpOptions.dnsServers[dnsNum];
          log.data('', dns);
        }
      } else {
        log.data($('DNS servers:       '), '');
      }
      if (resource.subnets && resource.subnets.length > 0) {
        log.data($('Subnets:'));
        for (var subnetNum in resource.subnets) {
          var subnet = resource.subnets[subnetNum];
          log.data('', subnet.name + ' ' + subnet.addressPrefix);
        }
      } else {
        log.data($('Subnets:       '), '');
      }
      log.data('');
    });
  },

  _handleDnsServers: function(options, vNet, vNetUtil) {
    var dnsServers = options.dnsServers.split(',');
    for (var dnsNum in dnsServers) {
      var dnsServer = dnsServers[dnsNum];
      var dnsIndex = vNet.dhcpOptions.dnsServers.indexOf(dnsServer);
      if (dnsIndex >= 0) {
      	continue;
      }

      var parsedDnsIp = vNetUtil.parseIPv4(dnsServer);
      if (parsedDnsIp.error) {
        throw new Error(parsedDnsIp.error);
      }

      vNet.dhcpOptions.dnsServers.push(dnsServer);
    }
  },

  _handleAddressPrefixes: function(options, vNet, vNetUtil) {
    var addressPrefixes = options.addressPrefixes.split(',');
    for (var addNum in addressPrefixes) {
      var addressPrefix = addressPrefixes[addNum];
      var parsedAddressPrefix = vNetUtil.parseIPv4Cidr(addressPrefix);
      if (parsedAddressPrefix.error) {
        throw new Error(parsedAddressPrefix.error);
      }

      vNet.addressSpace.addressPrefixes.push(addressPrefix);
    }

    for (var i = 0; i < addressPrefixes.length; i++) {
      var addPrefixToCheck = addressPrefixes[i];
      for (var j = 0; j < addressPrefixes.length; j++) {
        var addPrefixToCompare = addressPrefixes[j];
        if (i === j) {
          continue;
        }

        var overlapped = vNetUtil.isCidrsOverlapping(addPrefixToCheck, addPrefixToCompare);
        if (overlapped) {
          throw new Error(util.format($('Address prefix "%s" overlaps address prefix "%s"'), addPrefixToCheck, addPrefixToCompare));
        }
      }
    }
  }
});

module.exports = VNet;<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 exec = require('cordova/exec'),
    FileTransferError = require('cordova/plugin/FileTransferError'),
    ProgressEvent = require('cordova/plugin/ProgressEvent');

function newProgressEvent(result) {
    var pe = new ProgressEvent();
    pe.lengthComputable = result.lengthComputable;
    pe.loaded = result.loaded;
    pe.total = result.total;
    return pe;
}

var idCounter = 0;

/**
 * FileTransfer uploads a file to a remote server.
 * @constructor
 */
var FileTransfer = function() {
    this._id = ++idCounter;
    this.onprogress = null; // optional callback
};

/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String}           Full path of the file on the device
* @param server {String}             URL of the server to receive the file
* @param successCallback (Function}  Callback to be invoked when upload has completed
* @param errorCallback {Function}    Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
    // sanity parameter checking
    if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum.");
    // check for options
    var fileKey = null;
    var fileName = null;
    var mimeType = null;
    var params = null;
    var chunkedMode = true;
    var headers = null;
    if (options) {
        fileKey = options.fileKey;
        fileName = options.fileName;
        mimeType = options.mimeType;
        headers = options.headers;
        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
            chunkedMode = options.chunkedMode;
        }
        if (options.params) {
            params = options.params;
        }
        else {
            params = {};
        }
    }

    var fail = function(e) {
        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
        errorCallback(error);
    };

    var self = this;
    var win = function(result) {
        if (typeof result.lengthComputable != "undefined") {
            if (self.onprogress) {
                return self.onprogress(newProgressEvent(result));
            }
        } else {
            return successCallback(result);
        }
    };
    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
};

/**
 * Downloads a file form a given URL and saves it to the specified directory.
 * @param source {String}          URL of the server to receive the file
 * @param target {String}         Full path of the file on the device
 * @param successCallback (Function}  Callback to be invoked when upload has completed
 * @param errorCallback {Function}    Callback to be invoked upon error
 * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
 */
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
    // sanity parameter checking
    if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum.");
    var self = this;
    var win = function(result) {
        if (typeof result.lengthComputable != "undefined") {
            if (self.onprogress) {
                return self.onprogress(newProgressEvent(result));
            }
        } else {
            var entry = null;
            if (result.isDirectory) {
                entry = new (require('cordova/plugin/DirectoryEntry'))();
            }
            else if (result.isFile) {
                entry = new (require('cordova/plugin/FileEntry'))();
            }
            entry.isDirectory = result.isDirectory;
            entry.isFile = result.isFile;
            entry.name = result.name;
            entry.fullPath = result.fullPath;
            successCallback(entry);
        }
    };

    var fail = function(e) {
        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
        errorCallback(error);
    };

    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
};

/**
 * Aborts the ongoing file transfer on this object
 * @param successCallback {Function}  Callback to be invoked upon success
 * @param errorCallback {Function}    Callback to be invoked upon error
 */
FileTransfer.prototype.abort = function(successCallback, errorCallback) {
    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
};

module.exports = FileTransfer;
<commit_msg>[all] Add argscheck to FileTransfer.js<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 argscheck = require('cordova/argscheck'),
    exec = require('cordova/exec'),
    FileTransferError = require('cordova/plugin/FileTransferError'),
    ProgressEvent = require('cordova/plugin/ProgressEvent');

function newProgressEvent(result) {
    var pe = new ProgressEvent();
    pe.lengthComputable = result.lengthComputable;
    pe.loaded = result.loaded;
    pe.total = result.total;
    return pe;
}

var idCounter = 0;

/**
 * FileTransfer uploads a file to a remote server.
 * @constructor
 */
var FileTransfer = function() {
    this._id = ++idCounter;
    this.onprogress = null; // optional callback
};

/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String}           Full path of the file on the device
* @param server {String}             URL of the server to receive the file
* @param successCallback (Function}  Callback to be invoked when upload has completed
* @param errorCallback {Function}    Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
    argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
    // check for options
    var fileKey = null;
    var fileName = null;
    var mimeType = null;
    var params = null;
    var chunkedMode = true;
    var headers = null;
    if (options) {
        fileKey = options.fileKey;
        fileName = options.fileName;
        mimeType = options.mimeType;
        headers = options.headers;
        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
            chunkedMode = options.chunkedMode;
        }
        if (options.params) {
            params = options.params;
        }
        else {
            params = {};
        }
    }

    var fail = errorCallback && function(e) {
        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
        errorCallback(error);
    };

    var self = this;
    var win = function(result) {
        if (typeof result.lengthComputable != "undefined") {
            if (self.onprogress) {
                self.onprogress(newProgressEvent(result));
            }
        } else {
            successCallback && successCallback(result);
        }
    };
    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
};

/**
 * Downloads a file form a given URL and saves it to the specified directory.
 * @param source {String}          URL of the server to receive the file
 * @param target {String}         Full path of the file on the device
 * @param successCallback (Function}  Callback to be invoked when upload has completed
 * @param errorCallback {Function}    Callback to be invoked upon error
 * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
 */
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
    argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
    var self = this;
    var win = function(result) {
        if (typeof result.lengthComputable != "undefined") {
            if (self.onprogress) {
                return self.onprogress(newProgressEvent(result));
            }
        } else if (successCallback) {
            var entry = null;
            if (result.isDirectory) {
                entry = new (require('cordova/plugin/DirectoryEntry'))();
            }
            else if (result.isFile) {
                entry = new (require('cordova/plugin/FileEntry'))();
            }
            entry.isDirectory = result.isDirectory;
            entry.isFile = result.isFile;
            entry.name = result.name;
            entry.fullPath = result.fullPath;
            successCallback(entry);
        }
    };

    var fail = errorCallback && function(e) {
        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
        errorCallback(error);
    };

    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
};

/**
 * Aborts the ongoing file transfer on this object
 * @param successCallback {Function}  Callback to be invoked upon success
 * @param errorCallback {Function}    Callback to be invoked upon error
 */
FileTransfer.prototype.abort = function(successCallback, errorCallback) {
    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
};

module.exports = FileTransfer;
<|endoftext|>


<commit_before>/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

//  NOTE: The popup menu with id "contactsMenu" must exist before
//  using this interface.
//
//  This interface fires two events:
//   - autocompletion:changed : fired when a new contact is selected
//   - autocompletion:changedlist : fired when a new list is selected
//
var SOGoAutoCompletionInterface = {

    // Attributes that could be changed from the object
    // inheriting the inteface
    uidField: "c_name",
    addressBook: null,
    excludeGroups: false,
    excludeLists: false,

    // Internal attributes
    animationParent: null,
    selectedIndex: -1,
    delay: 0.750,
    delayedSearch: false,
    menu: null,

    bind: function () {
        this.menu = $('contactsMenu');
        this.writeAttribute("autocomplete", "off");
        this.observe("keydown", this.onKeydown.bindAsEventListener(this));
        this.observe("blur", this.onBlur.bindAsEventListener(this));
    },

    onKeydown: function (event) {
        if (event.ctrlKey || event.metaKey) {
            this.focussed = true;
            return;
        }
        if (event.keyCode == Event.KEY_TAB) {
            if (this.confirmedValue)
                this.value = this.confirmedValue;
            else
                this.writeAttribute("uid", null);
             if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
            if (this.readAttribute("container")) {
                this.confirmedValue = null;
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
            }
            else
                this.fire("autocompletion:changed");
        }
        else if (event.keyCode == 0
                 || event.keyCode == Event.KEY_BACKSPACE
                 || event.keyCode == 32  // Space
                 || event.keyCode > 47) {
            this.confirmedValue = null;
            this.selectedIndex = -1;
            if (this.delayedSearch)
                window.clearTimeout(this.delayedSearch);
            this.delayedSearch = this.performSearch.delay(this.delay, this);
        }
        else if (event.keyCode == Event.KEY_RETURN) {
            preventDefault(event);
            if (this.confirmedValue)
                this.value = this.confirmedValue;
            else
                this.writeAttribute("uid", null);
            $(this).select();
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
            this.selectedIndex = -1;
            if (this.readAttribute("container")) {
                this.confirmedValue = null;
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
            }
            else
                this.fire("autocompletion:changed");
        }
        else if (this.menu.getStyle('visibility') == 'visible') {
            if (event.keyCode == Event.KEY_UP) { // Up arrow
                if (this.selectedIndex > 0) {
                    var contacts = this.menu.select("li");
                    contacts[this.selectedIndex--].removeClassName("selected");
                    this.value = contacts[this.selectedIndex].readAttribute("address");
                    this.confirmedValue = this.value;
                    this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
                    contacts[this.selectedIndex].addClassName("selected");
                    var container = contacts[this.selectedIndex].readAttribute("container");
                    if (container)
                        this.writeAttribute("container", container);
                }
            }
            else if (event.keyCode == Event.KEY_DOWN) { // Down arrow
                var contacts = this.menu.select("li");
                if (contacts.size() - 1 > this.selectedIndex) {
                    if (this.selectedIndex >= 0)
                        contacts[this.selectedIndex].removeClassName("selected");
                    this.selectedIndex++;
                    this.value = contacts[this.selectedIndex].readAttribute("address");
                    this.confirmedValue = this.value;
                    this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
                    contacts[this.selectedIndex].addClassName("selected");
                    var container = contacts[this.selectedIndex].readAttribute("container");
                    if (container)
                        this.writeAttribute("container", container);
                }
            }
        }
    },

    onBlur: function (event) {
        if (this.delayedSearch)
            window.clearTimeout(this.delayedSearch);
        if (this.confirmedValue) {
            this.value = this.confirmedValue;
            if (this.readAttribute("container"))
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
        }
        else
            this.writeAttribute("uid", null);
    },

    performSearch: function (input) {
        // Perform address completion
        if (document.contactLookupAjaxRequest) {
            // Abort any pending request
            document.contactLookupAjaxRequest.aborted = true;
            document.contactLookupAjaxRequest.abort();
        }
        if (input.value.trim().length > 2) {
            var urlstr = UserFolderURL + "Contacts/";
            if (input.addressBook)
                urlstr += input.addressBook + "/contact";
            else
                urlstr += "allContact";
            urlstr += "Search?search=" + encodeURIComponent(input.value);
            if (input.excludeGroups)
                urlstr += "&excludeGroups=1";
            if (input.excludeLists)
                urlstr += "&excludeLists=1";
            if (input.animationParent)
                startAnimation(input.animationParent);
            document.contactLookupAjaxRequest =
            triggerAjaxRequest(urlstr, input.performSearchCallback.bind(input), input);
        }
        else {
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
        }
    },

    performSearchCallback: function (http) {
        if (http.readyState == 4) {
            var list = this.menu.down("ul");

            var input = http.callbackData;

            if (http.status == 200) {
                var start = input.value.length;
                var data = http.responseText.evalJSON(true);

                if (data.contacts.length > 1) {
                    list.select("li").each(function(item) {
                            item.stopObserving("mousedown");
                            item.remove();
                        });

                    // Populate popup menu
                    for (var i = 0; i < data.contacts.length; i++) {
                        var contact = data.contacts[i];
                        var completeEmail = contact["c_cn"];
                        if (contact["c_mail"])
                            completeEmail += " <" + contact["c_mail"] + ">";
                        var node = new Element('li', { 'address': completeEmail,
                                                       'uid': contact[this.uidField] });
                        var matchPosition = completeEmail.toLowerCase().indexOf(data.searchText.toLowerCase());
                        var matchBefore = completeEmail.substring(0, matchPosition);
                        var matchText = completeEmail.substring(matchPosition, matchPosition + data.searchText.length);
                        var matchAfter = completeEmail.substring(matchPosition + data.searchText.length);
                        list.appendChild(node);
                        if (contact['c_name'].endsWith (".vlf")) {
                            // Keep track of list containers
                            node.writeAttribute("container", contact['container']);
                        }
                        node.appendChild(document.createTextNode(matchBefore));
                        node.appendChild(new Element('strong').update(matchText));
                        node.appendChild(document.createTextNode(matchAfter));
                        if (contact["contactInfo"])
                            node.appendChild(document.createTextNode(" (" + contact["contactInfo"] + ")"));
                        $(node).observe("mousedown", this.onAddressResultClick.bindAsEventListener(this));
                    }

                    // Show popup menu
                    var offsetScroll = Element.cumulativeScrollOffset(input);
                    var offset = Element.positionedOffset(input);
                    if (Element.up(input).tagName == 'TD')
                        // Special case for UIxListEditor
                        offset = Element.cumulativeOffset(input);
                    var top = offset.top - offsetScroll.top + node.offsetHeight + 3;
                    var height = 'auto';
                    var heightDiff = window.height() - offset[1];
                    var nodeHeight = node.getHeight();

                    if ((data.contacts.length * nodeHeight) > heightDiff)
                        // Limit the size of the popup to the window height, minus 12 pixels
                        height = parseInt(heightDiff/nodeHeight) * nodeHeight - 12 + 'px';

                    this.menu.setStyle({ top: top + "px",
                                left: offset[0] + "px",
                                height: height,
                                visibility: "visible" });
                    this.menu.scrollTop = 0;

                    document.currentPopupMenu = this.menu;
                    $(document.body).stopObserving("click");
                    $(document.body).observe("click", onBodyClickMenuHandler);
                }
                else {
                    if (document.currentPopupMenu)
                        hideMenu(document.currentPopupMenu);

                    if (data.contacts.length == 1) {
                        // Single result
                        var contact = data.contacts[0];
                        input.writeAttribute("uid", contact[this.uidField]);
                        if (contact['c_name'].endsWith(".vlf")) {
                            this.writeAttribute("container", contact['container']);
                        }
                        var completeEmail = contact["c_cn"];
                        if (contact["c_mail"])
                            completeEmail += " <" + contact["c_mail"] + ">";
                        if (contact["c_cn"].substring(0, input.value.length).toUpperCase()
                            == input.value.toUpperCase())
                            input.value = completeEmail;
                        else
                            // The result matches email address, not user name
                            input.value += ' >> ' + completeEmail;
                        input.confirmedValue = completeEmail;

                        var end = input.value.length;
                        $(input).selectText(start, end);

                        this.selectedIndex = -1;
                    }
                }
            }
            else
                if (document.currentPopupMenu)
                    hideMenu(document.currentPopupMenu);
            document.contactLookupAjaxRequest = null;
        }
    },

    onAddressResultClick: function(event) {
        var e = Event.element(event);
        if (e.tagName != 'LI')
            e = e.up('LI');
        if (e) {
            this.value = e.readAttribute("address");
            this.writeAttribute("uid", e.readAttribute("uid"));
            if (e.readAttribute("container"))
                this.fire("autocompletion:changedlist", e.readAttribute("container"));
            else {
                this.confirmedValue = this.value;
                this.fire("autocompletion:changed");
            }
        }
    }
};
<commit_msg>Fixed computation of position of auto-completion list.<commit_after>/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

//  NOTE: The popup menu with id "contactsMenu" must exist before
//  using this interface.
//
//  This interface fires two events:
//   - autocompletion:changed : fired when a new contact is selected
//   - autocompletion:changedlist : fired when a new list is selected
//
var SOGoAutoCompletionInterface = {

    // Attributes that could be changed from the object
    // inheriting the inteface
    uidField: "c_name",
    addressBook: null,
    excludeGroups: false,
    excludeLists: false,

    // Internal attributes
    animationParent: null,
    selectedIndex: -1,
    delay: 0.750,
    delayedSearch: false,
    menu: null,

    bind: function () {
        this.menu = $('contactsMenu');
        this.writeAttribute("autocomplete", "off");
        this.observe("keydown", this.onKeydown.bindAsEventListener(this));
        this.observe("blur", this.onBlur.bindAsEventListener(this));
    },

    onKeydown: function (event) {
        if (event.ctrlKey || event.metaKey) {
            this.focussed = true;
            return;
        }
        if (event.keyCode == Event.KEY_TAB) {
            if (this.confirmedValue)
                this.value = this.confirmedValue;
            else
                this.writeAttribute("uid", null);
             if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
            if (this.readAttribute("container")) {
                this.confirmedValue = null;
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
            }
            else
                this.fire("autocompletion:changed");
        }
        else if (event.keyCode == 0
                 || event.keyCode == Event.KEY_BACKSPACE
                 || event.keyCode == 32  // Space
                 || event.keyCode > 47) {
            this.confirmedValue = null;
            this.selectedIndex = -1;
            if (this.delayedSearch)
                window.clearTimeout(this.delayedSearch);
            this.delayedSearch = this.performSearch.delay(this.delay, this);
        }
        else if (event.keyCode == Event.KEY_RETURN) {
            preventDefault(event);
            if (this.confirmedValue)
                this.value = this.confirmedValue;
            else
                this.writeAttribute("uid", null);
            $(this).select();
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
            this.selectedIndex = -1;
            if (this.readAttribute("container")) {
                this.confirmedValue = null;
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
            }
            else
                this.fire("autocompletion:changed");
        }
        else if (this.menu.getStyle('visibility') == 'visible') {
            if (event.keyCode == Event.KEY_UP) { // Up arrow
                if (this.selectedIndex > 0) {
                    var contacts = this.menu.select("li");
                    contacts[this.selectedIndex--].removeClassName("selected");
                    this.value = contacts[this.selectedIndex].readAttribute("address");
                    this.confirmedValue = this.value;
                    this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
                    contacts[this.selectedIndex].addClassName("selected");
                    var container = contacts[this.selectedIndex].readAttribute("container");
                    if (container)
                        this.writeAttribute("container", container);
                }
            }
            else if (event.keyCode == Event.KEY_DOWN) { // Down arrow
                var contacts = this.menu.select("li");
                if (contacts.size() - 1 > this.selectedIndex) {
                    if (this.selectedIndex >= 0)
                        contacts[this.selectedIndex].removeClassName("selected");
                    this.selectedIndex++;
                    this.value = contacts[this.selectedIndex].readAttribute("address");
                    this.confirmedValue = this.value;
                    this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
                    contacts[this.selectedIndex].addClassName("selected");
                    var container = contacts[this.selectedIndex].readAttribute("container");
                    if (container)
                        this.writeAttribute("container", container);
                }
            }
        }
    },

    onBlur: function (event) {
        if (this.delayedSearch)
            window.clearTimeout(this.delayedSearch);
        if (this.confirmedValue) {
            this.value = this.confirmedValue;
            if (this.readAttribute("container"))
                this.fire("autocompletion:changedlist", this.readAttribute("container"));
        }
        else
            this.writeAttribute("uid", null);
    },

    performSearch: function (input) {
        // Perform address completion
        if (document.contactLookupAjaxRequest) {
            // Abort any pending request
            document.contactLookupAjaxRequest.aborted = true;
            document.contactLookupAjaxRequest.abort();
        }
        if (input.value.trim().length > 2) {
            var urlstr = UserFolderURL + "Contacts/";
            if (input.addressBook)
                urlstr += input.addressBook + "/contact";
            else
                urlstr += "allContact";
            urlstr += "Search?search=" + encodeURIComponent(input.value);
            if (input.excludeGroups)
                urlstr += "&excludeGroups=1";
            if (input.excludeLists)
                urlstr += "&excludeLists=1";
            if (input.animationParent)
                startAnimation(input.animationParent);
            document.contactLookupAjaxRequest =
            triggerAjaxRequest(urlstr, input.performSearchCallback.bind(input), input);
        }
        else {
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);
        }
    },

    performSearchCallback: function (http) {
        if (http.readyState == 4) {
            var list = this.menu.down("ul");

            var input = http.callbackData;

            if (http.status == 200) {
                var start = input.value.length;
                var data = http.responseText.evalJSON(true);

                if (data.contacts.length > 1) {
                    list.select("li").each(function(item) {
                            item.stopObserving("mousedown");
                            item.remove();
                        });

                    // Populate popup menu
                    for (var i = 0; i < data.contacts.length; i++) {
                        var contact = data.contacts[i];
                        var completeEmail = contact["c_cn"];
                        if (contact["c_mail"])
                            completeEmail += " <" + contact["c_mail"] + ">";
                        var node = new Element('li', { 'address': completeEmail,
                                                       'uid': contact[this.uidField] });
                        var matchPosition = completeEmail.toLowerCase().indexOf(data.searchText.toLowerCase());
                        var matchBefore = completeEmail.substring(0, matchPosition);
                        var matchText = completeEmail.substring(matchPosition, matchPosition + data.searchText.length);
                        var matchAfter = completeEmail.substring(matchPosition + data.searchText.length);
                        list.appendChild(node);
                        if (contact['c_name'].endsWith (".vlf")) {
                            // Keep track of list containers
                            node.writeAttribute("container", contact['container']);
                        }
                        node.appendChild(document.createTextNode(matchBefore));
                        node.appendChild(new Element('strong').update(matchText));
                        node.appendChild(document.createTextNode(matchAfter));
                        if (contact["contactInfo"])
                            node.appendChild(document.createTextNode(" (" + contact["contactInfo"] + ")"));
                        $(node).observe("mousedown", this.onAddressResultClick.bindAsEventListener(this));
                    }

                    // Show popup menu
                    var offsetScroll = Element.cumulativeScrollOffset(input);
                    var offset = Element.positionedOffset(input);
                    if (offset.top < 50)
                        // Hack for some situations where the offset must be computed differently
                        offset = Element.cumulativeOffset(input);
                    var top = offset.top - offsetScroll.top + node.offsetHeight + 3;
                    var height = 'auto';
                    var heightDiff = window.height() - offset[1];
                    var nodeHeight = node.getHeight();

                    if ((data.contacts.length * nodeHeight) > heightDiff)
                        // Limit the size of the popup to the window height, minus 12 pixels
                        height = parseInt(heightDiff/nodeHeight) * nodeHeight - 12 + 'px';

                    this.menu.setStyle({ top: top + "px",
                                left: offset[0] + "px",
                                height: height,
                                visibility: "visible" });
                    this.menu.scrollTop = 0;

                    document.currentPopupMenu = this.menu;
                    $(document.body).stopObserving("click");
                    $(document.body).observe("click", onBodyClickMenuHandler);
                }
                else {
                    if (document.currentPopupMenu)
                        hideMenu(document.currentPopupMenu);

                    if (data.contacts.length == 1) {
                        // Single result
                        var contact = data.contacts[0];
                        input.writeAttribute("uid", contact[this.uidField]);
                        if (contact['c_name'].endsWith(".vlf")) {
                            this.writeAttribute("container", contact['container']);
                        }
                        var completeEmail = contact["c_cn"];
                        if (contact["c_mail"])
                            completeEmail += " <" + contact["c_mail"] + ">";
                        if (contact["c_cn"].substring(0, input.value.length).toUpperCase()
                            == input.value.toUpperCase())
                            input.value = completeEmail;
                        else
                            // The result matches email address, not user name
                            input.value += ' >> ' + completeEmail;
                        input.confirmedValue = completeEmail;

                        var end = input.value.length;
                        $(input).selectText(start, end);

                        this.selectedIndex = -1;
                    }
                }
            }
            else
                if (document.currentPopupMenu)
                    hideMenu(document.currentPopupMenu);
            document.contactLookupAjaxRequest = null;
        }
    },

    onAddressResultClick: function(event) {
        var e = Event.element(event);
        if (e.tagName != 'LI')
            e = e.up('LI');
        if (e) {
            this.value = e.readAttribute("address");
            this.writeAttribute("uid", e.readAttribute("uid"));
            if (e.readAttribute("container"))
                this.fire("autocompletion:changedlist", e.readAttribute("container"));
            else {
                this.confirmedValue = this.value;
                this.fire("autocompletion:changed");
            }
        }
    }
};
<|endoftext|>


<commit_before>var setting = require('../settings.js');
var Enum = require('../enum.js');
var format = require('../format.js');
var Feed = require('feed');
var log = require('winston');
/**
 * @type {number}
 * @const
 */
var MAX_RETRIES = 10;
/** @type {number} */
var retries = 0;

/**
 * Default route action
 */
exports.view = function(req, res)
{
	var library = require('../models/library.js');

	if (!library.postInfoLoaded)
	{
		if (retries > MAX_RETRIES)
		{
			log.error('Unable to load library after %d tries', MAX_RETRIES);
			res.render(Enum.httpStatus.notFound, {'title': 'Unable to load feed'});
			// reset tries so page can be refreshed
			retries = 0;
		}
		else
		{
			retries++;
			log.error('Library not ready when creating RSS feed — attempt %d', retries);
			setTimeout(function() { exports.view(req, res); }, 2000);
		}
		return;
	}

	/** @type {Post} */
	var post = null,
		feed = new Feed(
	{
		title:          setting.title,
		description:    setting.description,
		link:           'http://' + setting.domain,
		image:          'http://' + setting.domain + '/img/logo.png',
		copyright:      'Copyright © 2014 Jason Abbott. All rights reserved',
		author:
		{
			name:       'Jason Abbott',
			link:       'https://www.facebook.com/jason.e.abbott'
		}
	});

	for (var i = 0; i < library.posts.length; i++)
	{
		post = library.posts[i];

		if (post.chronological)
		{
			feed.addItem({
				title: post.title,
				link: format.string('http://{0}/{1}/', setting.domain, post.slug),
				description: post.description,
				date: post.createdOn
			});
		}
	}
	res.set('Content-Type', 'text/xml');
	res.send(feed.render('rss-2.0'));
};<commit_msg>feed tweaks<commit_after>var setting = require('../settings.js');
var Enum = require('../enum.js');
var format = require('../format.js');
var Feed = require('feed');
var log = require('winston');
/**
 * @type {number}
 * @const
 */
var MAX_RETRIES = 10;
/** @type {number} */
var retries = 0;

/**
 * Default route action
 */
exports.view = function(req, res)
{
	var library = require('../models/library.js');

	if (!library.postInfoLoaded)
	{
		if (retries >= MAX_RETRIES)
		{
			log.error('Unable to load library after %d tries', MAX_RETRIES);
			res.render(Enum.httpStatus.notFound, {'title': 'Unable to load feed'});
			// reset tries so page can be refreshed
			retries = 0;
		}
		else
		{
			retries++;
			log.error('Library not ready when creating RSS feed — attempt %d', retries);
			setTimeout(function() { exports.view(req, res); }, 3000);
		}
		return;
	}

	var author = { name: 'Jason Abbott', link: 'https://www.facebook.com/jason.e.abbott' };
	var copyright = 'Copyright © ' + new Date().getFullYear() + ' Jason Abbott. All rights reserved';

	/** @type {Post} */
	var post = null,
		feed = new Feed(
	{
		title:          setting.title,
		description:    setting.description,
		link:           'http://' + setting.domain,
		image:          'http://' + setting.domain + '/img/logo.png',
		copyright:      copyright,
		author:         author

	});

	for (var i = 0; i < library.posts.length; i++)
	{
		post = library.posts[i];

		if (post.chronological)
		{
			feed.addItem({
				image: post.bigThumb,
				author: author,
				copyright: copyright,
				title: post.title,
				link: format.string('http://{0}/{1}/', setting.domain, post.slug),
				description: post.description,
				date: post.createdOn
			});
		}
	}
	res.set('Content-Type', 'text/xml');
	res.send(feed.render('rss-2.0'));
};<|endoftext|>


<commit_before>import path from 'path';
import { Router } from 'express';
import webpack from 'webpack';

import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';

import { logger } from '@storybook/node-logger';
import { getMiddleware } from './utils/middleware';
import { logConfig } from './logConfig';
import loadConfig from './config';
import loadManagerConfig from './manager/manager-config';

const dllPath = path.join(__dirname, '../../dll');

const cache = {};

let previewProcess;
let previewReject;
let resolved = false;

const router = new Router();

export default function(options) {
  const configDir = path.resolve(options.configDir);
  const outputDir = path.resolve(options.outputDir || path.join(__dirname, '..', 'public'));
  const configType = 'DEVELOPMENT';

  const startTime = process.hrtime();
  let managerTotalTime;
  let previewTotalTime;

  const managerPromise = loadManagerConfig({
    configType,
    outputDir,
    configDir,
    cache,
    corePresets: [require.resolve('./manager/manager-preset.js')],
    ...options,
  }).then(config => {
    if (options.debugWebpack) {
      logConfig('Manager webpack config', config, logger);
    }
    return new Promise((resolve, reject) => {
      webpack(config).watch(
        {
          aggregateTimeout: 1,
          ignored: /node_modules/,
        },
        (err, stats) => {
          managerTotalTime = process.hrtime(startTime);
          if (!resolved && (err || stats.hasErrors())) {
            const error = new Error('Manager build is broken');
            error.error = err;
            error.close = true;
            error.stats = stats;
            stats.toJson(options.errorDetails);
            logger.line();
            logger.line();
            try {
              previewReject(error);
              previewProcess.close();
              logger.warn('force closed preview build');
            } catch (e) {
              logger.warn('Unable to close preview build!');
            }
            logger.line();
            reject(error);
          } else {
            resolve(stats);
          }
        }
      );
    });
  });

  const previewPromise = options.ignorePreview
    ? new Promise(resolve => resolve())
    : loadConfig({
        configType,
        outputDir,
        cache,
        corePresets: [require.resolve('./preview/preview-preset.js')],
        overridePresets: [require.resolve('./preview/custom-webpack-preset.js')],
        ...options,
      }).then(previewConfig => {
        if (options.debugWebpack) {
          logConfig('Preview webpack config', previewConfig, logger);
        }

        // remove the leading '/'
        let { publicPath } = previewConfig.output;
        if (publicPath[0] === '/') {
          publicPath = publicPath.slice(1);
        }

        const previewCompiler = webpack(previewConfig);

        const devMiddlewareOptions = {
          publicPath: previewConfig.output.publicPath,
          watchOptions: {
            aggregateTimeout: 1,
            ignored: /node_modules/,
            ...(previewConfig.watchOptions || {}),
          },
          // this actually causes 0 (regular) output from wdm & webpack
          logLevel: 'warn',
          clientLogLevel: 'warning',
          noInfo: true,
          ...previewConfig.devServer,
        };

        const webpackDevMiddlewareInstance = webpackDevMiddleware(
          previewCompiler,
          devMiddlewareOptions
        );

        router.use(webpackDevMiddlewareInstance);
        router.use(webpackHotMiddleware(previewCompiler));

        return new Promise((resolve, reject) => {
          previewReject = reject;
          webpackDevMiddlewareInstance.waitUntilValid(stats => {
            previewTotalTime = process.hrtime(startTime);

            if (!stats) {
              reject(new Error('no stats after building preview'));
            } else if (stats.hasErrors()) {
              reject(stats);
            } else {
              resolve(stats);
            }
          });
          previewProcess = webpackDevMiddlewareInstance;
        });
      });

  // custom middleware
  const middlewareFn = getMiddleware(configDir);
  middlewareFn(router);

  return Promise.all([managerPromise, previewPromise]).then(([managerStats, previewStats]) => {
    resolved = true;
    router.get('/', (request, response) => {
      response.set('Content-Type', 'text/html');
      response.sendFile(path.join(`${outputDir}/index.html`));
    });
    router.get(/\/sb_dll\/(.+\.js)$/, (request, response) => {
      response.set('Content-Type', 'text/javascript');
      response.sendFile(path.join(`${dllPath}/${request.params[0]}`));
    });
    router.get(/\/sb_dll\/(.+\.LICENCE)$/, (request, response) => {
      response.set('Content-Type', 'text/html');
      response.sendFile(path.join(`${dllPath}/${request.params[0]}`));
    });
    router.get(/(.+\.js)$/, (request, response) => {
      response.set('Content-Type', 'text/javascript');
      response.sendFile(path.join(`${outputDir}/${request.params[0]}`));
    });

    return { previewStats, managerStats, managerTotalTime, previewTotalTime, router };
  });
}
<commit_msg>feat(core): move webpack default output from `storybook/core` own package folder to SB cache folder<commit_after>import path from 'path';
import { Router } from 'express';
import webpack from 'webpack';

import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';

import { logger } from '@storybook/node-logger';
import { getMiddleware } from './utils/middleware';
import { logConfig } from './logConfig';
import loadConfig from './config';
import loadManagerConfig from './manager/manager-config';
import { resolvePathInStorybookCache } from './utils/resolve-path-in-sb-cache';

const dllPath = path.join(__dirname, '../../dll');

const cache = {};

let previewProcess;
let previewReject;
let resolved = false;

const router = new Router();

export default function(options) {
  const configDir = path.resolve(options.configDir);
  const outputDir = path.resolve(options.outputDir || resolvePathInStorybookCache('public'));
  const configType = 'DEVELOPMENT';

  const startTime = process.hrtime();
  let managerTotalTime;
  let previewTotalTime;

  const managerPromise = loadManagerConfig({
    configType,
    outputDir,
    configDir,
    cache,
    corePresets: [require.resolve('./manager/manager-preset.js')],
    ...options,
  }).then(config => {
    if (options.debugWebpack) {
      logConfig('Manager webpack config', config, logger);
    }
    return new Promise((resolve, reject) => {
      webpack(config).watch(
        {
          aggregateTimeout: 1,
          ignored: /node_modules/,
        },
        (err, stats) => {
          managerTotalTime = process.hrtime(startTime);
          if (!resolved && (err || stats.hasErrors())) {
            const error = new Error('Manager build is broken');
            error.error = err;
            error.close = true;
            error.stats = stats;
            stats.toJson(options.errorDetails);
            logger.line();
            logger.line();
            try {
              previewReject(error);
              previewProcess.close();
              logger.warn('force closed preview build');
            } catch (e) {
              logger.warn('Unable to close preview build!');
            }
            logger.line();
            reject(error);
          } else {
            resolve(stats);
          }
        }
      );
    });
  });

  const previewPromise = options.ignorePreview
    ? new Promise(resolve => resolve())
    : loadConfig({
        configType,
        outputDir,
        cache,
        corePresets: [require.resolve('./preview/preview-preset.js')],
        overridePresets: [require.resolve('./preview/custom-webpack-preset.js')],
        ...options,
      }).then(previewConfig => {
        if (options.debugWebpack) {
          logConfig('Preview webpack config', previewConfig, logger);
        }

        // remove the leading '/'
        let { publicPath } = previewConfig.output;
        if (publicPath[0] === '/') {
          publicPath = publicPath.slice(1);
        }

        const previewCompiler = webpack(previewConfig);

        const devMiddlewareOptions = {
          publicPath: previewConfig.output.publicPath,
          watchOptions: {
            aggregateTimeout: 1,
            ignored: /node_modules/,
            ...(previewConfig.watchOptions || {}),
          },
          // this actually causes 0 (regular) output from wdm & webpack
          logLevel: 'warn',
          clientLogLevel: 'warning',
          noInfo: true,
          ...previewConfig.devServer,
        };

        const webpackDevMiddlewareInstance = webpackDevMiddleware(
          previewCompiler,
          devMiddlewareOptions
        );

        router.use(webpackDevMiddlewareInstance);
        router.use(webpackHotMiddleware(previewCompiler));

        return new Promise((resolve, reject) => {
          previewReject = reject;
          webpackDevMiddlewareInstance.waitUntilValid(stats => {
            previewTotalTime = process.hrtime(startTime);

            if (!stats) {
              reject(new Error('no stats after building preview'));
            } else if (stats.hasErrors()) {
              reject(stats);
            } else {
              resolve(stats);
            }
          });
          previewProcess = webpackDevMiddlewareInstance;
        });
      });

  // custom middleware
  const middlewareFn = getMiddleware(configDir);
  middlewareFn(router);

  return Promise.all([managerPromise, previewPromise]).then(([managerStats, previewStats]) => {
    resolved = true;
    router.get('/', (request, response) => {
      response.set('Content-Type', 'text/html');
      response.sendFile(path.join(`${outputDir}/index.html`));
    });
    router.get(/\/sb_dll\/(.+\.js)$/, (request, response) => {
      response.set('Content-Type', 'text/javascript');
      response.sendFile(path.join(`${dllPath}/${request.params[0]}`));
    });
    router.get(/\/sb_dll\/(.+\.LICENCE)$/, (request, response) => {
      response.set('Content-Type', 'text/html');
      response.sendFile(path.join(`${dllPath}/${request.params[0]}`));
    });
    router.get(/(.+\.js)$/, (request, response) => {
      response.set('Content-Type', 'text/javascript');
      response.sendFile(path.join(`${outputDir}/${request.params[0]}`));
    });

    return { previewStats, managerStats, managerTotalTime, previewTotalTime, router };
  });
}
<|endoftext|>


<commit_before>
/**
 * Determines whether a rule should run
 * @param  {Rule}    rule     The rule to test
 * @param  {Context} context  The context of the Audit
 * @param  {Object}  options  Options object
 * @return {Boolean}
 */
utils.ruleShouldRun = function (rule, context, options) {
	'use strict';
	if (rule.pageLevel && !context.page) {
		return false;
	}

	var runOnly = options.runOnly,
		ruleOptions = (options.rules || {})[rule.id];

	if (runOnly) {
		if (runOnly.type === 'rule') {
			return runOnly.values.indexOf(rule.id) !== -1;
		}

		return !!(runOnly.values || []).filter(function (item) {
			if (typeof ruleOptions === 'object' && typeof ruleOptions.enabled === 'boolean') {
				return ruleOptions.enabled;
			}
			return rule.tags.indexOf(item) !== -1;
		}).length;
	}

	if ((ruleOptions && ruleOptions.hasOwnProperty('enabled')) ? !ruleOptions.enabled : !rule.enabled) {
		return false;
	}

	return true;
};<commit_msg>Modified rule-should-run to take in/exclude object<commit_after>/**
 * Check if a rule matches the value of runOnly type=tag
 * @private
 * @param  {object} rule
 * @param  {object}	runOnly Value of runOnly with type=tags
 * @return {bool}
 */
function matchTags(rule, runOnly) {
	'use strict';
	var include, exclude, matching;

	// normalize include/exclude
	if (runOnly.include || runOnly.exclude) {
		// Wrap include and exclude if it's not already an array
		include = runOnly.include || [];
		include = Array.isArray(include) ? include : [include];

		exclude = runOnly.exclude || [];
		exclude = Array.isArray(exclude) ? exclude : [exclude];

	// Otherwise, only use the include value, ignore exclude
	} else {
		include = Array.isArray(runOnly) ? runOnly : [runOnly];
		exclude = [];
	}

	matching = include.some(function (tag) {
		return (rule.tags.indexOf(tag) !== -1);
	});

	if (matching) {
		return !exclude.some(function (tag) {
			return (rule.tags.indexOf(tag) !== -1);
		});

	} else {
		return false;
	}
}

/**
 * Determines whether a rule should run
 * @param  {Rule}    rule     The rule to test
 * @param  {Context} context  The context of the Audit
 * @param  {Object}  options  Options object
 * @return {Boolean}
 */
utils.ruleShouldRun = function (rule, context, options) {
	'use strict';
	var runOnly = options.runOnly || {};
	var ruleOptions = (options.rules || {})[rule.id];

	// Never run page level rules if the context is not on the page
	if (rule.pageLevel && !context.page) {
		return false;

	// First, runOnly type rule overrides anything else
	} else if (runOnly.type === 'rule') {
		return runOnly.values.indexOf(rule.id) !== -1;

	// Second, if options.rules[rule].enabled is set, it overrides all
	} else if (ruleOptions && typeof ruleOptions.enabled === 'boolean') {
		return ruleOptions.enabled;

	// Third, if tags are set, look at those
	} else if(runOnly.type === 'tag' && runOnly.values) {
		return matchTags(rule, runOnly.values);

	// If nothing is set, use the default
	} else {
		return !!rule.enabled;
	}

};<|endoftext|>


<commit_before>Ext.namespace('Vpc.Directories.Item.Directory');
Vpc.Directories.Item.Directory.TabsPanel = Ext.extend(Vps.Binding.ProxyPanel,
{
    initComponent: function() {

        if (this.componentPlugins) {
            this.plugins = [ ];
            this.componentPlugins.each(function(v) {
                var pluginClass = eval(v.pluginClass);
                var plugin = new pluginClass(v);
                this.plugins.push(plugin);
            }, this);
        }

        this.layout = 'border';
        var grid = new Vps.Auto.GridPanel({
            controllerUrl: this.controllerUrl,
            region: 'west',
            width: 500,
            split: true
        });

        this.detailsForm = new Vps.Auto.FormPanel({
            controllerUrl: this.detailsControllerUrl,
            title: trlVps('Details')
        });
        grid.addBinding(this.detailsForm);

        this.editPanels = [this.detailsForm];
        this.contentEditComponents.each(function(ec) {
            var componentConfig = this.componentConfigs[ec.componentClass+'-'+ec.type];
            if (componentConfig.needsComponentPanel) {
                var panel = new Vps.Component.ComponentPanel({
                    title: componentConfig.title,
                    mainComponentClass: ec.componentClass,
                    mainType: ec.type,
                    mainComponentId: ec.idTemplate+ec.componentIdSuffix,
                    componentConfigs: this.componentConfigs,
                    mainEditComponents: [ec]
                });
                grid.addBinding(panel);
            } else {
                var panel = Ext.ComponentMgr.create(componentConfig);
                grid.addBinding({
                    item: panel,
                    componentId: ec.idTemplate+ec.componentIdSuffix
                });
            }
            this.editPanels.push(panel);
        }, this);

        var tabs = new Ext.TabPanel({
            region: 'center',
            activeTab: 0,
            items: this.editPanels
        });
        this.proxyItem = grid;
        this.items = [grid, tabs];

        Vpc.Directories.Item.Directory.TabsPanel.superclass.initComponent.call(this);
    },
    applyBaseParams: function(baseParams) {
        this.detailsForm.setBaseParams(baseParams);
        return Vpc.Directories.Item.Directory.TabsPanel.superclass.applyBaseParams.apply(this, arguments);
    },
    setBaseParams : function(baseParams) {
        this.detailsForm.setBaseParams(baseParams);
        return Vpc.Directories.Item.Directory.TabsPanel.superclass.setBaseParams.apply(this, arguments);
    },

});

Ext.reg('vpc.directories.item.directory.tabs', Vpc.Directories.Item.Directory.TabsPanel);
<commit_msg>beistrich zuviel; ie suxx<commit_after>Ext.namespace('Vpc.Directories.Item.Directory');
Vpc.Directories.Item.Directory.TabsPanel = Ext.extend(Vps.Binding.ProxyPanel,
{
    initComponent: function() {

        if (this.componentPlugins) {
            this.plugins = [ ];
            this.componentPlugins.each(function(v) {
                var pluginClass = eval(v.pluginClass);
                var plugin = new pluginClass(v);
                this.plugins.push(plugin);
            }, this);
        }

        this.layout = 'border';
        var grid = new Vps.Auto.GridPanel({
            controllerUrl: this.controllerUrl,
            region: 'west',
            width: 500,
            split: true
        });

        this.detailsForm = new Vps.Auto.FormPanel({
            controllerUrl: this.detailsControllerUrl,
            title: trlVps('Details')
        });
        grid.addBinding(this.detailsForm);

        this.editPanels = [this.detailsForm];
        this.contentEditComponents.each(function(ec) {
            var componentConfig = this.componentConfigs[ec.componentClass+'-'+ec.type];
            if (componentConfig.needsComponentPanel) {
                var panel = new Vps.Component.ComponentPanel({
                    title: componentConfig.title,
                    mainComponentClass: ec.componentClass,
                    mainType: ec.type,
                    mainComponentId: ec.idTemplate+ec.componentIdSuffix,
                    componentConfigs: this.componentConfigs,
                    mainEditComponents: [ec]
                });
                grid.addBinding(panel);
            } else {
                var panel = Ext.ComponentMgr.create(componentConfig);
                grid.addBinding({
                    item: panel,
                    componentId: ec.idTemplate+ec.componentIdSuffix
                });
            }
            this.editPanels.push(panel);
        }, this);

        var tabs = new Ext.TabPanel({
            region: 'center',
            activeTab: 0,
            items: this.editPanels
        });
        this.proxyItem = grid;
        this.items = [grid, tabs];

        Vpc.Directories.Item.Directory.TabsPanel.superclass.initComponent.call(this);
    },
    applyBaseParams: function(baseParams) {
        this.detailsForm.setBaseParams(baseParams);
        return Vpc.Directories.Item.Directory.TabsPanel.superclass.applyBaseParams.apply(this, arguments);
    },
    setBaseParams : function(baseParams) {
        this.detailsForm.setBaseParams(baseParams);
        return Vpc.Directories.Item.Directory.TabsPanel.superclass.setBaseParams.apply(this, arguments);
    }

});

Ext.reg('vpc.directories.item.directory.tabs', Vpc.Directories.Item.Directory.TabsPanel);
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash'),
    $ = require('jquery');

// document wide unique overlay ids
var ids = new (require('../../util/IdGenerator'))('ov');

/**
 * A plugin that allows users to attach overlays to diagram elements.
 *
 * The overlay service will take care of overlay positioning during updates.
 *
 * @class
 *
 * @example
 *
 * // add a pink badge on the top left of the shape
 * overlays.add(someShape, {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 * // or add via shape id
 *
 * overlays.add('some-element-id', {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 * // or add with optional type
 *
 * overlays.add(someShape, 'badge', {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 *
 * // remove an overlay
 *
 * var id = overlays.add(...);
 * overlays.remove(id);
 *
 * @param {EventBus} eventBus
 * @param {Canvas} canvas
 * @param {ElementRegistry} elementRegistry
 */
function Overlays(config, eventBus, canvas, elementRegistry) {

  this._eventBus = eventBus;
  this._canvas = canvas;
  this._elementRegistry = elementRegistry;

  this._ids = ids;

  this._overlayDefaults = {
    show: {
      trigger: 'automatic',
      minZoom: 0.7,
      maxZoom: 5.0
    }
  };

  /**
   * Mapping overlay-id > overlay
   */
  this._overlays = {};

  /**
   * Mapping element-id > overlay container
   */
  this._overlayContainers = {};

  // root html element for all overlays
  this._overlayRoot = $('
')
                        .css({ position: 'absolute', width: 0, height: 0 })
                        .prependTo(canvas.getContainer());

  this._init(config);
}

Overlays.$inject = [ 'config.overlays', 'eventBus', 'canvas', 'elementRegistry' ];

module.exports = Overlays;

/**
 * Returns the overlay with the specified id or a list of overlays
 * for an element with a given type.
 *
 * @example
 *
 * // return the single overlay with the given id
 * overlays.get('some-id');
 *
 * // return all overlays for the shape
 * overlays.get({ element: someShape });
 *
 * // return all overlays on shape with type 'badge'
 * overlays.get({ element: someShape, type: 'badge' });
 *
 * // shape can also be specified as id
 * overlays.get({ element: 'element-id', type: 'badge' });
 *
 *
 * @param {Object} filter
 * @param {String} [filter.id]
 * @param {String|djs.model.Base} [filter.element]
 * @param {String} [filter.type]
 *
 * @return {Object|Array<Object>} the overlay(s)
 */
Overlays.prototype.get = function(filter) {

  if (_.isString(filter)) {
    filter = { id: filter };
  }

  if (filter.element) {
    var container = this._getOverlayContainer(filter.element, true);

    // return a list of overlays when filtering by element (+type)
    if (container) {
      return filter.type ? _.filter(container.overlays, { type: filter.type }) : _.clone(container.overlays);
    } else {
      return [];
    }
  } else
  if (filter.type) {
    return _.filter(this._overlays, { type: filter.type });
  } else {
    // return single element when filtering by id
    return filter.id ? this._overlays[filter.id] : null;
  }
};

/**
 * Adds a HTML overlay to an element.
 *
 * @param {String|djs.model.Base}   element   attach overlay to this shape
 * @param {String}                  [type]    optional type to assign to the overlay
 * @param {Object}                  overlay   the overlay configuration
 *
 * @param {String|DOMElement}       overlay.html                      html element to use as an overlay
 * @param {Object}                  [overlay.show]                    show configuration
 * @param {Number}                  overlay.show.minZoom              minimal zoom level to show the overlay
 * @param {Number}                  overlay.show.maxZoom              maximum zoom level to show the overlay
 * @param {String}                  [overlay.show.trigger=automatic]  automatic or manual (user triggers show)
 * @param {Object}                  overlay.show.position             where to attach the overlay
 * @param {Number}                  [overlay.show.position.left]      relative to element bbox left attachment
 * @param {Number}                  [overlay.show.position.top]       relative to element bbox top attachment
 * @param {Number}                  [overlay.show.position.bottom]    relative to element bbox bottom attachment
 * @param {Number}                  [overlay.show.position.right]     relative to element bbox right attachment
 *
 * @return {String}                 id that may be used to reference the overlay for update or removal
 */
Overlays.prototype.add = function(element, type, overlay) {

  if (_.isObject(type)) {
    overlay = type;
    type = null;
  }

  if (_.isString(element)) {
    element = this._elementRegistry.getById(element);
  }

  if (element.waypoints) {
    throw new Error('overlays for connections are not supported');
  }

  if (!overlay.position) {
    throw new Error('must specifiy overlay position');
  }

  if (!overlay.html) {
    throw new Error('must specifiy overlay html');
  }

  if (!element) {
    throw new Error('invalid element specified');
  }

  var id = this._ids.next();

  overlay = _.extend({}, this._overlayDefaults, overlay, {
    id: id,
    type: type,
    element: element,
    html: $(overlay.html)
  });

  this._addOverlay(overlay);

  return id;
};

/**
 * Remove an overlay with the given id or all overlays matching the given filter.
 *
 * @see Overlays#get for filter options.
 *
 * @param {String} [id]
 * @param {Object} [filter]
 */
Overlays.prototype.remove = function(filter) {

  var overlays = this.get(filter) || [];

  if (!_.isArray(overlays)) {
    overlays = [ overlays ];
  }

  var self = this;

  _.forEach(overlays, function(overlay) {

    var container = self._getOverlayContainer(overlay.element, true);

    if (overlay) {
      overlay.html.remove();
      overlay.htmlContainer.remove();

      delete overlay.htmlContainer;
      delete overlay.element;

      delete self._overlays[overlay.id];
    }

    if (container) {
      var idx = container.overlays.indexOf(overlay);
      if (idx !== -1) {
        container.overlays.splice(idx, 1);
      }
    }
  });

};

Overlays.prototype.show = function() {
  this._overlayRoot.show();
};

Overlays.prototype.hide = function() {
  this._overlayRoot.hide();
};

Overlays.prototype._updateOverlayContainer = function(container) {
  var element = container.element,
      html = container.html;

  // update container left,top according to the elements x,y coordinates
  // this ensures we can attach child elements relative to this container

  // TODO(nre): update according to element bbox (for connections)
  html.css({ left: element.x, top: element.y });
};

Overlays.prototype._updateOverlay = function(overlay) {

  var position = overlay.position,
      htmlContainer = overlay.htmlContainer,
      element = overlay.element;

  // update overlay html relative to shape because
  // it is already positioned on the element

  // update relative
  var left = position.left,
      top = position.top;

  if (position.right !== undefined) {
    left = position.right * -1 + element.width;
  }

  if (position.bottom !== undefined) {
    top = position.bottom * -1 + element.height;
  }

  htmlContainer.css({ left: left || 0, top: top || 0 });
};

Overlays.prototype._createOverlayContainer = function(element) {
  var html = $('
')
                .addClass('djs-overlays')
                .addClass('djs-overlays-' + element.id)
                .css({ position: 'absolute' });

  html.appendTo(this._overlayRoot);

  var container = {
    html: html,
    element: element,
    overlays: []
  };

  this._updateOverlayContainer(container);

  return container;
};

Overlays.prototype._updateRoot = function(viewbox) {
  var a = viewbox.scale || 1;
  var d = viewbox.scale || 1;
  var e = viewbox.x || 0;
  var f = viewbox.y || 0;

  var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';

  this._overlayRoot.css('transform', matrix);
};

Overlays.prototype._getOverlayContainer = function(element, raw) {
  var id = (element && element.id) || element;

  var container = this._overlayContainers[id];
  if (!container && !raw) {
    container = this._overlayContainers[id] = this._createOverlayContainer(element);
  }

  return container;
};

Overlays.prototype._addOverlay = function(overlay) {

  var id = overlay.id,
      element = overlay.element;

  var container = this._getOverlayContainer(element);

  var htmlContainer = $('
', {
    id: id,
    className: 'djs-overlay'
  }).css({ position: 'absolute' }).append(overlay.html);

  if (overlay.type) {
    htmlContainer.addClass('djs-overlay-' + overlay.type);
  }

  overlay.htmlContainer = htmlContainer;

  container.overlays.push(overlay);
  container.html.append(htmlContainer);

  this._overlays[id] = overlay;

  this._updateOverlay(overlay);
};

Overlays.prototype._updateOverlayVisibilty = function(viewbox) {

  _.forEach(this._overlays, function(overlay) {
    if (overlay.show) {
      if (overlay.show.minZoom > viewbox.scale ||
          overlay.show.maxZoom < viewbox.scale) {
        overlay.htmlContainer.hide();
      } else {
        overlay.htmlContainer.show();
      }
    }
  });
};

Overlays.prototype._init = function(config) {

  var eventBus = this._eventBus;

  var self = this;

  // scroll/zoom integration

  var updateViewbox = function(viewbox) {
    self._updateRoot(viewbox);
    self._updateOverlayVisibilty(viewbox);

    self.show();
  };

  if (!config || config.deferUpdate !== false) {
    updateViewbox = _.debounce(updateViewbox, 300);
  }

  eventBus.on('canvas.viewbox.changed', function(event) {
    self.hide();
    updateViewbox(event.viewbox);
  });

  // remove integration

  eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
    var overlays = self.get({ element: e.element });

    _.forEach(overlays, function(o) {
      self.remove(o.id);
    });
  });

  // move integration

  eventBus.on([
    'commandStack.shape.move.executed',
    'commandStack.shape.move.reverted'
  ], function(e) {
    var element = e.context.shape;

    var container = self._getOverlayContainer(element, true);
    if (container) {
      self._updateOverlayContainer(container);
    }
  });

  // marker integration, simply add them on the overlays as classes, too.

  eventBus.on('element.marker.update', function(e) {
    var container = self._getOverlayContainer(e.element, true);
    if (container) {
      container.html[e.add ? 'addClass' : 'removeClass'](e.marker);
    }
  });
};<commit_msg>fix(overlays): correctly assign djs-overlay class<commit_after>'use strict';

var _ = require('lodash'),
    $ = require('jquery');

// document wide unique overlay ids
var ids = new (require('../../util/IdGenerator'))('ov');

/**
 * A plugin that allows users to attach overlays to diagram elements.
 *
 * The overlay service will take care of overlay positioning during updates.
 *
 * @class
 *
 * @example
 *
 * // add a pink badge on the top left of the shape
 * overlays.add(someShape, {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 * // or add via shape id
 *
 * overlays.add('some-element-id', {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 * // or add with optional type
 *
 * overlays.add(someShape, 'badge', {
 *   position: {
 *     top: -5,
 *     left: -5
 *   }
 *   html: '
0
'
 * });
 *
 *
 * // remove an overlay
 *
 * var id = overlays.add(...);
 * overlays.remove(id);
 *
 * @param {EventBus} eventBus
 * @param {Canvas} canvas
 * @param {ElementRegistry} elementRegistry
 */
function Overlays(config, eventBus, canvas, elementRegistry) {

  this._eventBus = eventBus;
  this._canvas = canvas;
  this._elementRegistry = elementRegistry;

  this._ids = ids;

  this._overlayDefaults = {
    show: {
      trigger: 'automatic',
      minZoom: 0.7,
      maxZoom: 5.0
    }
  };

  /**
   * Mapping overlay-id > overlay
   */
  this._overlays = {};

  /**
   * Mapping element-id > overlay container
   */
  this._overlayContainers = {};

  // root html element for all overlays
  this._overlayRoot = $('
')
                        .css({ position: 'absolute', width: 0, height: 0 })
                        .prependTo(canvas.getContainer());

  this._init(config);
}

Overlays.$inject = [ 'config.overlays', 'eventBus', 'canvas', 'elementRegistry' ];

module.exports = Overlays;

/**
 * Returns the overlay with the specified id or a list of overlays
 * for an element with a given type.
 *
 * @example
 *
 * // return the single overlay with the given id
 * overlays.get('some-id');
 *
 * // return all overlays for the shape
 * overlays.get({ element: someShape });
 *
 * // return all overlays on shape with type 'badge'
 * overlays.get({ element: someShape, type: 'badge' });
 *
 * // shape can also be specified as id
 * overlays.get({ element: 'element-id', type: 'badge' });
 *
 *
 * @param {Object} filter
 * @param {String} [filter.id]
 * @param {String|djs.model.Base} [filter.element]
 * @param {String} [filter.type]
 *
 * @return {Object|Array<Object>} the overlay(s)
 */
Overlays.prototype.get = function(filter) {

  if (_.isString(filter)) {
    filter = { id: filter };
  }

  if (filter.element) {
    var container = this._getOverlayContainer(filter.element, true);

    // return a list of overlays when filtering by element (+type)
    if (container) {
      return filter.type ? _.filter(container.overlays, { type: filter.type }) : _.clone(container.overlays);
    } else {
      return [];
    }
  } else
  if (filter.type) {
    return _.filter(this._overlays, { type: filter.type });
  } else {
    // return single element when filtering by id
    return filter.id ? this._overlays[filter.id] : null;
  }
};

/**
 * Adds a HTML overlay to an element.
 *
 * @param {String|djs.model.Base}   element   attach overlay to this shape
 * @param {String}                  [type]    optional type to assign to the overlay
 * @param {Object}                  overlay   the overlay configuration
 *
 * @param {String|DOMElement}       overlay.html                      html element to use as an overlay
 * @param {Object}                  [overlay.show]                    show configuration
 * @param {Number}                  overlay.show.minZoom              minimal zoom level to show the overlay
 * @param {Number}                  overlay.show.maxZoom              maximum zoom level to show the overlay
 * @param {String}                  [overlay.show.trigger=automatic]  automatic or manual (user triggers show)
 * @param {Object}                  overlay.show.position             where to attach the overlay
 * @param {Number}                  [overlay.show.position.left]      relative to element bbox left attachment
 * @param {Number}                  [overlay.show.position.top]       relative to element bbox top attachment
 * @param {Number}                  [overlay.show.position.bottom]    relative to element bbox bottom attachment
 * @param {Number}                  [overlay.show.position.right]     relative to element bbox right attachment
 *
 * @return {String}                 id that may be used to reference the overlay for update or removal
 */
Overlays.prototype.add = function(element, type, overlay) {

  if (_.isObject(type)) {
    overlay = type;
    type = null;
  }

  if (_.isString(element)) {
    element = this._elementRegistry.getById(element);
  }

  if (element.waypoints) {
    throw new Error('overlays for connections are not supported');
  }

  if (!overlay.position) {
    throw new Error('must specifiy overlay position');
  }

  if (!overlay.html) {
    throw new Error('must specifiy overlay html');
  }

  if (!element) {
    throw new Error('invalid element specified');
  }

  var id = this._ids.next();

  overlay = _.extend({}, this._overlayDefaults, overlay, {
    id: id,
    type: type,
    element: element,
    html: $(overlay.html)
  });

  this._addOverlay(overlay);

  return id;
};

/**
 * Remove an overlay with the given id or all overlays matching the given filter.
 *
 * @see Overlays#get for filter options.
 *
 * @param {String} [id]
 * @param {Object} [filter]
 */
Overlays.prototype.remove = function(filter) {

  var overlays = this.get(filter) || [];

  if (!_.isArray(overlays)) {
    overlays = [ overlays ];
  }

  var self = this;

  _.forEach(overlays, function(overlay) {

    var container = self._getOverlayContainer(overlay.element, true);

    if (overlay) {
      overlay.html.remove();
      overlay.htmlContainer.remove();

      delete overlay.htmlContainer;
      delete overlay.element;

      delete self._overlays[overlay.id];
    }

    if (container) {
      var idx = container.overlays.indexOf(overlay);
      if (idx !== -1) {
        container.overlays.splice(idx, 1);
      }
    }
  });

};

Overlays.prototype.show = function() {
  this._overlayRoot.show();
};

Overlays.prototype.hide = function() {
  this._overlayRoot.hide();
};

Overlays.prototype._updateOverlayContainer = function(container) {
  var element = container.element,
      html = container.html;

  // update container left,top according to the elements x,y coordinates
  // this ensures we can attach child elements relative to this container

  // TODO(nre): update according to element bbox (for connections)
  html.css({ left: element.x, top: element.y });
};

Overlays.prototype._updateOverlay = function(overlay) {

  var position = overlay.position,
      htmlContainer = overlay.htmlContainer,
      element = overlay.element;

  // update overlay html relative to shape because
  // it is already positioned on the element

  // update relative
  var left = position.left,
      top = position.top;

  if (position.right !== undefined) {
    left = position.right * -1 + element.width;
  }

  if (position.bottom !== undefined) {
    top = position.bottom * -1 + element.height;
  }

  htmlContainer.css({ left: left || 0, top: top || 0 });
};

Overlays.prototype._createOverlayContainer = function(element) {
  var html = $('
')
                .addClass('djs-overlays')
                .addClass('djs-overlays-' + element.id)
                .css({ position: 'absolute' });

  html.appendTo(this._overlayRoot);

  var container = {
    html: html,
    element: element,
    overlays: []
  };

  this._updateOverlayContainer(container);

  return container;
};

Overlays.prototype._updateRoot = function(viewbox) {
  var a = viewbox.scale || 1;
  var d = viewbox.scale || 1;
  var e = viewbox.x || 0;
  var f = viewbox.y || 0;

  var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';

  this._overlayRoot.css('transform', matrix);
};

Overlays.prototype._getOverlayContainer = function(element, raw) {
  var id = (element && element.id) || element;

  var container = this._overlayContainers[id];
  if (!container && !raw) {
    container = this._overlayContainers[id] = this._createOverlayContainer(element);
  }

  return container;
};

Overlays.prototype._addOverlay = function(overlay) {

  var id = overlay.id,
      element = overlay.element;

  var container = this._getOverlayContainer(element);

  var htmlContainer = $('
', {
    id: id,
    'class': 'djs-overlay'
  }).css({ position: 'absolute' }).append(overlay.html);

  if (overlay.type) {
    htmlContainer.addClass('djs-overlay-' + overlay.type);
  }

  overlay.htmlContainer = htmlContainer;

  container.overlays.push(overlay);
  container.html.append(htmlContainer);

  this._overlays[id] = overlay;

  this._updateOverlay(overlay);
};

Overlays.prototype._updateOverlayVisibilty = function(viewbox) {

  _.forEach(this._overlays, function(overlay) {
    if (overlay.show) {
      if (overlay.show.minZoom > viewbox.scale ||
          overlay.show.maxZoom < viewbox.scale) {
        overlay.htmlContainer.hide();
      } else {
        overlay.htmlContainer.show();
      }
    }
  });
};

Overlays.prototype._init = function(config) {

  var eventBus = this._eventBus;

  var self = this;

  // scroll/zoom integration

  var updateViewbox = function(viewbox) {
    self._updateRoot(viewbox);
    self._updateOverlayVisibilty(viewbox);

    self.show();
  };

  if (!config || config.deferUpdate !== false) {
    updateViewbox = _.debounce(updateViewbox, 300);
  }

  eventBus.on('canvas.viewbox.changed', function(event) {
    self.hide();
    updateViewbox(event.viewbox);
  });

  // remove integration

  eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
    var overlays = self.get({ element: e.element });

    _.forEach(overlays, function(o) {
      self.remove(o.id);
    });
  });

  // move integration

  eventBus.on([
    'commandStack.shape.move.executed',
    'commandStack.shape.move.reverted'
  ], function(e) {
    var element = e.context.shape;

    var container = self._getOverlayContainer(element, true);
    if (container) {
      self._updateOverlayContainer(container);
    }
  });

  // marker integration, simply add them on the overlays as classes, too.

  eventBus.on('element.marker.update', function(e) {
    var container = self._getOverlayContainer(e.element, true);
    if (container) {
      container.html[e.add ? 'addClass' : 'removeClass'](e.marker);
    }
  });
};<|endoftext|>