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

'use strict';

var __ = require('underscore');
var util = require('util');

var groupUtils = require('../group/groupUtils');
var permissionsUtils = require('../role/permissionsUtils');
var profile = require('../../../util/profile');
var resourceUtils = require('./resourceUtils');
var tagUtils = require('../tag/tagUtils');
var utils = require('../../../util/utils');

var $ = utils.getLocaleString;

exports.init = function (cli) {
  var log = cli.output;
  var withProgress = cli.interaction.withProgress.bind(cli.interaction);

  var resource = cli.category('resource')
    .description($('Commands to manage your resources'));

  resource.command('create [resource-group] [name] [resource-type] [location] [api-version]')
    .description($('Creates a resource in a resource group'))
    .usage('[options] <resource-group> <name> <resource-type> <location> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-l --location <location>', $('the location where we will create the resource'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-p --properties <properties>', $('a JSON-formatted string containing properties'))
    .option('-t --tags <tags>', $('Tags to set to the resource group. Can be mutliple. ' +
            'In the format of \'name=value\'. Name is required and value is optional. For example, -t tag1=value1;tag2'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, location, apiVersion, options, _) {
      resource.createResource(resourceGroup, name, resourceType, location, apiVersion, options.properties, options, _);
    });

  resource.command('set [resource-group] [name] [resource-type] [properties] [api-version]')
    .usage('[options] <resource-group> <name> <resource-type> <properties> <api-version>')
    .description($('Updates a resource in a resource group without any templates or parameters'))
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-p --properties <properties>', $('a JSON-formatted string containing properties'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-t --tags <tags>', $('Tags to set to the resource. Can be mutliple. ' +
      'In the format of \'name=value\'. Name is required and value is optional. For example, -t tag1=value1;tag2'))
    .option('--no-tags', $('remove all existing tags'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, properties, apiVersion, options, _) {
      resource.createResource(resourceGroup, name, resourceType, '', apiVersion, properties, options, _);
    });

  resource.createResource = function (resourceGroup, name, resourceType, location, apiVersion, propertiesParam, options, _) {
    if (!resourceGroup) {
      return cli.missingArgument('resourceGroup');
    } else if (!name) {
      return cli.missingArgument('name');
    } else if (!resourceType) {
      return cli.missingArgument('resourceType');
    } else if (!apiVersion) {
      return cli.missingArgument('apiVersion');
    }

    var subscription = profile.current.getSubscription(options.subscription);
    var client = subscription.createResourceClient('createResourceManagementClient');

    // Create group if it does not exist
    var group = withProgress($('Checking resource group'),
      function (log, _) {
        var g = groupUtils.getGroup(client, resourceGroup, _);
        if (!g) {
          g = client.resourceGroups.createOrUpdate(resourceGroup, { location: location }, _).resourceGroup;
        }
        return g;
      }, _);

    var identity = {
      resourceName: name,
      resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
      resourceProviderApiVersion: apiVersion,
      resourceType: resourceUtils.getResourceTypeName(resourceType),
      // TODO: parent should be optional in the API. temporary workaround.
      parentResourcePath: __.isString(options.parent) ? options.parent : ''
    };

    var resource = withProgress(util.format($('Getting resource %s'), name),
      function (log, _) {
        return groupUtils.getResource(client, resourceGroup, identity, _);
      }, _);

    resource = resource || {};
    var properties = {};
    if (propertiesParam) {
      properties = JSON.parse(propertiesParam);
    }

    var tags = {};
    tags = tagUtils.buildTagsParameter(tags, options);

    var message = util.format($('Creating resource %s'), name);
    var doneMessage = util.format($('Created resource %s'), name);
    if (resource) {
      message = util.format($('Updating resource %s'), name);
      doneMessage = util.format($('Resource %s is updated'), name);
    }

    var resourceLocation = location || resource.location || group.location;
    if (!resourceLocation){
      cli.missingArgument('location');
    }

    var newResource;
    cli.interaction.withProgress(util.format($('Creating resource %s'), name),
      function (log, _) {
        newResource = client.resources.createOrUpdate(resourceGroup,
          identity,
          {
            location: resourceLocation,
            resource: resource,
            properties: properties,
            resourceProviderApiVersion: apiVersion,
            tags: tags
          }, _).resource;
      }, _);

    log.info(doneMessage);
    log.data('');
    log.data($('Name:     '), name);
    log.data($('Type:     '), resourceType);
    log.data($('Location: '), resourceLocation);
    log.data($('Tags:     '), tagUtils.getTagsInfo(newResource.tags));
  };

  resource.command('list [resource-group]')
    .description($('Lists the resources'))
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('--details', $('show details such as permissions, etc.'))
    .option('-t --tags <tags>', $('Tag to use to filter to the resource group. Can only take 1 tag. ' +
        'In the format of "name=value". Name is required and value is optional. ' +
        'For example, -t tag1 or -t tag1=value1.'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Listing resources')));

      var resources;
      try {
        var parameters = {};
        if (options) {
          if (options.resourceType) {
            parameters.resourceType = options.resourceType;
          }
          if (options.tags) {
            tagUtils.populateQueryFilterWithTagInfo(options.tags, parameters);
          }
        }

        if (resourceGroup) {
          parameters.resourceGroupName = resourceGroup;
        }

        resources = client.resources.list(parameters, _).resources;
      } finally {
        progress.end();
      }

      if (options.details) {
        for (var i = 0; i < resources.length; i++) {
          var resourceInformation = getResourceInformation(resources[i].id);
          resources[i].permissions = client.resources.getPermissions(resourceGroup, {
            resourceName: resourceInformation.resourceName,
            resourceType: resourceUtils.getResourceTypeName(resourceInformation.resourceType),
            resourceProviderNamespace: resourceUtils.getProviderName(resourceInformation.resourceType),
            parentResourcePath: resourceInformation.parentResource ? resourceInformation.parentResource : '',
            resourceProviderApiVersion: '2014-04-01' //TODO: refresh/delete this when CSM fully integrates with PAS
          },
          _).permissions;
        }
      }

      if (resources.length === 0) {
        log.info($('No matched resources were found.'));
      } else {
        log.table(resources, function (row, item) {
          var resourceInformation = getResourceInformation(item.id);
          row.cell($('Name'), resourceInformation.resourceName || item.name );
          row.cell($('Resource Group'), resourceInformation.resourceGroup || '');
          row.cell($('Type'), resourceInformation.resourceType || item.type);
          row.cell($('Parent'), resourceInformation.parentResource ?  resourceInformation.parentResource : '');
          row.cell($('Location'), item.location);
          row.cell($('Tags'), tagUtils.getTagsInfo(item.tags));
          if (item.permissions) {
            var permissionDetails = permissionsUtils.getPermissionDetails(item.permissions);
            row.cell($('Actions'), permissionDetails.actions);
            row.cell($('NotActions'), permissionDetails.notActions);
          }
        });
      }
    });

  resource.command('show [resource-group] [name] [resource-type] [api-version]')
    .description($('Gets one resource within a resource group or subscription'))
    .usage('[options] <resource-group> <name> <resource-type> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, apiVersion, options, _) {
      if (!resourceGroup) {
        return cli.missingArgument('resourceGroup');
      } else if (!name) {
        return cli.missingArgument('name');
      } else if (!resourceType) {
        return cli.missingArgument('resourceType');
      } else if (!apiVersion) {
        return cli.missingArgument('apiVersion');
      }

      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Getting resource %s'), name));

      var resource;
      try {
        var identity = {
          resourceName: name,
          resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
          resourceProviderApiVersion: apiVersion,
          resourceType: resourceUtils.getResourceTypeName(resourceType),
          // TODO: parent should be optional in the API. temporary workaround.
          parentResourcePath: __.isString(options.parent) ? options.parent : ''
        };

        resource = client.resources.get(resourceGroup, identity, _).resource;
        resource.permissions = client.resources.getPermissions(resourceGroup, identity, _).permissions;
      } finally {
        progress.end();
      }

      cli.interaction.formatOutput(resource, function (outputData) {
        var resourceInformation = getResourceInformation(outputData.id);
        log.data($('Name:     '), resourceInformation.resourceName || outputData.name);
        log.data($('Type:     '), resourceInformation.resourceType || outputData.type);
        log.data($('Parent:   '), resourceInformation.parentResource || '');
        log.data($('Location: '), outputData.location);
        log.data($('Tags:     '), tagUtils.getTagsInfo(outputData.tags));
        log.data('');
        log.data($('Properties:'));
        cli.interaction.logEachData($('Property'), outputData.properties);
        log.data('');
        var permissionDetails = permissionsUtils.getPermissionDetails(resource.permissions);
        log.data($('Permissions:'));
        log.data($('  Actions: ') + permissionDetails.actions);
        log.data($('  NotActions: ') + permissionDetails.notActions);
      });
    });

  resource.command('delete [resource-group] [name] [resource-type] [api-version]')
    .description($('Deletes a resource in a resource group'))
    .usage('[options] <resource-group> <name> <resource-type> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-q, --quiet', $('quiet mode (do not ask for delete confirmation)'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, apiVersion, options, _) {
      if (!resourceGroup) {
        return cli.missingArgument('resourceGroup');
      } else if (!name) {
        return cli.missingArgument('name');
      } else if (!resourceType) {
        return cli.missingArgument('resourceType');
      } else if (!apiVersion) {
        return cli.missingArgument('apiVersion');
      }

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

      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Deleting resource %s'), name));
      try {
        var identity = {
          resourceName: name,
          resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
          resourceProviderApiVersion: apiVersion,
          resourceType: resourceUtils.getResourceTypeName(resourceType),
          // TODO: parent should be optional in the API. temporary workaround.
          parentResourcePath: __.isString(options.parent) ? options.parent : ''
        };

        if (!resourceExists(client, resourceGroup, identity, _)) {
          throw new Error($('Resource does not exist'));
        }

        client.resources.delete(resourceGroup, identity, _);
      } finally {
        progress.end();
      }
    });
};

function resourceExists(client, resourceGroup, identity, _) {
  try {
    client.resources.get(resourceGroup, identity, _);
    return true;
  } catch (e) {
    return false;
  }
}

function getResourceInformation(resourceIDFromServer) {
  function removeEmptyElement(existing) {
    var newArray = [];
    for (var i = 0; i < existing.length; i++) {
      if (existing[i]) {
        newArray.push(existing[i]);
      }
    }
    return newArray;
  }

  if (!resourceIDFromServer){
    return {};
  }

  var tokens = resourceIDFromServer.split('/');
  tokens = removeEmptyElement(tokens);
  if (tokens.length < 8){
    throw new Error('invalid resource id from server');
  }
  var resourceGroupName = tokens[3];
  var resourceName = tokens[tokens.length - 1];

  var resourceTypeBuilder = [];
  resourceTypeBuilder.push(tokens[5]);

  // Extract out the 'parent resource' and 'full resource type'
  // for id like: subscriptions/abc123/resourceGroups/group1/providers/Microsoft.Test/servers/r12345sql/db/r45678db,
  // we will extract out parent resource: 'servers/r12345sql'.
  // from id like: subscriptions/abc123/resourceGroups/group1/providers/Microsoft.Test/db/r45678db,
  // parent resource does not exist.
  var parentResourceBuilder = [];
  for (var i = 6; i <= tokens.length - 3; i++){
    parentResourceBuilder.push(tokens[i]);
    //from 'resourceType/resourcName/<same pattern...>', skip the "resourceName" and keep the type
    if (i%2 === 0){
      resourceTypeBuilder.push(tokens[i]);
    }
  }
  resourceTypeBuilder.push(tokens[tokens.length - 2]);

  var parentResource;
  if (parentResourceBuilder.length > 0){
    parentResource = parentResourceBuilder.join('/');
  }

  var resourceType;
  if (resourceTypeBuilder.length > 0){
    resourceType = resourceTypeBuilder.join('/');
  }

  return {
    'resourceName': resourceName,
    'resourceGroup' : resourceGroupName,
    'resourceType' : resourceType,
    'parentResource' : parentResource
  };
}
<commit_msg>provided resourcegroup name correctly so resource list command can execute properly<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var __ = require('underscore');
var util = require('util');

var groupUtils = require('../group/groupUtils');
var permissionsUtils = require('../role/permissionsUtils');
var profile = require('../../../util/profile');
var resourceUtils = require('./resourceUtils');
var tagUtils = require('../tag/tagUtils');
var utils = require('../../../util/utils');

var $ = utils.getLocaleString;

exports.init = function (cli) {
  var log = cli.output;
  var withProgress = cli.interaction.withProgress.bind(cli.interaction);

  var resource = cli.category('resource')
    .description($('Commands to manage your resources'));

  resource.command('create [resource-group] [name] [resource-type] [location] [api-version]')
    .description($('Creates a resource in a resource group'))
    .usage('[options] <resource-group> <name> <resource-type> <location> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-l --location <location>', $('the location where we will create the resource'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-p --properties <properties>', $('a JSON-formatted string containing properties'))
    .option('-t --tags <tags>', $('Tags to set to the resource group. Can be mutliple. ' +
            'In the format of \'name=value\'. Name is required and value is optional. For example, -t tag1=value1;tag2'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, location, apiVersion, options, _) {
      resource.createResource(resourceGroup, name, resourceType, location, apiVersion, options.properties, options, _);
    });

  resource.command('set [resource-group] [name] [resource-type] [properties] [api-version]')
    .usage('[options] <resource-group> <name> <resource-type> <properties> <api-version>')
    .description($('Updates a resource in a resource group without any templates or parameters'))
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-p --properties <properties>', $('a JSON-formatted string containing properties'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-t --tags <tags>', $('Tags to set to the resource. Can be mutliple. ' +
      'In the format of \'name=value\'. Name is required and value is optional. For example, -t tag1=value1;tag2'))
    .option('--no-tags', $('remove all existing tags'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, properties, apiVersion, options, _) {
      resource.createResource(resourceGroup, name, resourceType, '', apiVersion, properties, options, _);
    });

  resource.createResource = function (resourceGroup, name, resourceType, location, apiVersion, propertiesParam, options, _) {
    if (!resourceGroup) {
      return cli.missingArgument('resourceGroup');
    } else if (!name) {
      return cli.missingArgument('name');
    } else if (!resourceType) {
      return cli.missingArgument('resourceType');
    } else if (!apiVersion) {
      return cli.missingArgument('apiVersion');
    }

    var subscription = profile.current.getSubscription(options.subscription);
    var client = subscription.createResourceClient('createResourceManagementClient');

    // Create group if it does not exist
    var group = withProgress($('Checking resource group'),
      function (log, _) {
        var g = groupUtils.getGroup(client, resourceGroup, _);
        if (!g) {
          g = client.resourceGroups.createOrUpdate(resourceGroup, { location: location }, _).resourceGroup;
        }
        return g;
      }, _);

    var identity = {
      resourceName: name,
      resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
      resourceProviderApiVersion: apiVersion,
      resourceType: resourceUtils.getResourceTypeName(resourceType),
      // TODO: parent should be optional in the API. temporary workaround.
      parentResourcePath: __.isString(options.parent) ? options.parent : ''
    };

    var resource = withProgress(util.format($('Getting resource %s'), name),
      function (log, _) {
        return groupUtils.getResource(client, resourceGroup, identity, _);
      }, _);

    resource = resource || {};
    var properties = {};
    if (propertiesParam) {
      properties = JSON.parse(propertiesParam);
    }

    var tags = {};
    tags = tagUtils.buildTagsParameter(tags, options);

    var message = util.format($('Creating resource %s'), name);
    var doneMessage = util.format($('Created resource %s'), name);
    if (resource) {
      message = util.format($('Updating resource %s'), name);
      doneMessage = util.format($('Resource %s is updated'), name);
    }

    var resourceLocation = location || resource.location || group.location;
    if (!resourceLocation){
      cli.missingArgument('location');
    }

    var newResource;
    cli.interaction.withProgress(util.format($('Creating resource %s'), name),
      function (log, _) {
        newResource = client.resources.createOrUpdate(resourceGroup,
          identity,
          {
            location: resourceLocation,
            resource: resource,
            properties: properties,
            resourceProviderApiVersion: apiVersion,
            tags: tags
          }, _).resource;
      }, _);

    log.info(doneMessage);
    log.data('');
    log.data($('Name:     '), name);
    log.data($('Type:     '), resourceType);
    log.data($('Location: '), resourceLocation);
    log.data($('Tags:     '), tagUtils.getTagsInfo(newResource.tags));
  };

  resource.command('list [resource-group]')
    .description($('Lists the resources'))
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('--details', $('show details such as permissions, etc.'))
    .option('-t --tags <tags>', $('Tag to use to filter to the resource group. Can only take 1 tag. ' +
        'In the format of "name=value". Name is required and value is optional. ' +
        'For example, -t tag1 or -t tag1=value1.'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Listing resources')));

      var resources;
      try {
        var parameters = {};
        if (options) {
          if (options.resourceType) {
            parameters.resourceType = options.resourceType;
          }
          if (options.tags) {
            tagUtils.populateQueryFilterWithTagInfo(options.tags, parameters);
          }
        }

        if (resourceGroup) {
          parameters.resourceGroupName = resourceGroup;
        }

        resources = client.resources.list(parameters, _).resources;
      } finally {
        progress.end();
      }

      if (options.details) {
        for (var i = 0; i < resources.length; i++) {
          var resourceInformation = getResourceInformation(resources[i].id);
          resources[i].permissions = client.resources.getPermissions(resourceInformation.resourceGroup, {
            resourceName: resourceInformation.resourceName,
            resourceType: resourceUtils.getResourceTypeName(resourceInformation.resourceType),
            resourceProviderNamespace: resourceUtils.getProviderName(resourceInformation.resourceType),
            parentResourcePath: resourceInformation.parentResource ? resourceInformation.parentResource : '',
            resourceProviderApiVersion: '2014-04-01' //TODO: refresh/delete this when CSM fully integrates with PAS
          },
          _).permissions;
        }
      }

      if (resources.length === 0) {
        log.info($('No matched resources were found.'));
      } else {
        log.table(resources, function (row, item) {
          var resourceInformation = getResourceInformation(item.id);
          row.cell($('Name'), resourceInformation.resourceName || item.name );
          row.cell($('Resource Group'), resourceInformation.resourceGroup || '');
          row.cell($('Type'), resourceInformation.resourceType || item.type);
          row.cell($('Parent'), resourceInformation.parentResource ?  resourceInformation.parentResource : '');
          row.cell($('Location'), item.location);
          row.cell($('Tags'), tagUtils.getTagsInfo(item.tags));
          if (item.permissions) {
            var permissionDetails = permissionsUtils.getPermissionDetails(item.permissions);
            row.cell($('Actions'), permissionDetails.actions);
            row.cell($('NotActions'), permissionDetails.notActions);
          }
        });
      }
    });

  resource.command('show [resource-group] [name] [resource-type] [api-version]')
    .description($('Gets one resource within a resource group or subscription'))
    .usage('[options] <resource-group> <name> <resource-type> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, apiVersion, options, _) {
      if (!resourceGroup) {
        return cli.missingArgument('resourceGroup');
      } else if (!name) {
        return cli.missingArgument('name');
      } else if (!resourceType) {
        return cli.missingArgument('resourceType');
      } else if (!apiVersion) {
        return cli.missingArgument('apiVersion');
      }

      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Getting resource %s'), name));

      var resource;
      try {
        var identity = {
          resourceName: name,
          resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
          resourceProviderApiVersion: apiVersion,
          resourceType: resourceUtils.getResourceTypeName(resourceType),
          // TODO: parent should be optional in the API. temporary workaround.
          parentResourcePath: __.isString(options.parent) ? options.parent : ''
        };

        resource = client.resources.get(resourceGroup, identity, _).resource;
        resource.permissions = client.resources.getPermissions(resourceGroup, identity, _).permissions;
      } finally {
        progress.end();
      }

      cli.interaction.formatOutput(resource, function (outputData) {
        var resourceInformation = getResourceInformation(outputData.id);
        log.data($('Name:     '), resourceInformation.resourceName || outputData.name);
        log.data($('Type:     '), resourceInformation.resourceType || outputData.type);
        log.data($('Parent:   '), resourceInformation.parentResource || '');
        log.data($('Location: '), outputData.location);
        log.data($('Tags:     '), tagUtils.getTagsInfo(outputData.tags));
        log.data('');
        log.data($('Properties:'));
        cli.interaction.logEachData($('Property'), outputData.properties);
        log.data('');
        var permissionDetails = permissionsUtils.getPermissionDetails(resource.permissions);
        log.data($('Permissions:'));
        log.data($('  Actions: ') + permissionDetails.actions);
        log.data($('  NotActions: ') + permissionDetails.notActions);
      });
    });

  resource.command('delete [resource-group] [name] [resource-type] [api-version]')
    .description($('Deletes a resource in a resource group'))
    .usage('[options] <resource-group> <name> <resource-type> <api-version>')
    .option('-g --resource-group <resource-group>', $('the resource group name'))
    .option('-n --name <name>', $('the resource name'))
    .option('-r --resource-type <resource-type>', $('the resource type'))
    .option('-o --api-version <api-version>', $('the API version of the resource provider'))
    .option('--parent <parent>', $('the name of the parent resource (if needed), in path/path/path format'))
    .option('-q, --quiet', $('quiet mode (do not ask for delete confirmation)'))
    .option('--subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, resourceType, apiVersion, options, _) {
      if (!resourceGroup) {
        return cli.missingArgument('resourceGroup');
      } else if (!name) {
        return cli.missingArgument('name');
      } else if (!resourceType) {
        return cli.missingArgument('resourceType');
      } else if (!apiVersion) {
        return cli.missingArgument('apiVersion');
      }

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

      var subscription = profile.current.getSubscription(options.subscription);
      var client = subscription.createResourceClient('createResourceManagementClient');
      var progress = cli.interaction.progress(util.format($('Deleting resource %s'), name));
      try {
        var identity = {
          resourceName: name,
          resourceProviderNamespace: resourceUtils.getProviderName(resourceType),
          resourceProviderApiVersion: apiVersion,
          resourceType: resourceUtils.getResourceTypeName(resourceType),
          // TODO: parent should be optional in the API. temporary workaround.
          parentResourcePath: __.isString(options.parent) ? options.parent : ''
        };

        if (!resourceExists(client, resourceGroup, identity, _)) {
          throw new Error($('Resource does not exist'));
        }

        client.resources.delete(resourceGroup, identity, _);
      } finally {
        progress.end();
      }
    });
};

function resourceExists(client, resourceGroup, identity, _) {
  try {
    client.resources.get(resourceGroup, identity, _);
    return true;
  } catch (e) {
    return false;
  }
}

function getResourceInformation(resourceIDFromServer) {
  function removeEmptyElement(existing) {
    var newArray = [];
    for (var i = 0; i < existing.length; i++) {
      if (existing[i]) {
        newArray.push(existing[i]);
      }
    }
    return newArray;
  }

  if (!resourceIDFromServer){
    return {};
  }

  var tokens = resourceIDFromServer.split('/');
  tokens = removeEmptyElement(tokens);
  if (tokens.length < 8){
    throw new Error('invalid resource id from server');
  }
  var resourceGroupName = tokens[3];
  var resourceName = tokens[tokens.length - 1];

  var resourceTypeBuilder = [];
  resourceTypeBuilder.push(tokens[5]);

  // Extract out the 'parent resource' and 'full resource type'
  // for id like: subscriptions/abc123/resourceGroups/group1/providers/Microsoft.Test/servers/r12345sql/db/r45678db,
  // we will extract out parent resource: 'servers/r12345sql'.
  // from id like: subscriptions/abc123/resourceGroups/group1/providers/Microsoft.Test/db/r45678db,
  // parent resource does not exist.
  var parentResourceBuilder = [];
  for (var i = 6; i <= tokens.length - 3; i++){
    parentResourceBuilder.push(tokens[i]);
    //from 'resourceType/resourcName/<same pattern...>', skip the "resourceName" and keep the type
    if (i%2 === 0){
      resourceTypeBuilder.push(tokens[i]);
    }
  }
  resourceTypeBuilder.push(tokens[tokens.length - 2]);

  var parentResource;
  if (parentResourceBuilder.length > 0){
    parentResource = parentResourceBuilder.join('/');
  }

  var resourceType;
  if (resourceTypeBuilder.length > 0){
    resourceType = resourceTypeBuilder.join('/');
  }

  return {
    'resourceName': resourceName,
    'resourceGroup' : resourceGroupName,
    'resourceType' : resourceType,
    'parentResource' : parentResource
  };
}
<|endoftext|>


<commit_before>import React, { Component, PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Label, Button } from 'react-bootstrap'
import { VelocityTransitionGroup } from 'velocity-react'
import { connect } from 'react-redux'

import Icon from '../narrative/icon'

import { setMainPanelContent, setViewedRoute } from '../../actions/ui'
import { findRoutes, findRoute } from '../../actions/api'
import { routeComparator } from '../../util/itinerary'

function operatorForRoute (operators, route) {
  return operators.find(o =>
    route.agency && o.id.toLowerCase() === route.agency.id.split(':')[0].toLowerCase())
}

function operatorIndexForRoute (operators, route) {
  if (!route.agency) return 0
  const index = operators.findIndex(o =>
    o.id.toLowerCase() === route.agency.id.split(':')[0].toLowerCase())
  if (index !== -1 && typeof operators[index].order !== 'undefined') return operators[index].order
  else return 0
}

class RouteViewer extends Component {
  static propTypes = {
    hideBackButton: PropTypes.bool,
    routes: PropTypes.object
  }

  _backClicked = () => this.props.setMainPanelContent(null)

  componentWillMount () {
    this.props.findRoutes()
  }

  componentDidMount () {
    // this.props.findRoute({ routeId: 'TriMet:1' })
    // this.props.setViewedRoute({ routeId: 'TriMet:1' })
  }

  render () {
    const {
      findRoute,
      hideBackButton,
      languageConfig,
      operators,
      routes,
      setViewedRoute,
      viewedRoute
    } = this.props
    const sortedRoutes = routes ? Object.values(routes).sort(routeComparator) : []
    const agencySortedRoutes = operators.length > 0
      ? sortedRoutes.sort((a, b) => {
        return operatorIndexForRoute(operators, a) - operatorIndexForRoute(operators, b)
      })
      : sortedRoutes
    return (

        {/* Header Block */}

          {/* Back button */}
          {!hideBackButton && (

              <Button
                bsSize='small'
                onClick={this._backClicked}
              ><Icon type='arrow-left' />Back</Button>

          )}

          {/* Header Text */}

            {languageConfig.routeViewer || 'Route Viewer'}

            {languageConfig.routeViewerDetails}

          {agencySortedRoutes
            .map(route => {
              // Find operator based on agency_id (extracted from OTP route ID).
              // TODO: re-implement multi-agency logos for route viewer.
              // const operator = operatorForRoute(operators, route) || {}
              return (
                <RouteRow
                  findRoute={findRoute}
                  isActive={viewedRoute && viewedRoute.routeId === route.id}
                  key={route.id
                  /* operator={operator */}
                  route={route}
                  setViewedRoute={setViewedRoute}
                />
              )
            })
          }

    )
  }
}

class RouteRow extends PureComponent {
  _onClick = () => {
    const { findRoute, isActive, route, setViewedRoute } = this.props
    if (isActive) {
      // Deselect current route if active.
      setViewedRoute({ routeId: null })
    } else {
      // Otherwise, set active and fetch route patterns.
      findRoute({ routeId: route.id })
      setViewedRoute({ routeId: route.id })
    }
  }

  render () {
    const {isActive, route, operator} = this.props
    const {defaultRouteColor, defaultRouteTextColor, longNameSplitter} = operator || {}
    const color = `#${defaultRouteTextColor || route.textColor || '000000'}`
    const backgroundColor = `#${defaultRouteColor || route.color || 'ffffff'}`
    const nameParts = route.longName.split(longNameSplitter)
    const longName = (longNameSplitter && route.longName && nameParts.length > 1)
      ? nameParts[1]
      : route.longName
    return (

        <Button className='clear-button-formatting' style={{ padding: 8, width: '100%' }}
          onClick={this._onClick}
        >

            {// TODO: re-implement multi-agency logos for route viewer.
              // Currently, the agency object is not nested within the get all
              // routes endpoint and causing this to only display operators for
              // the selected route.
              // operator &&
            }

              {route.shortName} {longName}

        </Button>
        <VelocityTransitionGroup enter={{animation: 'slideDown'}} leave={{animation: 'slideUp'}}>
          {isActive && (

              {route.url
                ? Route Details
                : 'No route URL provided.'
              }

          )}
        </VelocityTransitionGroup>

    )
  }
}
// connect to redux store

const mapStateToProps = (state, ownProps) => {
  return {
    operators: state.otp.config.operators,
    routes: state.otp.transitIndex.routes,
    viewedRoute: state.otp.ui.viewedRoute,
    languageConfig: state.otp.config.language
  }
}

const mapDispatchToProps = {
  findRoute,
  findRoutes,
  setMainPanelContent,
  setViewedRoute
}

export default connect(mapStateToProps, mapDispatchToProps)(RouteViewer)
<commit_msg>refactor(route-viewer): comment out unused function<commit_after>import React, { Component, PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Label, Button } from 'react-bootstrap'
import { VelocityTransitionGroup } from 'velocity-react'
import { connect } from 'react-redux'

import Icon from '../narrative/icon'

import { setMainPanelContent, setViewedRoute } from '../../actions/ui'
import { findRoutes, findRoute } from '../../actions/api'
import { routeComparator } from '../../util/itinerary'

// function operatorForRoute (operators, route) {
//   return operators.find(o =>
//     route.agency && o.id.toLowerCase() === route.agency.id.split(':')[0].toLowerCase())
// }

function operatorIndexForRoute (operators, route) {
  if (!route.agency) return 0
  const index = operators.findIndex(o =>
    o.id.toLowerCase() === route.agency.id.split(':')[0].toLowerCase())
  if (index !== -1 && typeof operators[index].order !== 'undefined') return operators[index].order
  else return 0
}

class RouteViewer extends Component {
  static propTypes = {
    hideBackButton: PropTypes.bool,
    routes: PropTypes.object
  }

  _backClicked = () => this.props.setMainPanelContent(null)

  componentWillMount () {
    this.props.findRoutes()
  }

  componentDidMount () {
    // this.props.findRoute({ routeId: 'TriMet:1' })
    // this.props.setViewedRoute({ routeId: 'TriMet:1' })
  }

  render () {
    const {
      findRoute,
      hideBackButton,
      languageConfig,
      operators,
      routes,
      setViewedRoute,
      viewedRoute
    } = this.props
    const sortedRoutes = routes ? Object.values(routes).sort(routeComparator) : []
    const agencySortedRoutes = operators.length > 0
      ? sortedRoutes.sort((a, b) => {
        return operatorIndexForRoute(operators, a) - operatorIndexForRoute(operators, b)
      })
      : sortedRoutes
    return (

        {/* Header Block */}

          {/* Back button */}
          {!hideBackButton && (

              <Button
                bsSize='small'
                onClick={this._backClicked}
              ><Icon type='arrow-left' />Back</Button>

          )}

          {/* Header Text */}

            {languageConfig.routeViewer || 'Route Viewer'}

            {languageConfig.routeViewerDetails}

          {agencySortedRoutes
            .map(route => {
              // Find operator based on agency_id (extracted from OTP route ID).
              // TODO: re-implement multi-agency logos for route viewer.
              // const operator = operatorForRoute(operators, route) || {}
              return (
                <RouteRow
                  findRoute={findRoute}
                  isActive={viewedRoute && viewedRoute.routeId === route.id}
                  key={route.id
                  /* operator={operator */}
                  route={route}
                  setViewedRoute={setViewedRoute}
                />
              )
            })
          }

    )
  }
}

class RouteRow extends PureComponent {
  _onClick = () => {
    const { findRoute, isActive, route, setViewedRoute } = this.props
    if (isActive) {
      // Deselect current route if active.
      setViewedRoute({ routeId: null })
    } else {
      // Otherwise, set active and fetch route patterns.
      findRoute({ routeId: route.id })
      setViewedRoute({ routeId: route.id })
    }
  }

  render () {
    const {isActive, route, operator} = this.props
    const {defaultRouteColor, defaultRouteTextColor, longNameSplitter} = operator || {}
    const color = `#${defaultRouteTextColor || route.textColor || '000000'}`
    const backgroundColor = `#${defaultRouteColor || route.color || 'ffffff'}`
    const nameParts = route.longName.split(longNameSplitter)
    const longName = (longNameSplitter && route.longName && nameParts.length > 1)
      ? nameParts[1]
      : route.longName
    return (

        <Button className='clear-button-formatting' style={{ padding: 8, width: '100%' }}
          onClick={this._onClick}
        >

            {// TODO: re-implement multi-agency logos for route viewer.
              // Currently, the agency object is not nested within the get all
              // routes endpoint and causing this to only display operators for
              // the selected route.
              // operator &&
            }

              {route.shortName} {longName}

        </Button>
        <VelocityTransitionGroup enter={{animation: 'slideDown'}} leave={{animation: 'slideUp'}}>
          {isActive && (

              {route.url
                ? Route Details
                : 'No route URL provided.'
              }

          )}
        </VelocityTransitionGroup>

    )
  }
}
// connect to redux store

const mapStateToProps = (state, ownProps) => {
  return {
    operators: state.otp.config.operators,
    routes: state.otp.transitIndex.routes,
    viewedRoute: state.otp.ui.viewedRoute,
    languageConfig: state.otp.config.language
  }
}

const mapDispatchToProps = {
  findRoute,
  findRoutes,
  setMainPanelContent,
  setViewedRoute
}

export default connect(mapStateToProps, mapDispatchToProps)(RouteViewer)
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

// Public node modules.
const _ = require('lodash');
const Grant = require('grant-koa');

/**
 * Grant hook
 */

module.exports = function (strapi) {
  const hook = {

    /**
     * Default options
     */

    defaults: {
      grant: {}
    },

    /**
     * Initialize the hook
     */

    initialize: function (cb) {
      _.defaultsDeep(strapi.api.user.config.grant, {
        server: {
          protocol: strapi.config.ssl ? 'https' : 'http',
          host: strapi.config.host + ':' + strapi.config.port
        }
      });
      const grant = new Grant(strapi.api.user.config.grant);

      strapi.app.use(strapi.middlewares.mount(grant));

      cb();
    }
  };

  return hook;
};
<commit_msg>Do not load Grant middleware without user API<commit_after>'use strict';

/**
 * Module dependencies
 */

// Public node modules.
const _ = require('lodash');
const Grant = require('grant-koa');

/**
 * Grant hook
 */

module.exports = function (strapi) {
  const hook = {

    /**
     * Default options
     */

    defaults: {
      grant: {}
    },

    /**
     * Initialize the hook
     */

    initialize: function (cb) {
      if (_.isPlainObject(strapi.api.user)) {
        _.defaultsDeep(strapi.api.user.config.grant, {
          server: {
            protocol: strapi.config.ssl ? 'https' : 'http',
            host: strapi.config.host + ':' + strapi.config.port
          }
        });
        const grant = new Grant(strapi.api.user.config.grant);
        strapi.app.use(strapi.middlewares.mount(grant));
      }

      cb();
    }
  };

  return hook;
};
<|endoftext|>


<commit_before>/*global define*/
define([
	"esri/layers/ArcGISTiledMapServiceLayer",
	"esri/layers/ArcGISDynamicMapServiceLayer"
], function (ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer) {

	(function () {
		if (!document.createElement("div").dataset) {
			console.error("The dataset property is not supported by this browser.");
		}
	}());

	function createLayerList(map, layerDefinitions) {
		/** Creates the layer options div.
		* @param {esri/layers/Layer} layer
		* @returns {HTMLDivElement}
		*/
		function createLayerOptions(layer) {

			/*

				opacity <input type="range" min="0" max="1" step="0.1" value="1" />

				*/
			var div = document.createElement("div");
			div.classList.add("layer-options");
			div.classList.add("well");
			var label = document.createElement("label");
			label.textContent = "Opacity";
			var slider = document.createElement("input");
			slider.type = "range";
			slider.min = 0;
			slider.max = 1;
			slider.step = 0.1;
			slider.value = layer.opacity;
			div.appendChild(label);
			div.appendChild(slider);

			slider.onchange = function () {
				layer.setOpacity(slider.value);
			};

			return div;
		}

		/**
			* Creates the link to toggle the layer options div's visiblitly.
			* @param {HTMLDivElement} optionsDiv - The div that will have it's visibility controlled by this link.
			* @returns {HTMLAnchorElement}
			*/
		function createOptionsToggleLink(optionsDiv) {
			var link, span;
			if (!optionsDiv) {
				throw new TypeError("Options div was not provided.");
			}
			link = document.createElement("a");
			link.href = "#";
			span = document.createElement("span");
			span.classList.add("glyphicon");
			span.classList.add("glyphicon-collapse-up");
			link.appendChild(span);

			link.onclick = function () {
				if (span.classList.contains("glyphicon-collapse-up")) {
					span.classList.remove("glyphicon-collapse-up");
					span.classList.add("glyphicon-collapse-down");
					optionsDiv.classList.add("hidden");
				} else {
					span.classList.remove("glyphicon-collapse-down");
					span.classList.add("glyphicon-collapse-up");
					optionsDiv.classList.remove("hidden");
				}
				return false;
			};

			return link;
		}

		/** Applies the `checked` value of the current checkbox
			* to nested checkboxes.
			* @param {Event} e
			*/
		function checkNestedCheckboxes(e) {
			// Get the checkbox that triggered the event.
			// li > label > input[type='checkbox']
			var currentCheckbox = e.currentTarget;
			// Get the list item that hosts the checkbox.
			var listItem = currentCheckbox.parentElement.parentElement;
			// Get a NodeList of child checkboxes.
			// li > ul > li input[type='checkbox']
			var subCheckboxes = listItem.querySelectorAll("ul > li input[type='checkbox']");

			for (var i = 0, l = subCheckboxes.length; i < l; i += 1) {
				subCheckboxes[i].checked = currentCheckbox.checked;
			}

		}

		function createListItem(layerInfos, id) {
			var layerInfo = layerInfos[id], li, ul, hasSublayers;
			hasSublayers = layerInfo.subLayerIds && layerInfo.subLayerIds.length;
			// Create the output list item.
			li = document.createElement("li");
			// Add class for bootstrap styling.
			li.classList.add("list-group-item");
			// Add sublayer lists.
			// Set data-has-sublayers.
			if (hasSublayers) {
				ul = document.createElement("ul");
				// Add class for bootstrap styling.
				ul.classList.add("list-group");
				// For each child layer id, make a recursive call to this function.
				layerInfo.subLayerIds.forEach(function (layerId) {
					var subli = createListItem(layerInfos, layerId);
					ul.appendChild(subli);
				});
			}
			var label = document.createElement("label");
			var checkbox = document.createElement("input");
			checkbox.type = "checkbox";
			checkbox.checked = layerInfo.defaultVisibility;
			//checkbox.dataset.layerId = id;
			checkbox.value = id;
			checkbox.dataset.subLayerIds = layerInfo.subLayerIds ? layerInfo.subLayerIds.join(",") : "";
			checkbox.dataset.parentLayerId = layerInfo.parentLayerId === -1 ? "" : layerInfo.parentLayerId;
			if (hasSublayers) {
				checkbox.onchange = checkNestedCheckboxes;
			}
			label.appendChild(checkbox);
			// label.textContent = layerInfo.name;
			var labelText = document.createElement("span");
			labelText.textContent = layerInfo.name;
			label.appendChild(labelText);
			li.appendChild(label);
			if (ul) {
				li.appendChild(ul);
			}
			return li;
		}

		/**
			* Creates a div with sublayer list.
			* @param {esri/layers/Layer} layer
			* @returns {HTMLDivElement}
			*/
		function createSublayerDiv(layer) {
			var div = null, ul, applyButton;

			if (layer.setVisibleLayers && layer.layerInfos && layer.layerInfos.length > 1) {
				div = document.createElement("div");
				div.setAttribute("class", "well sublayer-list");
				ul = document.createElement("ul");
				ul.classList.add("list-group");

				layer.layerInfos.forEach(function (layerInfo) {
					/*jshint eqnull:true*/
					if (layerInfo.parentLayerId < 0) {
						ul.appendChild(createListItem(layer.layerInfos, layerInfo.id));
					}
					/*jshint eqnull:false*/
				});
				div.appendChild(ul);
				applyButton = document.createElement("button");
				applyButton.type = "button";
				applyButton.appendChild(document.createTextNode("Apply"));
				applyButton.classList.add("btn");
				applyButton.classList.add("btn-primary");
				div.appendChild(applyButton);

				applyButton.onclick = function () {
					// Select all checked checkboxes without a sublayer ID.
					////var checkboxes = ul.querySelectorAll("input[data-sub-layer-ids='']:checked");
					var checkboxes = ul.querySelectorAll("input[type='checkbox']:checked");
					// Get the sublayer IDs.
					var sublayers = [];
					for (var i = 0, l = checkboxes.length; i < l; i += 1) {
						sublayers.push(parseInt(checkboxes[i].value, 10));
					}
					// If the list of sublayer IDs is empty, add -1.
					// This is how you indicate "no sublayers".
					if (sublayers.length < 1) {
						sublayers.push(-1);
					}
					layer.setVisibleLayers(sublayers);
				};
			}

			return div;
		}

		/** Toggles a layers visibility.
			* The first time the checkbox is checked, the corresponding layer will be created and added to the map.
			* @param {Event} e
			* @this {HTMLInputElement} - The clicked checkbox.
			*/
		function toggleLayer(e) {
			var checkbox, layerId, layer, listItem, checkboxLabel, progress;
			checkbox = e.currentTarget;
			checkboxLabel = checkbox.parentElement;
			// Get the li that contains the checkbox.
			listItem = checkboxLabel.parentElement;

			layerId = checkbox.dataset.layerId;
			layer = map.getLayer(layerId);
			if (layer) {
				if (checkbox.checked) {
					layer.show();
				} else {
					layer.hide();
				}
			} else {
				if (checkbox.checked) {
					// Create the layer and add it to the map.

					progress = document.createElement("progress");
					checkbox.disabled = true;
					listItem.appendChild(progress);

					if (checkbox.dataset.layerType === "ArcGISTiledMapServiceLayer") {
						layer = new ArcGISTiledMapServiceLayer(checkbox.dataset.url, {
							id: checkbox.dataset.layerId
						});
					} else if (checkbox.dataset.layerType === "ArcGISDynamicMapServiceLayer") {
						layer = new ArcGISDynamicMapServiceLayer(checkbox.dataset.url, {
							id: checkbox.dataset.layerId
						});
					}

					if (layer) {
						map.addLayer(layer);
						layer.on("load", function () {
							checkbox.disabled = false;
							listItem.removeChild(progress);
							// Add the layer options section.
							var optionsDiv, link, sublayerLabel, sublayerDiv;
							optionsDiv = createLayerOptions(layer);
							link = createOptionsToggleLink(optionsDiv);
							checkboxLabel.appendChild(document.createTextNode(" "));
							checkboxLabel.appendChild(link);
							sublayerDiv = createSublayerDiv(layer);
							if (sublayerDiv) {
								sublayerLabel = document.createElement("label");
								sublayerLabel.innerText = "Sublayers";
								optionsDiv.appendChild(sublayerLabel);
								optionsDiv.appendChild(sublayerDiv);
							}
							listItem.appendChild(optionsDiv);
						});
						layer.on("error", function (error) {
							checkboxLabel.removeChild(progress);
							checkbox.checked = false;
							console.error(error);
							layer = null;
							listItem.classList.add("bg-danger");
						});
					}
				}
			}
		}

		function createLayerListDiv(layerDefinitions) {
			var outputDiv, listGroupUL;

			function createListItem(layerDef) {
				var li, label, input, textNode;
				li = document.createElement("li");
				label = document.createElement("label");
				li.appendChild(label);
				input = document.createElement("input");
				input.type = "checkbox";
				input.dataset.url = layerDef.url;
				input.dataset.layerType = layerDef.type;
				input.dataset.layerId = layerDef.id;
				label.appendChild(input);
				textNode = document.createTextNode(layerDef.title || layerDef.id);
				label.appendChild(textNode);
				return li;
			}

			if (!(layerDefinitions && layerDefinitions.length)) {
				throw TypeError("layerDefinitions not provided.");
			}

			// Create the outer container.
			outputDiv = document.createElement("div");
			// Create the list of layers.
			listGroupUL = document.createElement("ul");
			listGroupUL.setAttribute("class", "list-group");
			outputDiv.appendChild(listGroupUL);
			// Add the list items.
			layerDefinitions.forEach(function (layerDef) {
				var li = createListItem(layerDef);
				listGroupUL.appendChild(li);
			});

			return outputDiv;
		}

		var layerListDiv = createLayerListDiv(layerDefinitions), checkboxes, checkbox;

		checkboxes = layerListDiv.querySelectorAll("[type='checkbox']");
		// Attach click event handler to checkboxes.
		for (var i = 0, l = checkboxes.length; i < l; i += 1) {
			checkbox = checkboxes[i];
			checkbox.addEventListener("click", toggleLayer);
		}

		return layerListDiv;
	}

	return {
		createLayerList: createLayerList
	};
});<commit_msg>Replaced collapse link icon.<commit_after>/*global define*/
define([
	"esri/layers/ArcGISTiledMapServiceLayer",
	"esri/layers/ArcGISDynamicMapServiceLayer"
], function (ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer) {

	(function () {
		if (!document.createElement("div").dataset) {
			console.error("The dataset property is not supported by this browser.");
		}
	}());

	function createLayerList(map, layerDefinitions) {
		/** Creates the layer options div.
		* @param {esri/layers/Layer} layer
		* @returns {HTMLDivElement}
		*/
		function createLayerOptions(layer) {

			/*

				opacity <input type="range" min="0" max="1" step="0.1" value="1" />

				*/
			var div = document.createElement("div");
			div.classList.add("layer-options");
			div.classList.add("well");
			var label = document.createElement("label");
			label.textContent = "Opacity";
			var slider = document.createElement("input");
			slider.type = "range";
			slider.min = 0;
			slider.max = 1;
			slider.step = 0.1;
			slider.value = layer.opacity;
			div.appendChild(label);
			div.appendChild(slider);

			slider.onchange = function () {
				layer.setOpacity(slider.value);
			};

			return div;
		}

		/**
			* Creates the link to toggle the layer options div's visiblitly.
			* @param {HTMLDivElement} optionsDiv - The div that will have it's visibility controlled by this link.
			* @returns {HTMLAnchorElement}
			*/
		function createOptionsToggleLink(optionsDiv) {
			var link, span;
			if (!optionsDiv) {
				throw new TypeError("Options div was not provided.");
			}
			link = document.createElement("a");
			link.href = "#";
			span = document.createElement("span");
			span.classList.add("glyphicon");
			span.classList.add("glyphicon-chevron-up");
			link.appendChild(span);

			link.onclick = function () {
				if (span.classList.contains("glyphicon-chevron-up")) {
					span.classList.remove("glyphicon-chevron-up");
					span.classList.add("glyphicon-chevron-down");
					optionsDiv.classList.add("hidden");
				} else {
					span.classList.remove("glyphicon-chevron-down");
					span.classList.add("glyphicon-chevron-up");
					optionsDiv.classList.remove("hidden");
				}
				return false;
			};

			return link;
		}

		/** Applies the `checked` value of the current checkbox
			* to nested checkboxes.
			* @param {Event} e
			*/
		function checkNestedCheckboxes(e) {
			// Get the checkbox that triggered the event.
			// li > label > input[type='checkbox']
			var currentCheckbox = e.currentTarget;
			// Get the list item that hosts the checkbox.
			var listItem = currentCheckbox.parentElement.parentElement;
			// Get a NodeList of child checkboxes.
			// li > ul > li input[type='checkbox']
			var subCheckboxes = listItem.querySelectorAll("ul > li input[type='checkbox']");

			for (var i = 0, l = subCheckboxes.length; i < l; i += 1) {
				subCheckboxes[i].checked = currentCheckbox.checked;
			}

		}

		function createListItem(layerInfos, id) {
			var layerInfo = layerInfos[id], li, ul, hasSublayers;
			hasSublayers = layerInfo.subLayerIds && layerInfo.subLayerIds.length;
			// Create the output list item.
			li = document.createElement("li");
			// Add class for bootstrap styling.
			li.classList.add("list-group-item");
			// Add sublayer lists.
			// Set data-has-sublayers.
			if (hasSublayers) {
				ul = document.createElement("ul");
				// Add class for bootstrap styling.
				ul.classList.add("list-group");
				// For each child layer id, make a recursive call to this function.
				layerInfo.subLayerIds.forEach(function (layerId) {
					var subli = createListItem(layerInfos, layerId);
					ul.appendChild(subli);
				});
			}
			var label = document.createElement("label");
			var checkbox = document.createElement("input");
			checkbox.type = "checkbox";
			checkbox.checked = layerInfo.defaultVisibility;
			//checkbox.dataset.layerId = id;
			checkbox.value = id;
			checkbox.dataset.subLayerIds = layerInfo.subLayerIds ? layerInfo.subLayerIds.join(",") : "";
			checkbox.dataset.parentLayerId = layerInfo.parentLayerId === -1 ? "" : layerInfo.parentLayerId;
			if (hasSublayers) {
				checkbox.onchange = checkNestedCheckboxes;
			}
			label.appendChild(checkbox);
			// label.textContent = layerInfo.name;
			var labelText = document.createElement("span");
			labelText.textContent = layerInfo.name;
			label.appendChild(labelText);
			li.appendChild(label);
			if (ul) {
				li.appendChild(ul);
			}
			return li;
		}

		/**
			* Creates a div with sublayer list.
			* @param {esri/layers/Layer} layer
			* @returns {HTMLDivElement}
			*/
		function createSublayerDiv(layer) {
			var div = null, ul, applyButton;

			if (layer.setVisibleLayers && layer.layerInfos && layer.layerInfos.length > 1) {
				div = document.createElement("div");
				div.setAttribute("class", "well sublayer-list");
				ul = document.createElement("ul");
				ul.classList.add("list-group");

				layer.layerInfos.forEach(function (layerInfo) {
					/*jshint eqnull:true*/
					if (layerInfo.parentLayerId < 0) {
						ul.appendChild(createListItem(layer.layerInfos, layerInfo.id));
					}
					/*jshint eqnull:false*/
				});
				div.appendChild(ul);
				applyButton = document.createElement("button");
				applyButton.type = "button";
				applyButton.appendChild(document.createTextNode("Apply"));
				applyButton.classList.add("btn");
				applyButton.classList.add("btn-primary");
				div.appendChild(applyButton);

				applyButton.onclick = function () {
					// Select all checked checkboxes without a sublayer ID.
					////var checkboxes = ul.querySelectorAll("input[data-sub-layer-ids='']:checked");
					var checkboxes = ul.querySelectorAll("input[type='checkbox']:checked");
					// Get the sublayer IDs.
					var sublayers = [];
					for (var i = 0, l = checkboxes.length; i < l; i += 1) {
						sublayers.push(parseInt(checkboxes[i].value, 10));
					}
					// If the list of sublayer IDs is empty, add -1.
					// This is how you indicate "no sublayers".
					if (sublayers.length < 1) {
						sublayers.push(-1);
					}
					layer.setVisibleLayers(sublayers);
				};
			}

			return div;
		}

		/** Toggles a layers visibility.
			* The first time the checkbox is checked, the corresponding layer will be created and added to the map.
			* @param {Event} e
			* @this {HTMLInputElement} - The clicked checkbox.
			*/
		function toggleLayer(e) {
			var checkbox, layerId, layer, listItem, checkboxLabel, progress;
			checkbox = e.currentTarget;
			checkboxLabel = checkbox.parentElement;
			// Get the li that contains the checkbox.
			listItem = checkboxLabel.parentElement;

			layerId = checkbox.dataset.layerId;
			layer = map.getLayer(layerId);
			if (layer) {
				if (checkbox.checked) {
					layer.show();
				} else {
					layer.hide();
				}
			} else {
				if (checkbox.checked) {
					// Create the layer and add it to the map.

					progress = document.createElement("progress");
					checkbox.disabled = true;
					listItem.appendChild(progress);

					if (checkbox.dataset.layerType === "ArcGISTiledMapServiceLayer") {
						layer = new ArcGISTiledMapServiceLayer(checkbox.dataset.url, {
							id: checkbox.dataset.layerId
						});
					} else if (checkbox.dataset.layerType === "ArcGISDynamicMapServiceLayer") {
						layer = new ArcGISDynamicMapServiceLayer(checkbox.dataset.url, {
							id: checkbox.dataset.layerId
						});
					}

					if (layer) {
						map.addLayer(layer);
						layer.on("load", function () {
							checkbox.disabled = false;
							listItem.removeChild(progress);
							// Add the layer options section.
							var optionsDiv, link, sublayerLabel, sublayerDiv;
							optionsDiv = createLayerOptions(layer);
							link = createOptionsToggleLink(optionsDiv);
							checkboxLabel.appendChild(document.createTextNode(" "));
							checkboxLabel.appendChild(link);
							sublayerDiv = createSublayerDiv(layer);
							if (sublayerDiv) {
								sublayerLabel = document.createElement("label");
								sublayerLabel.innerText = "Sublayers";
								optionsDiv.appendChild(sublayerLabel);
								optionsDiv.appendChild(sublayerDiv);
							}
							listItem.appendChild(optionsDiv);
						});
						layer.on("error", function (error) {
							checkboxLabel.removeChild(progress);
							checkbox.checked = false;
							console.error(error);
							layer = null;
							listItem.classList.add("bg-danger");
						});
					}
				}
			}
		}

		function createLayerListDiv(layerDefinitions) {
			var outputDiv, listGroupUL;

			function createListItem(layerDef) {
				var li, label, input, textNode;
				li = document.createElement("li");
				label = document.createElement("label");
				li.appendChild(label);
				input = document.createElement("input");
				input.type = "checkbox";
				input.dataset.url = layerDef.url;
				input.dataset.layerType = layerDef.type;
				input.dataset.layerId = layerDef.id;
				label.appendChild(input);
				textNode = document.createTextNode(layerDef.title || layerDef.id);
				label.appendChild(textNode);
				return li;
			}

			if (!(layerDefinitions && layerDefinitions.length)) {
				throw TypeError("layerDefinitions not provided.");
			}

			// Create the outer container.
			outputDiv = document.createElement("div");
			// Create the list of layers.
			listGroupUL = document.createElement("ul");
			listGroupUL.setAttribute("class", "list-group");
			outputDiv.appendChild(listGroupUL);
			// Add the list items.
			layerDefinitions.forEach(function (layerDef) {
				var li = createListItem(layerDef);
				listGroupUL.appendChild(li);
			});

			return outputDiv;
		}

		var layerListDiv = createLayerListDiv(layerDefinitions), checkboxes, checkbox;

		checkboxes = layerListDiv.querySelectorAll("[type='checkbox']");
		// Attach click event handler to checkboxes.
		for (var i = 0, l = checkboxes.length; i < l; i += 1) {
			checkbox = checkboxes[i];
			checkbox.addEventListener("click", toggleLayer);
		}

		return layerListDiv;
	}

	return {
		createLayerList: createLayerList
	};
});<|endoftext|>


<commit_before>// Increase default pool size of 5
var https = require('https');
https.globalAgent.maxSockets = 10;

var async = require('async');
var request = require('request');

var eventCenter = require('../eventCenter');
var DateCache = require('../dateCache');

var crestHistoryFilter = require('./crestHistoryFilter');
var crestHistoryDBFilter = require('./crestHistoryDBFilter');
var crestHistoryStore = require('./crestHistoryStore');

var cache = new DateCache();

//
// CREST API Pipeline - each message triggers a CREST history update
//

// List of region/type pairs ready to be enqueued into the requestQueue
var regionTypeList = [];

// Queue for CREST requests
var requestQueue = async.queue(processJob, 30);

// Enqueue new jobs every second
setInterval(enqueueJobs, 1000);

// Register event handler for splitted message
eventCenter.on('splitMessage', enqueueRegionType);

function enqueueRegionType(resultSet) {
  //
  // Enqueues a region/type pair into CREST API queue
  //

  // Compose key of regionID-typeID pair
  var key = String(resultSet.regionID) + '-' + String(resultSet.typeID);

  // Get current time
  var now = new Date();

  // Get tomorrow 00:00:00 for caching purposes
  var tomorrow = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()+1, 0));

  if(cache.needsUpdate(key, tomorrow)) {
    regionTypeList.push({regionID: Number(resultSet.regionID), typeID: Number(resultSet.typeID)});
  }
}

function dequeueRegionTypes(numPairs) {
  //
  // Dequeue a given number of region/type pairs as long as the queue was not paused
  //

  var list = [];

  // Only run if queue wasn't paused
  if(!requestQueue.paused) {
    while(regionTypeList.length > 0 && list.length < numPairs) {
      list.push(regionTypeList.shift());
    }
  }

  return list;
}

function enqueueJobs() {
  //
  // Enqueues up to 10 jobs every second into worker queue
  //

  var list = dequeueRegionTypes(10);
  requestQueue.push(list);
}

function processJob(task, callback) {
  //
  // Performs CREST request
  //

  var options = {
    url: 'https://crest-tq.eveonline.com/market/' + task.regionID + '/history/?type=https://crest-tq.eveonline.com/inventory/types/' + task.typeID + '/'
    strictSSL: true,
    gzip: true,
    headers: {
     'User-Agent': 'Node43/2.3.1 (Git)'
    }
  };

  request(options, handleResponse.bind(null, task, callback));
}

function handleResponse(task, callback, error, response, body) {
  //
  // Parse response, filter and store it
  //

  if(typeof response !== 'undefined'){
    if (response.statusCode === 200) {

      var crestResponse = JSON.parse(body);

      if(crestResponse.items.length > 0) {
        crestResponse.regionID = task.regionID;
        crestResponse.typeID = task.typeID;

        //
        // Filter and store
        //

        async.waterfall([
          function (callback) {
            crestHistoryFilter(crestResponse, callback);   // Filter unnecessary datapoints
          },
          crestHistoryDBFilter,                            // Filter even more datapoints by DB
          crestHistoryStore                                // Store datapoints
        ], function (error, result) {
          if(error){
            if (!error.severity === 0) {
              error.module = 'crestHistoryAgentRequest';

              // Log Errors
              console.error(error);
            }
          }
        });
      }
    } else if (response.statusCode === 502 || response.statusCode === 503) {
      // Re-enqueue region/type pair
      enqueueRegionType({regionID: task.regionID, typeID: task.typeID});

      if(!requestQueue.paused) {
        // Pause queue execution for 1 minute as error 502/503 indicates downtime or throttling
        console.log('[' + new Date().toTimeString() + '] ERROR 502/503 - Halting queue for 1 minute');
        console.log(body);

        requestQueue.pause();
        setTimeout(requestQueue.resume, 60000);
      }
    } else if (response.statusCode === 404) {
      console.log('CREST Exception: ' + body);
    } else {
      console.log(response.headers);
      console.log(body);
    }
  } else {
    console.log('CREST Request Error: ' + error);
  }

  callback(error, response);
}
<commit_msg>Fixed missing comma<commit_after>// Increase default pool size of 5
var https = require('https');
https.globalAgent.maxSockets = 10;

var async = require('async');
var request = require('request');

var eventCenter = require('../eventCenter');
var DateCache = require('../dateCache');

var crestHistoryFilter = require('./crestHistoryFilter');
var crestHistoryDBFilter = require('./crestHistoryDBFilter');
var crestHistoryStore = require('./crestHistoryStore');

var cache = new DateCache();

//
// CREST API Pipeline - each message triggers a CREST history update
//

// List of region/type pairs ready to be enqueued into the requestQueue
var regionTypeList = [];

// Queue for CREST requests
var requestQueue = async.queue(processJob, 30);

// Enqueue new jobs every second
setInterval(enqueueJobs, 1000);

// Register event handler for splitted message
eventCenter.on('splitMessage', enqueueRegionType);

function enqueueRegionType(resultSet) {
  //
  // Enqueues a region/type pair into CREST API queue
  //

  // Compose key of regionID-typeID pair
  var key = String(resultSet.regionID) + '-' + String(resultSet.typeID);

  // Get current time
  var now = new Date();

  // Get tomorrow 00:00:00 for caching purposes
  var tomorrow = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()+1, 0));

  if(cache.needsUpdate(key, tomorrow)) {
    regionTypeList.push({regionID: Number(resultSet.regionID), typeID: Number(resultSet.typeID)});
  }
}

function dequeueRegionTypes(numPairs) {
  //
  // Dequeue a given number of region/type pairs as long as the queue was not paused
  //

  var list = [];

  // Only run if queue wasn't paused
  if(!requestQueue.paused) {
    while(regionTypeList.length > 0 && list.length < numPairs) {
      list.push(regionTypeList.shift());
    }
  }

  return list;
}

function enqueueJobs() {
  //
  // Enqueues up to 10 jobs every second into worker queue
  //

  var list = dequeueRegionTypes(10);
  requestQueue.push(list);
}

function processJob(task, callback) {
  //
  // Performs CREST request
  //

  var options = {
    url: 'https://crest-tq.eveonline.com/market/' + task.regionID + '/history/?type=https://crest-tq.eveonline.com/inventory/types/' + task.typeID + '/',
    strictSSL: true,
    gzip: true,
    headers: {
     'User-Agent': 'Node43/2.3.1 (Git)'
    }
  };

  request(options, handleResponse.bind(null, task, callback));
}

function handleResponse(task, callback, error, response, body) {
  //
  // Parse response, filter and store it
  //

  if(typeof response !== 'undefined'){
    if (response.statusCode === 200) {

      var crestResponse = JSON.parse(body);

      if(crestResponse.items.length > 0) {
        crestResponse.regionID = task.regionID;
        crestResponse.typeID = task.typeID;

        //
        // Filter and store
        //

        async.waterfall([
          function (callback) {
            crestHistoryFilter(crestResponse, callback);   // Filter unnecessary datapoints
          },
          crestHistoryDBFilter,                            // Filter even more datapoints by DB
          crestHistoryStore                                // Store datapoints
        ], function (error, result) {
          if(error){
            if (!error.severity === 0) {
              error.module = 'crestHistoryAgentRequest';

              // Log Errors
              console.error(error);
            }
          }
        });
      }
    } else if (response.statusCode === 502 || response.statusCode === 503) {
      // Re-enqueue region/type pair
      enqueueRegionType({regionID: task.regionID, typeID: task.typeID});

      if(!requestQueue.paused) {
        // Pause queue execution for 1 minute as error 502/503 indicates downtime or throttling
        console.log('[' + new Date().toTimeString() + '] ERROR 502/503 - Halting queue for 1 minute');
        console.log(body);

        requestQueue.pause();
        setTimeout(requestQueue.resume, 60000);
      }
    } else if (response.statusCode === 404) {
      console.log('CREST Exception: ' + body);
    } else {
      console.log(response.headers);
      console.log(body);
    }
  } else {
    console.log('CREST Request Error: ' + error);
  }

  callback(error, response);
}
<|endoftext|>


<commit_before>
/*
 * For Messaging, waiting for loadHashBang to know if we must load
 * an specific message thread at page loading
 */
$(document).bind('loadHashBang', function (event, hashbangvalue) {
    var urlParameters = getHashBangParameters(hashbangvalue);
    // Analize parameters values
    if (urlParameters.Thread) {
        openMessageThreadInTab(urlParameters.Thread, "Message Thread " + urlParameters.Thread, urlParameters.Message);
    }
    if (urlParameters.BookingRequest) {
        openBookingInTab(urlParameters.BookingRequest, urlParameters.Booking,
            "Booking Request " + urlParameters.BookingRequest);
    } else if (urlParameters.Booking) {
        openBookingInTab(0, urlParameters.Booking,
            "Booking " + urlParameters.Booking, ('Review' in urlParameters));
    }
});

$(document).ready(function () {
    /*
    * Change Photo
    */
    $('#changephoto').click(function () {
        popup(UrlUtil.LangPath + 'Dashboard/ChangePhoto/', 'small');
    });

    /*
    * Booking list actions
    */
    $('body').delegate('.bookings-list .actions .item-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            $t.data('booking-request-id'),
            $t.data('booking-id'),
            $t.closest('.bookings-list').find('.user-public-name:eq(0)').text()
        );
    });

    /*
    * Booking Request confirmation
    */
    $('body').delegate('.booking-request-action', 'click', function () {
        var brId = $(this).data('booking-request-id');
        var $tab = $(this).closest('.tab-body');
        var options = { autoUnblockLoading: true };
        var data = { BookingRequestID: brId };
        var $t = $(this);
        var url;
        if ($t.hasClass('button-confirm-datetime')) {
            data.ConfirmedDateType = $(this).data('date-type');
            url = 'Booking/$ConfirmBookingRequest/';
        } else if ($t.hasClass('button-decline-booking')) {
            url = 'Booking/$DeclineBookingRequest/';
        } else if ($t.hasClass('button-cancel-booking')) {
            url = 'Booking/$CancelBookingRequest/';
        } else {
            // Bad handler:
            return;
        }

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab, options)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
                // Some list updates
                // After update request, bookings-list tab need be reloaded
                $('#bookings-all').reload();
                // After update request, state changed, new message created, reload thread list to reflect it
                $('#inbox').reload();
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                if (options.autoUnblockLoading) {
                    $tab.unblock();
                }
            }
        });
    })
    .delegate('.review-booking-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            0,
            $t.data('booking-id'),
            $t.closest('.booking').find('.user-public-name:eq(0)').text(),
            true
        );
    })
    .delegate('.booking-review .open-booking-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            0,
            $t.data('booking-id'),
            $t.closest('.booking-review').find('.user-public-name:eq(0)').text()
        );
    });

    /*=========
    * Messaging
    */
    $('body').delegate('.message-thread-list .actions .item-action', 'click', function () {
        var $t = $(this);
        var auxT = $t.data('message-aux-t');
        var auxID = $t.data('message-aux-id');
        if ((auxT == "Booking" || auxT == "BookingRequest") && auxID) {
            var brID = auxID;
            var bID = 0;
            if (auxT == "Booking") {
                brID = 0;
                bID = auxID;
            }
            openBookingInTab(
                brID,
                bID,
                $t.closest('.items-list').find('.user-public-name:eq(0)').text()
            );
        } else
            openMessageThreadInTab(
                $(this).data('message-thread-id'),
                $(this).closest('.message-thread-list').find('.user-public-name:eq(0)').text());
    })
    .delegate('.conversation-messages > li.new-message textarea', 'focus', function () {
        $(this).animate({ height: 250 });
    });

});

function openBookingInTab(bookingRequestID, bookingID, tabTitle, openReview) {
    var bid = bookingID;
    var brid = bookingRequestID;
    var data = { BookingRequestID: brid };
    var url = "Booking/$BookingRequestDetails/";
    var tabId = 'bookingRequestID' + brid;

    if (bid && bid > 0) {
        url = "Booking/$BookingDetails/";
        data.BookingID = bid;
        tabId += '_bookingID' + bid;

        if (openReview === true) {
            url = "Booking/$BookingReview/";
            tabId += "_Review";
        }
    }

    var tab = TabbedUX.createTab('#main', tabId, tabTitle);
    if (tab) {
        TabbedUX.focusTab(tab);

        var $tab = $(tab);

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                $tab.unblock();

                // Updating the tab title, because when is loaded by URL, the title is the ID,
                // here is setted something more usable:
                TabbedUX.setTabTitle($tab, $tab.find('.user-public-name:eq(0)').text());
            }
        });
    } else
    // Tab couln't be created, already must exist, focus it
        TabbedUX.focusTab('#' + tabId);
}
function openMessageThreadInTab(threadId, tabTitle, highlightMessageId) {
    var tid = threadId;
    var data = { MessageThreadID: tid };
    var url = "Messaging/$MessageThread/";
    var tabId = 'messageThreadID-' + tid;

    var tab = TabbedUX.createTab('#main', tabId, tabTitle);
    if (tab) {
        TabbedUX.focusTab(tab);

        var $tab = $(tab);

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                $tab.unblock();

                // Updating the tab title, because when is loaded by URL, the title is the ID,
                // here is setted something more usable:
                TabbedUX.setTabTitle($tab, $tab.find('.user-public-name:eq(0)').text());

                if (highlightMessageId) {
                    $tab.find('.message-' + highlightMessageId + ' > .message-section').addClass('highlighted');
                }
            }
        });
    } else {
        // Tab couln't be created, already must exist, focus it
        TabbedUX.focusTab('#' + tabId);
        // Search MessageID to highlight it
        if (highlightMessageId) {
            $('#' + tabId).find('.message-' + highlightMessageId + ' > .message-section').addClass('highlighted');
        }
    }
}

/* Return true for 'handled' and false for 'not handled' (there is a custom data.Code to be managed) */
function dashboardGeneralJsonCodeHandler(data, container, options) {
    if (!container) container = $(document);

    // If is a JSON result:
    if (typeof (data) === 'object') {
        if (data.Code == 0) {
            // Special Code 0: general success code, show message saying that 'all was fine'

            // Unblock loading:
            container.unblock();
            // Block with message:
            var message = data.Result || container.data('success-ajax-message') || 'Confirmed!';
            container.block({
                message: message,
                css: popupStyle(popupSize('small'))
            })
            .click(function () { container.unblock(); });
            // Do not unblock in complete function!
            options.autoUnblockLoading = false;
        } else if (data.Code == 1) {
            // Special Code 1: do a redirect
            window.location = data.Result;
        } else if (data.Code == 2) {
            // Special Code 2: show login popup (with the given url at data.Result)
            container.unblock();
            popup(data.Result, { width: 410, height: 320 });
        } else if (data.Code == 3) {
            // Special Code 3: reload current page content to the given url at data.Result)
            // Note: to reload same url page content, is better return the html directly from
            // this ajax server request.
            //container.unblock(); is blocked and unblocked againg by the reload method:
            options.autoUnblockLoading = false;
            container.reload(data.Result);
        } else if (data.Code > 0) {
            // Not handled!
            return false;
        } else { // data.Code < 0
            // There is an error code.

            // Unblock loading:
            container.unblock();
            // Block with message:
            var message = data.Code + ": " + (data.Result ? data.Result.ErrorMessage ? data.Result.ErrorMessage : data.Result : '');
            container.block({
                message: 'Error: ' + message,
                css: popupStyle(popupSize('small'))
            })
            .click(function () { container.unblock(); });

            // Do not unblock in complete function!
            options.autoUnblockLoading = false;
        }
    } else {
        container.html(data);
    }
    return true;
}<commit_msg>Changed how booking tabs are identified, to avoid duplicated booking tabs (more than one tab opened showing the same booking content)<commit_after>
/*
 * For Messaging, waiting for loadHashBang to know if we must load
 * an specific message thread at page loading
 */
$(document).bind('loadHashBang', function (event, hashbangvalue) {
    var urlParameters = getHashBangParameters(hashbangvalue);
    // Analize parameters values
    if (urlParameters.Thread) {
        openMessageThreadInTab(urlParameters.Thread, "Message Thread " + urlParameters.Thread, urlParameters.Message);
    }
    if (urlParameters.BookingRequest) {
        openBookingInTab(urlParameters.BookingRequest, urlParameters.Booking,
            "Booking Request " + urlParameters.BookingRequest);
    } else if (urlParameters.Booking) {
        openBookingInTab(0, urlParameters.Booking,
            "Booking " + urlParameters.Booking, ('Review' in urlParameters));
    }
});

$(document).ready(function () {
    /*
    * Change Photo
    */
    $('#changephoto').click(function () {
        popup(UrlUtil.LangPath + 'Dashboard/ChangePhoto/', 'small');
    });

    /*
    * Booking list actions
    */
    $('body').delegate('.bookings-list .actions .item-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            $t.data('booking-request-id'),
            $t.data('booking-id'),
            $t.closest('.bookings-list').find('.user-public-name:eq(0)').text()
        );
    });

    /*
    * Booking Request confirmation
    */
    $('body').delegate('.booking-request-action', 'click', function () {
        var brId = $(this).data('booking-request-id');
        var $tab = $(this).closest('.tab-body');
        var options = { autoUnblockLoading: true };
        var data = { BookingRequestID: brId };
        var $t = $(this);
        var url;
        if ($t.hasClass('button-confirm-datetime')) {
            data.ConfirmedDateType = $(this).data('date-type');
            url = 'Booking/$ConfirmBookingRequest/';
        } else if ($t.hasClass('button-decline-booking')) {
            url = 'Booking/$DeclineBookingRequest/';
        } else if ($t.hasClass('button-cancel-booking')) {
            url = 'Booking/$CancelBookingRequest/';
        } else {
            // Bad handler:
            return;
        }

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab, options)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
                // Some list updates
                // After update request, bookings-list tab need be reloaded
                $('#bookings-all').reload();
                // After update request, state changed, new message created, reload thread list to reflect it
                $('#inbox').reload();
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                if (options.autoUnblockLoading) {
                    $tab.unblock();
                }
            }
        });
    })
    .delegate('.review-booking-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            0,
            $t.data('booking-id'),
            $t.closest('.booking').find('.user-public-name:eq(0)').text(),
            true
        );
    })
    .delegate('.booking-review .open-booking-action', 'click', function () {
        var $t = $(this);
        openBookingInTab(
            0,
            $t.data('booking-id'),
            $t.closest('.booking-review').find('.user-public-name:eq(0)').text()
        );
    });

    /*=========
    * Messaging
    */
    $('body').delegate('.message-thread-list .actions .item-action', 'click', function () {
        var $t = $(this);
        var auxT = $t.data('message-aux-t');
        var auxID = $t.data('message-aux-id');
        if ((auxT == "Booking" || auxT == "BookingRequest") && auxID) {
            var brID = auxID;
            var bID = 0;
            if (auxT == "Booking") {
                brID = 0;
                bID = auxID;
            }
            openBookingInTab(
                brID,
                bID,
                $t.closest('.items-list').find('.user-public-name:eq(0)').text()
            );
        } else
            openMessageThreadInTab(
                $(this).data('message-thread-id'),
                $(this).closest('.message-thread-list').find('.user-public-name:eq(0)').text());
    })
    .delegate('.conversation-messages > li.new-message textarea', 'focus', function () {
        $(this).animate({ height: 250 });
    });

});

function openBookingInTab(bookingRequestID, bookingID, tabTitle, openReview) {
    var bid = bookingID;
    var brid = bookingRequestID;
    var data = { BookingRequestID: brid };
    var url = "Booking/$BookingRequestDetails/";
    var tabId = 'bookingRequestID' + brid;

    if (bid && bid > 0) {
        url = "Booking/$BookingDetails/";
        data.BookingID = bid;
        tabId = 'bookingID' + bid;

        if (openReview === true) {
            url = "Booking/$BookingReview/";
            tabId += "_Review";
        }
    }

    var tab = TabbedUX.createTab('#main', tabId, tabTitle);
    if (tab) {
        TabbedUX.focusTab(tab);

        var $tab = $(tab);

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                $tab.unblock();

                // Updating the tab title, because when is loaded by URL, the title is the ID,
                // here is setted something more usable:
                TabbedUX.setTabTitle($tab, $tab.find('.user-public-name:eq(0)').text());
            }
        });
    } else
    // Tab couln't be created, already must exist, focus it
        TabbedUX.focusTab('#' + tabId);
}
function openMessageThreadInTab(threadId, tabTitle, highlightMessageId) {
    var tid = threadId;
    var data = { MessageThreadID: tid };
    var url = "Messaging/$MessageThread/";
    var tabId = 'messageThreadID-' + tid;

    var tab = TabbedUX.createTab('#main', tabId, tabTitle);
    if (tab) {
        TabbedUX.focusTab(tab);

        var $tab = $(tab);

        // Loading, with retard
        var loadingtimer = setTimeout(function () {
            $tab.block(loadingBlock);
        }, gLoadingRetard);

        // Do the Ajax post
        $.ajax({
            url: UrlUtil.LangPath + url,
            data: data,
            success: function (data, text, jx) {
                if (!dashboardGeneralJsonCodeHandler(data, $tab)) {
                    // Unknowed sucessfull code (if this happen in production there is a bug!)
                    alert("Result Code: " + data.Code);
                }
            },
            error: ajaxErrorPopupHandler,
            complete: function () {
                // Disable loading
                clearTimeout(loadingtimer);
                // Unblock
                $tab.unblock();

                // Updating the tab title, because when is loaded by URL, the title is the ID,
                // here is setted something more usable:
                TabbedUX.setTabTitle($tab, $tab.find('.user-public-name:eq(0)').text());

                if (highlightMessageId) {
                    $tab.find('.message-' + highlightMessageId + ' > .message-section').addClass('highlighted');
                }
            }
        });
    } else {
        // Tab couln't be created, already must exist, focus it
        TabbedUX.focusTab('#' + tabId);
        // Search MessageID to highlight it
        if (highlightMessageId) {
            $('#' + tabId).find('.message-' + highlightMessageId + ' > .message-section').addClass('highlighted');
        }
    }
}

/* Return true for 'handled' and false for 'not handled' (there is a custom data.Code to be managed) */
function dashboardGeneralJsonCodeHandler(data, container, options) {
    if (!container) container = $(document);

    // If is a JSON result:
    if (typeof (data) === 'object') {
        if (data.Code == 0) {
            // Special Code 0: general success code, show message saying that 'all was fine'

            // Unblock loading:
            container.unblock();
            // Block with message:
            var message = data.Result || container.data('success-ajax-message') || 'Confirmed!';
            container.block({
                message: message,
                css: popupStyle(popupSize('small'))
            })
            .click(function () { container.unblock(); });
            // Do not unblock in complete function!
            options.autoUnblockLoading = false;
        } else if (data.Code == 1) {
            // Special Code 1: do a redirect
            window.location = data.Result;
        } else if (data.Code == 2) {
            // Special Code 2: show login popup (with the given url at data.Result)
            container.unblock();
            popup(data.Result, { width: 410, height: 320 });
        } else if (data.Code == 3) {
            // Special Code 3: reload current page content to the given url at data.Result)
            // Note: to reload same url page content, is better return the html directly from
            // this ajax server request.
            //container.unblock(); is blocked and unblocked againg by the reload method:
            options.autoUnblockLoading = false;
            container.reload(data.Result);
        } else if (data.Code > 0) {
            // Not handled!
            return false;
        } else { // data.Code < 0
            // There is an error code.

            // Unblock loading:
            container.unblock();
            // Block with message:
            var message = data.Code + ": " + (data.Result ? data.Result.ErrorMessage ? data.Result.ErrorMessage : data.Result : '');
            container.block({
                message: 'Error: ' + message,
                css: popupStyle(popupSize('small'))
            })
            .click(function () { container.unblock(); });

            // Do not unblock in complete function!
            options.autoUnblockLoading = false;
        }
    } else {
        container.html(data);
    }
    return true;
}<|endoftext|>