<commit_before>'use strict';

(function() {
  angular.module('ncsaas')
    .service('resourceUtils', ['ncUtils', resourceUtils]);

  function resourceUtils(ncUtils) {
    return {
      setAccessInfo: function(item) {
        item.access_info_text = 'No access info';
        if (!item.access_url) {
          return;
        }

        if (ncUtils.startsWith(item.access_url, "http")) {
          item.access_info_url = item.access_url;
          item.access_info_text = 'Open';

          if (ncUtils.endsWith(item.access_url, "/rdp/")) {
            item.access_info_text = 'Connect';
          }
        } else if (angular.isArray(item.access_url)) {
          // IP addresses
          item.access_info_text = item.access_url.join(', ');
        } else {
          item.access_info_text = item.access_url;
        }
      }
    }
  }

  angular.module('ncsaas')
    .service('baseResourceListController',
    ['baseControllerListClass',
    'ENV',
    'ENTITYLISTFIELDTYPES',
    'resourcesService',
    'priceEstimationService',
    'servicesService',
    'currentStateService',
    'projectsService',
    'ngDialog',
    '$rootScope',
    'resourceUtils',
    baseResourceListController
    ]);

  // need for resource tab
  function baseResourceListController(
    baseControllerListClass,
    ENV,
    ENTITYLISTFIELDTYPES,
    resourcesService,
    priceEstimationService,
    servicesService,
    currentStateService,
    projectsService,
    ngDialog,
    $rootScope,
    resourceUtils) {
    var ControllerListClass = baseControllerListClass.extend({
      init: function() {
        this.service = resourcesService;
        this.blockUIElement = 'tab-content';
        this._super();
        this.searchFieldName = 'name';
        this.selectAll = true;
        this.hasCustomFilters = false;
        var currentCustomerUuid = currentStateService.getCustomerUuid();
        this.actionButtonsListItems = [
          {
            title: 'Start',
            icon: 'fa-play',
            clickFunction: this.startResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('start', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('start', model);
            }.bind(this.controllerScope)
          },
          {
            title: 'Stop',
            icon: 'fa-stop',
            clickFunction: this.stopResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('stop', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('stop', model);
            }.bind(this.controllerScope),
          },
          {
            title: 'Restart',
            icon: 'fa-repeat',
            clickFunction: this.restartResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('restart', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('restart', model);
            }.bind(this.controllerScope)
          },
          {
            title: 'Remove',
            clickFunction: this.remove.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('delete', model);
            }.bind(this.controllerScope),
            className: 'remove',
            icon: 'fa-trash'
          },
          {
            title: 'Unlink',
            clickFunction: this.unlink.bind(this.controllerScope),
            className: 'remove',
            icon: 'fa-unlink'
          }
        ];
        var vm = this;
        this.entityOptions = {
          entityData: {
            noDataText: 'You have no resources yet.',
            noMatchesText: 'No resources found matching filter.',
            checkQuotas: 'resource',
            timer: ENV.resourcesTimerInterval,
            rowTemplateUrl: 'views/resource/row.html'
          },
          list: [
            {
              type: ENTITYLISTFIELDTYPES.icon,
              className: 'icon',
              getTitle: function(item) {
                return item.resource_type;
              },
              getIcon: function(item) {
                var service_type = item.resource_type.split(".")[0];
                return "/static/images/appstore/icon-" + service_type.toLowerCase() + ".png";
              }
            },
            {
              name: 'Name',
              propertyName: 'name',
              type: ENTITYLISTFIELDTYPES.name,
              link: 'resources.details({uuid: entity.uuid, resource_type: entity.resource_type})',
              className: 'resource-name'
            },
            {
              name: 'Provider',
              propertyName: 'service_name',
              type: ENTITYLISTFIELDTYPES.link,
              link: 'organizations.details({uuid: "' + currentCustomerUuid +
              '",tab: "providers", providerUuid: entity.service_uuid, providerType: entity.resource_type.split(".")[0]})'
            },
            {
              name: 'State',
              type: ENTITYLISTFIELDTYPES.colorState,
              propertyName: 'state',
              className: 'visual-status',
              getClass: function(state) {
                var cls = ENV.resourceStateColorClasses[state];
                if (cls == 'processing') {
                  return 'icon refresh spin';
                } else {
                  return 'status-circle ' + cls;
                }
              }
            }
          ]
        };
        this.expandableOptions = [
          {
            isList: false,
            addItemBlock: false,
            viewType: 'resource'
          }
        ];

        currentStateService.getProject().then(function(project) {
          if (project) {
            if (ENV.featuresVisible || ENV.toBeFeatures.indexOf('resources') == -1) {
              vm.entityOptions.entityData.createLink = 'appstore.store';
              vm.entityOptions.entityData.createLinkText = 'Add';
            }
            if (ENV.featuresVisible || ENV.toBeFeatures.indexOf('import') == -1) {
              vm.entityOptions.entityData.importLink = 'import.import';
              vm.entityOptions.entityData.importLinkText = 'Import';
              if (!vm.projectHasNonSharedService(project)) {
                vm.entityOptions.entityData.importDisabled = true;
                vm.entityOptions.entityData.importLinkTooltip = 'Import is not possible as there are no personal provider accounts registered.';
              } else {
                vm.entityOptions.entityData.importDisabled = false;
                vm.entityOptions.entityData.importLinkTooltip = 'Import resources from the registered provider accounts.';
              }
            }
          }
        });
      },
      getMarkers: function() {
        var items = this.controllerScope.list.filter(function hasCoordinates(item) {
          return item.latitude != null && item.longitude != null;
        });

        var points = {};
        items.forEach(function groupMarkersByCoordinates(item) {
          var key = [item.latitude, item.longitude];
          if (!points[key]) {
            points[key] = [];
          }
          points[key].push(item);
        });

        var markers = [];
        angular.forEach(points, function createMarker(items) {
          var item = items[0];
          var message = items.map(function(item) {
            return item.name;
          }).join('<br/>');
          markers.push({
            lat: item.latitude,
            lng: item.longitude,
            message: message
          });
        });
        return markers;
      },
      openMap: function() {
        var markers = this.getMarkers();

        if(!markers) {
          alert('No virtual machines with coordinates');
        } else {
          var scope = $rootScope.$new();
          scope.markers = markers;
          scope.maxbounds = new L.LatLngBounds(markers);
          ngDialog.open({
            template: '<leaflet width="100%" markers="markers" maxbounds="maxbounds"></leaflet>',
            plain: true,
            className: 'ngdialog-theme-default map-dialog',
            scope: scope
          });
        }
      },
      projectHasNonSharedService: function(project) {
        for (var i = 0; i < project.services.length; i++) {
          var service = project.services[i];
          if (!service.shared) {
            return true;
          }
        }
        return false;
      },
      getList: function(filter) {
        var fn = this._super.bind(this);
        return this.adjustSearchFilters().then(function() {
          return fn(filter);
        });
      },
      afterGetList: function() {
        angular.forEach(this.list, resourceUtils.setAccessInfo);
        this._super();
      },
      adjustSearchFilters: function() {
        var vm = this,
          resourcesCounts = null;

        if (!this.hasCustomFilters) {
          vm.service.defaultFilter.resource_type = [];
        }

        return servicesService.getResourceTypes(vm.category).then(function(types) {
          if (!vm.hasCustomFilters) {
            vm.service.defaultFilter.resource_type = types;
          }
          vm.types = angular.copy(vm.service.defaultFilter);
          vm.types.resource_type = types;
        }).then(function() {
          return resourcesService.countByType(vm.types);
        }).then(function(counts) {
          resourcesCounts = counts;
          return servicesService.getServicesList();
        }).then(function(metadata) {
          var filters = [];
          for(var type in metadata) {
            var service = metadata[type];
            var resources = service.resources;
            for (var resource in resources) {
              var id = servicesService.formatResourceType(type, resource);
              if (resourcesCounts[id] > 0) {
                filters.push({
                  name: 'resource_type',
                  title: type + ' ' + resource + ' (' + resourcesCounts[id] + ')',
                  value: id
                });
              }
            }
          }
          vm.searchFilters = filters;
        });
      },
      stopResource:function(resource) {
        var vm = this;
        vm.service.operation('stop', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      startResource:function(resource) {
        var vm = this;
        vm.service.operation('start', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      restartResource:function(resource) {
        var vm = this;
        vm.service.operation('restart', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      removeInstance: function(resource) {
        return this.service.$deleteByUrl(resource.url).then(function(response) {
          if (response.status === "destroy was scheduled") {
            resource.state = 'Deletion Scheduled';
          }
        });
      },
      unlink: function(resource) {
        var vm = this;
        var confirmUnlink = confirm('Are you sure you want to unlink a ' + resource.resource_type + '?');
        if (confirmUnlink) {
          vm.service.operation('unlink', resource.url).then(
              function() {
                servicesService.clearAllCacheForCurrentEndpoint();
                vm.afterInstanceRemove(resource);
              },
              vm.handleActionException.bind(vm)
          );
        }
      },
      afterInstanceRemove: function(resource) {
        if (resource.state === 'Deletion Scheduled') {
          return;
        }
        this._super(resource);
        projectsService.clearAllCacheForCurrentEndpoint();
        priceEstimationService.clearAllCacheForCurrentEndpoint();
      },
      isOperationAvailable: function(operation, resource) {
        var availableOperations = this.service.getAvailableOperations(resource);
        return availableOperations.indexOf(operation.toLowerCase()) !== -1;
      },
      isOperationEnabled: function(operation, resource) {
        var availableOperations = this.service.getEnabledOperations(resource);
        return availableOperations.indexOf(operation.toLowerCase()) !== -1;
      },
      reInitResource:function(resource) {
        var vm = this;
        vm.service.$get(resource.resource_type, resource.uuid).then(function(response) {
          var index = vm.list.indexOf(resource);
          vm.list[index] = response;
          vm.afterGetList();
        });
      },
      remove: function(model) {
        var vm = this.controllerScope;
        var confirmText = (model.state === 'Erred')
          ? 'Are you sure you want to delete a {resource_type} in an Erred state?' +
            ' A cleanup attempt will be performed if you choose so.'
          : 'Are you sure you want to delete a {resource_type}?';
        var confirmDelete = confirm(confirmText.replace('{resource_type}', model.resource_type));
        if (confirmDelete) {
          vm.removeInstance(model).then(function() {
            servicesService.clearAllCacheForCurrentEndpoint();
            vm.afterInstanceRemove(model);
          }, vm.handleActionException.bind(vm));
        }
      }
    });

    return ControllerListClass;
  }
})();

(function() {
  angular.module('ncsaas')
      .controller('ResourceDetailUpdateController', [
        '$stateParams',
        '$state',
        '$scope',
        '$interval',
        'ENV',
        'resourcesService',
        'resourcesCountService',
        'resourceUtils',
        'alertsService',
        'servicesService',
        'baseControllerDetailUpdateClass',
        'currentStateService',
        ResourceDetailUpdateController
      ]);

  function ResourceDetailUpdateController(
    $stateParams,
    $state,
    $scope,
    $interval,
    ENV,
    resourcesService,
    resourcesCountService,
    resourceUtils,
    alertsService,
    servicesService,
    baseControllerDetailUpdateClass,
    currentStateService) {
    var controllerScope = this;
    var Controller = baseControllerDetailUpdateClass.extend({
      canEdit: true,
      defaultErrorMessage: "Reason unknown, please contact support",

      init:function() {
        this.service = resourcesService;
        this.controllerScope = controllerScope;
        this._super();
        this.detailsViewOptions = {
          title_plural: 'resources',
          listState: 'projects.details({uuid: controller.model.project_uuid, tab:controller.resourceTab})',
          aboutFields: [
            {
              fieldKey: 'name',
              isEditable: true,
              className: 'name'
            }
          ],
          tabs: [
            {
              title: 'Details',
              key: 'details',
              viewName: 'tabDetails',
              count: -1,
              hideSearch: true
            },
            {
              title: 'Backups',
              key: 'backups',
              viewName: 'tabBackups',
              count: 0
            },
            {
              title: 'Alerts',
              key: 'alerts',
              viewName: 'tabAlerts',
              countFieldKey: 'alerts'
            }
          ]
        };
        this.detailsViewOptions.activeTab = this.getActiveTab(this.detailsViewOptions.tabs, $stateParams.tab);
      },

      getModel: function() {
        return this.service.$get($stateParams.resource_type, $stateParams.uuid);
      },

      afterActivate: function() {
        this.setCounters();
        this.updateResourceTab();
        this.scheduleRefresh();
      },

      updateResourceTab: function() {
        var service_type = this.model.resource_type.split(".")[0];
        var vm_services = servicesService.getServiceTypes(ENV.VirtualMachines);
        var app_services = servicesService.getServiceTypes(ENV.Applications);
        if (vm_services.indexOf(service_type) > -1) {
          this.resourceTab = ENV.resourcesTypes.vms;
        } else if (app_services.indexOf(service_type) > -1) {
          this.resourceTab = ENV.resourcesTypes.applications;
        }
      },

      getCounters: function() {
        var query = angular.extend(alertsService.defaultFilter, {scope: this.model.url});
        return resourcesCountService.alerts(query).then(function(response) {
          return {alerts: response};
        });
      },

      scheduleRefresh: function() {
        var vm = this;
        vm.updateStatus();
        resourceUtils.setAccessInfo(vm.model);

        var refreshPromise = $interval(function() {
          vm.getModel().then(function(model) {
            vm.model = model;
            vm.updateStatus();
            resourceUtils.setAccessInfo(vm.model);
          });
        }, ENV.resourcesTimerInterval * 1000);

        $scope.$on('$destroy', function() {
          $interval.cancel(refreshPromise);
        });
      },
      updateStatus: function() {
        this.inProgress = (ENV.resourceStateColorClasses[this.model.state] === 'processing');
      },
      modelNotFound: function() {
        currentStateService.getProject().then(function() {
          $state.go('resources.list');
        }, function() {
          currentStateService.getCustomer().then(function(response) {
            $state.go('organizations.details', {uuid: response.uuid});
          }, function() {
            $state.go('dashboard.index');
          });
        });
      },

      update:function() {
        var vm = this;
        vm.model.$update(success, error);
        function success() {
          $state.go('resources.details', {'resource_type': $stateParams.resource_type, 'uuid': vm.model.uuid});
        }
        function error(response) {
          vm.errors = response.data;
        }
      }
    });

    controllerScope.__proto__ = new Controller();
  }
})();
<commit_msg>Fix confirm text template<commit_after>'use strict';

(function() {
  angular.module('ncsaas')
    .service('resourceUtils', ['ncUtils', resourceUtils]);

  function resourceUtils(ncUtils) {
    return {
      setAccessInfo: function(item) {
        item.access_info_text = 'No access info';
        if (!item.access_url) {
          return;
        }

        if (ncUtils.startsWith(item.access_url, "http")) {
          item.access_info_url = item.access_url;
          item.access_info_text = 'Open';

          if (ncUtils.endsWith(item.access_url, "/rdp/")) {
            item.access_info_text = 'Connect';
          }
        } else if (angular.isArray(item.access_url)) {
          // IP addresses
          item.access_info_text = item.access_url.join(', ');
        } else {
          item.access_info_text = item.access_url;
        }
      }
    }
  }

  angular.module('ncsaas')
    .service('baseResourceListController',
    ['baseControllerListClass',
    'ENV',
    'ENTITYLISTFIELDTYPES',
    'resourcesService',
    'priceEstimationService',
    'servicesService',
    'currentStateService',
    'projectsService',
    'ngDialog',
    '$rootScope',
    'resourceUtils',
    baseResourceListController
    ]);

  // need for resource tab
  function baseResourceListController(
    baseControllerListClass,
    ENV,
    ENTITYLISTFIELDTYPES,
    resourcesService,
    priceEstimationService,
    servicesService,
    currentStateService,
    projectsService,
    ngDialog,
    $rootScope,
    resourceUtils) {
    var ControllerListClass = baseControllerListClass.extend({
      init: function() {
        this.service = resourcesService;
        this.blockUIElement = 'tab-content';
        this._super();
        this.searchFieldName = 'name';
        this.selectAll = true;
        this.hasCustomFilters = false;
        var currentCustomerUuid = currentStateService.getCustomerUuid();
        this.actionButtonsListItems = [
          {
            title: 'Start',
            icon: 'fa-play',
            clickFunction: this.startResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('start', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('start', model);
            }.bind(this.controllerScope)
          },
          {
            title: 'Stop',
            icon: 'fa-stop',
            clickFunction: this.stopResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('stop', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('stop', model);
            }.bind(this.controllerScope),
          },
          {
            title: 'Restart',
            icon: 'fa-repeat',
            clickFunction: this.restartResource.bind(this.controllerScope),
            isHidden: function(model) {
              return !this.isOperationAvailable('restart', model);
            }.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('restart', model);
            }.bind(this.controllerScope)
          },
          {
            title: 'Remove',
            clickFunction: this.remove.bind(this.controllerScope),
            isDisabled: function(model) {
              return !this.isOperationEnabled('delete', model);
            }.bind(this.controllerScope),
            className: 'remove',
            icon: 'fa-trash'
          },
          {
            title: 'Unlink',
            clickFunction: this.unlink.bind(this.controllerScope),
            className: 'remove',
            icon: 'fa-unlink'
          }
        ];
        var vm = this;
        this.entityOptions = {
          entityData: {
            noDataText: 'You have no resources yet.',
            noMatchesText: 'No resources found matching filter.',
            checkQuotas: 'resource',
            timer: ENV.resourcesTimerInterval,
            rowTemplateUrl: 'views/resource/row.html'
          },
          list: [
            {
              type: ENTITYLISTFIELDTYPES.icon,
              className: 'icon',
              getTitle: function(item) {
                return item.resource_type;
              },
              getIcon: function(item) {
                var service_type = item.resource_type.split(".")[0];
                return "/static/images/appstore/icon-" + service_type.toLowerCase() + ".png";
              }
            },
            {
              name: 'Name',
              propertyName: 'name',
              type: ENTITYLISTFIELDTYPES.name,
              link: 'resources.details({uuid: entity.uuid, resource_type: entity.resource_type})',
              className: 'resource-name'
            },
            {
              name: 'Provider',
              propertyName: 'service_name',
              type: ENTITYLISTFIELDTYPES.link,
              link: 'organizations.details({uuid: "' + currentCustomerUuid +
              '",tab: "providers", providerUuid: entity.service_uuid, providerType: entity.resource_type.split(".")[0]})'
            },
            {
              name: 'State',
              type: ENTITYLISTFIELDTYPES.colorState,
              propertyName: 'state',
              className: 'visual-status',
              getClass: function(state) {
                var cls = ENV.resourceStateColorClasses[state];
                if (cls == 'processing') {
                  return 'icon refresh spin';
                } else {
                  return 'status-circle ' + cls;
                }
              }
            }
          ]
        };
        this.expandableOptions = [
          {
            isList: false,
            addItemBlock: false,
            viewType: 'resource'
          }
        ];

        currentStateService.getProject().then(function(project) {
          if (project) {
            if (ENV.featuresVisible || ENV.toBeFeatures.indexOf('resources') == -1) {
              vm.entityOptions.entityData.createLink = 'appstore.store';
              vm.entityOptions.entityData.createLinkText = 'Add';
            }
            if (ENV.featuresVisible || ENV.toBeFeatures.indexOf('import') == -1) {
              vm.entityOptions.entityData.importLink = 'import.import';
              vm.entityOptions.entityData.importLinkText = 'Import';
              if (!vm.projectHasNonSharedService(project)) {
                vm.entityOptions.entityData.importDisabled = true;
                vm.entityOptions.entityData.importLinkTooltip = 'Import is not possible as there are no personal provider accounts registered.';
              } else {
                vm.entityOptions.entityData.importDisabled = false;
                vm.entityOptions.entityData.importLinkTooltip = 'Import resources from the registered provider accounts.';
              }
            }
          }
        });
      },
      getMarkers: function() {
        var items = this.controllerScope.list.filter(function hasCoordinates(item) {
          return item.latitude != null && item.longitude != null;
        });

        var points = {};
        items.forEach(function groupMarkersByCoordinates(item) {
          var key = [item.latitude, item.longitude];
          if (!points[key]) {
            points[key] = [];
          }
          points[key].push(item);
        });

        var markers = [];
        angular.forEach(points, function createMarker(items) {
          var item = items[0];
          var message = items.map(function(item) {
            return item.name;
          }).join('<br/>');
          markers.push({
            lat: item.latitude,
            lng: item.longitude,
            message: message
          });
        });
        return markers;
      },
      openMap: function() {
        var markers = this.getMarkers();

        if(!markers) {
          alert('No virtual machines with coordinates');
        } else {
          var scope = $rootScope.$new();
          scope.markers = markers;
          scope.maxbounds = new L.LatLngBounds(markers);
          ngDialog.open({
            template: '<leaflet width="100%" markers="markers" maxbounds="maxbounds"></leaflet>',
            plain: true,
            className: 'ngdialog-theme-default map-dialog',
            scope: scope
          });
        }
      },
      projectHasNonSharedService: function(project) {
        for (var i = 0; i < project.services.length; i++) {
          var service = project.services[i];
          if (!service.shared) {
            return true;
          }
        }
        return false;
      },
      getList: function(filter) {
        var fn = this._super.bind(this);
        return this.adjustSearchFilters().then(function() {
          return fn(filter);
        });
      },
      afterGetList: function() {
        angular.forEach(this.list, resourceUtils.setAccessInfo);
        this._super();
      },
      adjustSearchFilters: function() {
        var vm = this,
          resourcesCounts = null;

        if (!this.hasCustomFilters) {
          vm.service.defaultFilter.resource_type = [];
        }

        return servicesService.getResourceTypes(vm.category).then(function(types) {
          if (!vm.hasCustomFilters) {
            vm.service.defaultFilter.resource_type = types;
          }
          vm.types = angular.copy(vm.service.defaultFilter);
          vm.types.resource_type = types;
        }).then(function() {
          return resourcesService.countByType(vm.types);
        }).then(function(counts) {
          resourcesCounts = counts;
          return servicesService.getServicesList();
        }).then(function(metadata) {
          var filters = [];
          for(var type in metadata) {
            var service = metadata[type];
            var resources = service.resources;
            for (var resource in resources) {
              var id = servicesService.formatResourceType(type, resource);
              if (resourcesCounts[id] > 0) {
                filters.push({
                  name: 'resource_type',
                  title: type + ' ' + resource + ' (' + resourcesCounts[id] + ')',
                  value: id
                });
              }
            }
          }
          vm.searchFilters = filters;
        });
      },
      stopResource:function(resource) {
        var vm = this;
        vm.service.operation('stop', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      startResource:function(resource) {
        var vm = this;
        vm.service.operation('start', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      restartResource:function(resource) {
        var vm = this;
        vm.service.operation('restart', resource.url).then(
          vm.reInitResource.bind(vm, resource),
          vm.handleActionException.bind(vm)
        );
      },
      removeInstance: function(resource) {
        return this.service.$deleteByUrl(resource.url).then(function(response) {
          if (response.status === "destroy was scheduled") {
            resource.state = 'Deletion Scheduled';
          }
        });
      },
      unlink: function(resource) {
        var vm = this;
        var confirmUnlink = confirm('Are you sure you want to unlink ' + resource.name + '?');
        if (confirmUnlink) {
          vm.service.operation('unlink', resource.url).then(
              function() {
                servicesService.clearAllCacheForCurrentEndpoint();
                vm.afterInstanceRemove(resource);
              },
              vm.handleActionException.bind(vm)
          );
        }
      },
      afterInstanceRemove: function(resource) {
        if (resource.state === 'Deletion Scheduled') {
          return;
        }
        this._super(resource);
        projectsService.clearAllCacheForCurrentEndpoint();
        priceEstimationService.clearAllCacheForCurrentEndpoint();
      },
      isOperationAvailable: function(operation, resource) {
        var availableOperations = this.service.getAvailableOperations(resource);
        return availableOperations.indexOf(operation.toLowerCase()) !== -1;
      },
      isOperationEnabled: function(operation, resource) {
        var availableOperations = this.service.getEnabledOperations(resource);
        return availableOperations.indexOf(operation.toLowerCase()) !== -1;
      },
      reInitResource:function(resource) {
        var vm = this;
        vm.service.$get(resource.resource_type, resource.uuid).then(function(response) {
          var index = vm.list.indexOf(resource);
          vm.list[index] = response;
          vm.afterGetList();
        });
      },
      remove: function(model) {
        var vm = this.controllerScope;
        var confirmText = (model.state === 'Erred')
          ? 'Are you sure you want to delete a {resource_type} in an Erred state?' +
            ' A cleanup attempt will be performed if you choose so.'
          : 'Are you sure you want to delete a {resource_type}?';
        var confirmDelete = confirm(confirmText.replace('{resource_type}', model.resource_type));
        if (confirmDelete) {
          vm.removeInstance(model).then(function() {
            servicesService.clearAllCacheForCurrentEndpoint();
            vm.afterInstanceRemove(model);
          }, vm.handleActionException.bind(vm));
        }
      }
    });

    return ControllerListClass;
  }
})();

(function() {
  angular.module('ncsaas')
      .controller('ResourceDetailUpdateController', [
        '$stateParams',
        '$state',
        '$scope',
        '$interval',
        'ENV',
        'resourcesService',
        'resourcesCountService',
        'resourceUtils',
        'alertsService',
        'servicesService',
        'baseControllerDetailUpdateClass',
        'currentStateService',
        ResourceDetailUpdateController
      ]);

  function ResourceDetailUpdateController(
    $stateParams,
    $state,
    $scope,
    $interval,
    ENV,
    resourcesService,
    resourcesCountService,
    resourceUtils,
    alertsService,
    servicesService,
    baseControllerDetailUpdateClass,
    currentStateService) {
    var controllerScope = this;
    var Controller = baseControllerDetailUpdateClass.extend({
      canEdit: true,
      defaultErrorMessage: "Reason unknown, please contact support",

      init:function() {
        this.service = resourcesService;
        this.controllerScope = controllerScope;
        this._super();
        this.detailsViewOptions = {
          title_plural: 'resources',
          listState: 'projects.details({uuid: controller.model.project_uuid, tab:controller.resourceTab})',
          aboutFields: [
            {
              fieldKey: 'name',
              isEditable: true,
              className: 'name'
            }
          ],
          tabs: [
            {
              title: 'Details',
              key: 'details',
              viewName: 'tabDetails',
              count: -1,
              hideSearch: true
            },
            {
              title: 'Backups',
              key: 'backups',
              viewName: 'tabBackups',
              count: 0
            },
            {
              title: 'Alerts',
              key: 'alerts',
              viewName: 'tabAlerts',
              countFieldKey: 'alerts'
            }
          ]
        };
        this.detailsViewOptions.activeTab = this.getActiveTab(this.detailsViewOptions.tabs, $stateParams.tab);
      },

      getModel: function() {
        return this.service.$get($stateParams.resource_type, $stateParams.uuid);
      },

      afterActivate: function() {
        this.setCounters();
        this.updateResourceTab();
        this.scheduleRefresh();
      },

      updateResourceTab: function() {
        var service_type = this.model.resource_type.split(".")[0];
        var vm_services = servicesService.getServiceTypes(ENV.VirtualMachines);
        var app_services = servicesService.getServiceTypes(ENV.Applications);
        if (vm_services.indexOf(service_type) > -1) {
          this.resourceTab = ENV.resourcesTypes.vms;
        } else if (app_services.indexOf(service_type) > -1) {
          this.resourceTab = ENV.resourcesTypes.applications;
        }
      },

      getCounters: function() {
        var query = angular.extend(alertsService.defaultFilter, {scope: this.model.url});
        return resourcesCountService.alerts(query).then(function(response) {
          return {alerts: response};
        });
      },

      scheduleRefresh: function() {
        var vm = this;
        vm.updateStatus();
        resourceUtils.setAccessInfo(vm.model);

        var refreshPromise = $interval(function() {
          vm.getModel().then(function(model) {
            vm.model = model;
            vm.updateStatus();
            resourceUtils.setAccessInfo(vm.model);
          });
        }, ENV.resourcesTimerInterval * 1000);

        $scope.$on('$destroy', function() {
          $interval.cancel(refreshPromise);
        });
      },
      updateStatus: function() {
        this.inProgress = (ENV.resourceStateColorClasses[this.model.state] === 'processing');
      },
      modelNotFound: function() {
        currentStateService.getProject().then(function() {
          $state.go('resources.list');
        }, function() {
          currentStateService.getCustomer().then(function(response) {
            $state.go('organizations.details', {uuid: response.uuid});
          }, function() {
            $state.go('dashboard.index');
          });
        });
      },

      update:function() {
        var vm = this;
        vm.model.$update(success, error);
        function success() {
          $state.go('resources.details', {'resource_type': $stateParams.resource_type, 'uuid': vm.model.uuid});
        }
        function error(response) {
          vm.errors = response.data;
        }
      }
    });

    controllerScope.__proto__ = new Controller();
  }
})();
<|endoftext|>


<commit_before>// jquery.tweet.js - See http://tweet.seaofclouds.com/ or https://github.com/seaofclouds/tweet for more info
// Copyright (c) 2008-2011 Todd Matthews & Steve Purcell
(function (factory) {
  if (typeof define === 'function' && define.amd)
    define(['jquery'], factory); // AMD support for RequireJS etc.
  else
    factory(jQuery);
}(function ($) {
  $.fn.tweet = function(o){
    var s = $.extend({
      username: null,                           // [string or array] required unless using the 'query' option; one or more twitter screen names (use 'list' option for multiple names, where possible)
      list: null,                               // [string]   optional name of list belonging to username
      favorites: false,                         // [boolean]  display the user's favorites instead of his tweets
      query: null,                              // [string]   optional search query (see also: http://search.twitter.com/operators)
      avatar_size: null,                        // [integer]  height and width of avatar if displayed (48px max)
      count: 3,                                 // [integer]  how many tweets to display?
      fetch: null,                              // [integer]  how many tweets to fetch via the API (set this higher than 'count' if using the 'filter' option)
      page: 1,                                  // [integer]  which page of results to fetch (if count != fetch, you'll get unexpected results)
      retweets: true,                           // [boolean]  whether to fetch (official) retweets (not supported in all display modes)
      intro_text: null,                         // [string]   do you want text BEFORE your your tweets?
      outro_text: null,                         // [string]   do you want text AFTER your tweets?
      join_text:  null,                         // [string]   optional text in between date and tweet, try setting to "auto"
      auto_join_text_default: "I said,",        // [string]   auto text for non verb: "I said" bullocks
      auto_join_text_ed: "I",                   // [string]   auto text for past tense: "I" surfed
      auto_join_text_ing: "I am",               // [string]   auto tense for present tense: "I was" surfing
      auto_join_text_reply: "I replied to",     // [string]   auto tense for replies: "I replied to" @someone "with"
      auto_join_text_url: "I was looking at",   // [string]   auto tense for urls: "I was looking at" http:...
      loading_text: null,                       // [string]   optional loading text, displayed while tweets load
      refresh_interval: null,                   // [integer]  optional number of seconds after which to reload tweets
      twitter_url: "twitter.com",               // [string]   custom twitter url, if any (apigee, etc.)
      twitter_api_url: "api.twitter.com",       // [string]   custom twitter api url, if any (apigee, etc.)
      twitter_search_url: "search.twitter.com", // [string]   custom twitter search url, if any (apigee, etc.)
      template: "{avatar}{time}{join}{text}",   // [string or function] template used to construct each tweet
 - see code for available vars
      comparator: function(tweet1, tweet2) {    // [function] comparator used to sort tweets (see Array.sort)
        return tweet2["tweet_time"] - tweet1["tweet_time"];
      },
      filter: function(tweet) {                 // [function] whether or not to include a particular tweet (be sure to also set 'fetch')
        return true;
      }
      // You can attach callbacks to the following events using jQuery's standard .bind() mechanism:
      //   "loaded" -- triggered when tweets have been fetched and rendered
    }, o);

    // See http://daringfireball.net/2010/07/improved_regex_for_matching_urls
    var url_regexp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;

    // Expand values inside simple string templates with {placeholders}
    function t(template, info) {
      if (typeof template === "string") {
        var result = template;
        for(var key in info) {
          var val = info[key];
          result = result.replace(new RegExp('{'+key+'}','g'), val === null ? '' : val);
        }
        return result;
      } else return template(info);
    }
    // Export the t function for use when passing a function as the 'template' option
    $.extend({tweet: {t: t}});

    function replacer (regex, replacement) {
      return function() {
        var returning = [];
        this.each(function() {
          returning.push(this.replace(regex, replacement));
        });
        return $(returning);
      };
    }

    function escapeHTML(s) {
      return s.replace(/</g,"<").replace(/>/g,"^>");
    }

    $.fn.extend({
      linkUser: replacer(/(^|[\W])@(\w+)/gi, "$1@$2"),
      // Support various latin1 (\u00**) and arabic (\u06**) alphanumeric chars
      linkHash: replacer(/(?:^| )[\#]+([\w\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0600-\u06ff]+)/gi,
                         ' #$1'),
      makeHeart: replacer(/(<)+[3]/gi, "<tt class='heart'>♥</tt>")
    });

    function linkURLs(text, entities) {
      return text.replace(url_regexp, function(match) {
        var url = (/^[a-z]+:/i).test(match) ? match : "http://"+match;
        var text = match;
        for(var i = 0; i < entities.length; ++i) {
          var entity = entities[i];
          if (entity.url == url && entity.expanded_url) {
            url = entity.expanded_url;
            text = entity.display_url;
            break;
          }
        }
        return ""+escapeHTML(text)+"";
      });
    }

    function parse_date(date_str) {
      // The non-search twitter APIs return inconsistently-formatted dates, which Date.parse
      // cannot handle in IE. We therefore perform the following transformation:
      // "Wed Apr 29 08:53:31 +0000 2009" => "Wed, Apr 29 2009 08:53:31 +0000"
      return Date.parse(date_str.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3'));
    }

    function relative_time(date) {
      var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
      var delta = parseInt((relative_to.getTime() - date) / 1000, 10);
      var r = '';
      if (delta < 1) {
        r = 'just now';
      } else if (delta < 60) {
        r = delta + ' seconds ago';
      } else if(delta < 120) {
        r = 'about a minute ago';
      } else if(delta < (45*60)) {
        r = 'about ' + (parseInt(delta / 60, 10)).toString() + ' minutes ago';
      } else if(delta < (2*60*60)) {
        r = 'about an hour ago';
      } else if(delta < (24*60*60)) {
        r = 'about ' + (parseInt(delta / 3600, 10)).toString() + ' hours ago';
      } else if(delta < (48*60*60)) {
        r = 'about a day ago';
      } else {
        r = 'about ' + (parseInt(delta / 86400, 10)).toString() + ' days ago';
      }
      return r;
    }

    function build_auto_join_text(text) {
      if (text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) {
        return s.auto_join_text_reply;
      } else if (text.match(url_regexp)) {
        return s.auto_join_text_url;
      } else if (text.match(/^((\w+ed)|just) .*/im)) {
        return s.auto_join_text_ed;
      } else if (text.match(/^(\w*ing) .*/i)) {
        return s.auto_join_text_ing;
      } else {
        return s.auto_join_text_default;
      }
    }

    function build_api_url() {
      var proto = ('https:' == document.location.protocol ? 'https:' : 'http:');
      var count = (s.fetch === null) ? s.count : s.fetch;
      var common_params = '&include_entities=1&callback=?';
      if (s.list) {
        return proto+"//"+s.twitter_api_url+"/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?page="+s.page+"&per_page="+count+common_params;
      } else if (s.favorites) {
        return proto+"//"+s.twitter_api_url+"/favorites/"+s.username[0]+".json?page="+s.page+"&count="+count+common_params;
      } else if (s.query === null && s.username.length == 1) {
        return proto+'//'+s.twitter_api_url+'/1/statuses/user_timeline.json?screen_name='+s.username[0]+'&count='+count+(s.retweets ? '&include_rts=1' : '')+'&page='+s.page+common_params;
      } else {
        var query = (s.query || 'from:'+s.username.join(' OR from:'));
        return proto+'//'+s.twitter_search_url+'/search.json?&q='+encodeURIComponent(query)+'&rpp='+count+'&page='+s.page+common_params;
      }
    }

    function extract_avatar_url(item, secure) {
      if (secure) {
        return ('user' in item) ?
          item.user.profile_image_url_https :
          extract_avatar_url(item, false).
            replace(/^http:\/\/[a-z0-9]{1,3}\.twimg\.com\//, "https://s3.amazonaws.com/twitter_production/");
      } else {
        return item.profile_image_url || item.user.profile_image_url;
      }
    }

    // Convert twitter API objects into data available for
    // constructing each tweet
 using a template
    function extract_template_data(item){
      var o = {};
      o.item = item;
      o.source = item.source;
      o.screen_name = item.from_user || item.user.screen_name;
      // The actual user name is not returned by all Twitter APIs, so please do not
      // file an issue if it is empty:
      o.name = item.from_user_name || item.user.name;
      o.avatar_size = s.avatar_size;
      o.avatar_url = extract_avatar_url(item, (document.location.protocol === 'https:'));
      o.retweet = typeof(item.retweeted_status) != 'undefined';
      o.tweet_time = parse_date(item.created_at);
      o.join_text = s.join_text == "auto" ? build_auto_join_text(item.text) : s.join_text;
      o.tweet_id = item.id_str;
      o.twitter_base = "http://"+s.twitter_url+"/";
      o.user_url = o.twitter_base+o.screen_name;
      o.tweet_url = o.user_url+"/status/"+o.tweet_id;
      o.reply_url = o.twitter_base+"intent/tweet?in_reply_to="+o.tweet_id;
      o.retweet_url = o.twitter_base+"intent/retweet?tweet_id="+o.tweet_id;
      o.favorite_url = o.twitter_base+"intent/favorite?tweet_id="+o.tweet_id;
      o.retweeted_screen_name = o.retweet && item.retweeted_status.user.screen_name;
      o.tweet_relative_time = relative_time(o.tweet_time);
      o.entities = item.entities ? (item.entities.urls || []).concat(item.entities.media || []) : [];
      o.tweet_raw_text = o.retweet ? ('RT @'+o.retweeted_screen_name+' '+item.retweeted_status.text) : item.text; // avoid '...' in long retweets
      o.tweet_text = $([linkURLs(o.tweet_raw_text, o.entities)]).linkUser().linkHash()[0];
      o.tweet_text_fancy = $([o.tweet_text]).makeHeart()[0];

      // Default spans, and pre-formatted blocks for common layouts
      o.user = t('{screen_name}', o);
      o.join = s.join_text ? t(' {join_text} ', o) : ' ';
      o.avatar = o.avatar_size ?
        t('', o) : '';
      o.time = t('{tweet_relative_time}', o);
      o.text = t('{tweet_text_fancy}', o);
      o.reply_action = t('reply', o);
      o.retweet_action = t('retweet', o);
      o.favorite_action = t('favorite', o);
      return o;
    }

    function load(widget) {
      var intro = '
'+s.intro_text+'
';
      var outro = '
'+s.outro_text+'
';
      var loading = $('
'+s.loading_text+'
');
      if (s.loading_text) $(widget).not(":has(.tweet_list)").empty().append(loading);
      $.getJSON(build_api_url(), function(data){
        var list = $('
');
        var tweets = $.map(data.results || data, extract_template_data);
        tweets = $.grep(tweets, s.filter).sort(s.comparator).slice(0, s.count);
        list.append($.map(tweets, function(o) { return "
" + t(s.template, o) + "
"; }).join('')).
          children('li:first').addClass('tweet_first').end().
          children('li:odd').addClass('tweet_even').end().
          children('li:even').addClass('tweet_odd');

        $(widget).empty().append(list);
        if (s.intro_text) list.before(intro);
        if (s.outro_text) list.after(outro);

        $(widget).trigger("loaded").trigger((tweets.length === 0 ? "empty" : "full"));
        if (s.refresh_interval) {
          window.setTimeout(function() { $(widget).trigger("tweet:load"); }, 1000 * s.refresh_interval);
        }
      });
    }

    return this.each(function(i, widget){
      if(s.username && typeof(s.username) == "string"){
        s.username = [s.username];
      }

      $(widget).unbind("tweet:load").bind("tweet:load", function(){
        load(widget);
      }).trigger("tweet:load");
    });
  };
}));
<commit_msg>Fix broken match for multiple URLs in a single tweet<commit_after>// jquery.tweet.js - See http://tweet.seaofclouds.com/ or https://github.com/seaofclouds/tweet for more info
// Copyright (c) 2008-2011 Todd Matthews & Steve Purcell
(function (factory) {
  if (typeof define === 'function' && define.amd)
    define(['jquery'], factory); // AMD support for RequireJS etc.
  else
    factory(jQuery);
}(function ($) {
  $.fn.tweet = function(o){
    var s = $.extend({
      username: null,                           // [string or array] required unless using the 'query' option; one or more twitter screen names (use 'list' option for multiple names, where possible)
      list: null,                               // [string]   optional name of list belonging to username
      favorites: false,                         // [boolean]  display the user's favorites instead of his tweets
      query: null,                              // [string]   optional search query (see also: http://search.twitter.com/operators)
      avatar_size: null,                        // [integer]  height and width of avatar if displayed (48px max)
      count: 3,                                 // [integer]  how many tweets to display?
      fetch: null,                              // [integer]  how many tweets to fetch via the API (set this higher than 'count' if using the 'filter' option)
      page: 1,                                  // [integer]  which page of results to fetch (if count != fetch, you'll get unexpected results)
      retweets: true,                           // [boolean]  whether to fetch (official) retweets (not supported in all display modes)
      intro_text: null,                         // [string]   do you want text BEFORE your your tweets?
      outro_text: null,                         // [string]   do you want text AFTER your tweets?
      join_text:  null,                         // [string]   optional text in between date and tweet, try setting to "auto"
      auto_join_text_default: "I said,",        // [string]   auto text for non verb: "I said" bullocks
      auto_join_text_ed: "I",                   // [string]   auto text for past tense: "I" surfed
      auto_join_text_ing: "I am",               // [string]   auto tense for present tense: "I was" surfing
      auto_join_text_reply: "I replied to",     // [string]   auto tense for replies: "I replied to" @someone "with"
      auto_join_text_url: "I was looking at",   // [string]   auto tense for urls: "I was looking at" http:...
      loading_text: null,                       // [string]   optional loading text, displayed while tweets load
      refresh_interval: null,                   // [integer]  optional number of seconds after which to reload tweets
      twitter_url: "twitter.com",               // [string]   custom twitter url, if any (apigee, etc.)
      twitter_api_url: "api.twitter.com",       // [string]   custom twitter api url, if any (apigee, etc.)
      twitter_search_url: "search.twitter.com", // [string]   custom twitter search url, if any (apigee, etc.)
      template: "{avatar}{time}{join}{text}",   // [string or function] template used to construct each tweet
 - see code for available vars
      comparator: function(tweet1, tweet2) {    // [function] comparator used to sort tweets (see Array.sort)
        return tweet2["tweet_time"] - tweet1["tweet_time"];
      },
      filter: function(tweet) {                 // [function] whether or not to include a particular tweet (be sure to also set 'fetch')
        return true;
      }
      // You can attach callbacks to the following events using jQuery's standard .bind() mechanism:
      //   "loaded" -- triggered when tweets have been fetched and rendered
    }, o);

    // See http://daringfireball.net/2010/07/improved_regex_for_matching_urls
    var url_regexp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi;

    // Expand values inside simple string templates with {placeholders}
    function t(template, info) {
      if (typeof template === "string") {
        var result = template;
        for(var key in info) {
          var val = info[key];
          result = result.replace(new RegExp('{'+key+'}','g'), val === null ? '' : val);
        }
        return result;
      } else return template(info);
    }
    // Export the t function for use when passing a function as the 'template' option
    $.extend({tweet: {t: t}});

    function replacer (regex, replacement) {
      return function() {
        var returning = [];
        this.each(function() {
          returning.push(this.replace(regex, replacement));
        });
        return $(returning);
      };
    }

    function escapeHTML(s) {
      return s.replace(/</g,"<").replace(/>/g,"^>");
    }

    $.fn.extend({
      linkUser: replacer(/(^|[\W])@(\w+)/gi, "$1@$2"),
      // Support various latin1 (\u00**) and arabic (\u06**) alphanumeric chars
      linkHash: replacer(/(?:^| )[\#]+([\w\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0600-\u06ff]+)/gi,
                         ' #$1'),
      makeHeart: replacer(/(<)+[3]/gi, "<tt class='heart'>♥</tt>")
    });

    function linkURLs(text, entities) {
      return text.replace(url_regexp, function(match) {
        var url = (/^[a-z]+:/i).test(match) ? match : "http://"+match;
        var text = match;
        for(var i = 0; i < entities.length; ++i) {
          var entity = entities[i];
          if (entity.url == url && entity.expanded_url) {
            url = entity.expanded_url;
            text = entity.display_url;
            break;
          }
        }
        return ""+escapeHTML(text)+"";
      });
    }

    function parse_date(date_str) {
      // The non-search twitter APIs return inconsistently-formatted dates, which Date.parse
      // cannot handle in IE. We therefore perform the following transformation:
      // "Wed Apr 29 08:53:31 +0000 2009" => "Wed, Apr 29 2009 08:53:31 +0000"
      return Date.parse(date_str.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3'));
    }

    function relative_time(date) {
      var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
      var delta = parseInt((relative_to.getTime() - date) / 1000, 10);
      var r = '';
      if (delta < 1) {
        r = 'just now';
      } else if (delta < 60) {
        r = delta + ' seconds ago';
      } else if(delta < 120) {
        r = 'about a minute ago';
      } else if(delta < (45*60)) {
        r = 'about ' + (parseInt(delta / 60, 10)).toString() + ' minutes ago';
      } else if(delta < (2*60*60)) {
        r = 'about an hour ago';
      } else if(delta < (24*60*60)) {
        r = 'about ' + (parseInt(delta / 3600, 10)).toString() + ' hours ago';
      } else if(delta < (48*60*60)) {
        r = 'about a day ago';
      } else {
        r = 'about ' + (parseInt(delta / 86400, 10)).toString() + ' days ago';
      }
      return r;
    }

    function build_auto_join_text(text) {
      if (text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) {
        return s.auto_join_text_reply;
      } else if (text.match(url_regexp)) {
        return s.auto_join_text_url;
      } else if (text.match(/^((\w+ed)|just) .*/im)) {
        return s.auto_join_text_ed;
      } else if (text.match(/^(\w*ing) .*/i)) {
        return s.auto_join_text_ing;
      } else {
        return s.auto_join_text_default;
      }
    }

    function build_api_url() {
      var proto = ('https:' == document.location.protocol ? 'https:' : 'http:');
      var count = (s.fetch === null) ? s.count : s.fetch;
      var common_params = '&include_entities=1&callback=?';
      if (s.list) {
        return proto+"//"+s.twitter_api_url+"/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?page="+s.page+"&per_page="+count+common_params;
      } else if (s.favorites) {
        return proto+"//"+s.twitter_api_url+"/favorites/"+s.username[0]+".json?page="+s.page+"&count="+count+common_params;
      } else if (s.query === null && s.username.length == 1) {
        return proto+'//'+s.twitter_api_url+'/1/statuses/user_timeline.json?screen_name='+s.username[0]+'&count='+count+(s.retweets ? '&include_rts=1' : '')+'&page='+s.page+common_params;
      } else {
        var query = (s.query || 'from:'+s.username.join(' OR from:'));
        return proto+'//'+s.twitter_search_url+'/search.json?&q='+encodeURIComponent(query)+'&rpp='+count+'&page='+s.page+common_params;
      }
    }

    function extract_avatar_url(item, secure) {
      if (secure) {
        return ('user' in item) ?
          item.user.profile_image_url_https :
          extract_avatar_url(item, false).
            replace(/^http:\/\/[a-z0-9]{1,3}\.twimg\.com\//, "https://s3.amazonaws.com/twitter_production/");
      } else {
        return item.profile_image_url || item.user.profile_image_url;
      }
    }

    // Convert twitter API objects into data available for
    // constructing each tweet
 using a template
    function extract_template_data(item){
      var o = {};
      o.item = item;
      o.source = item.source;
      o.screen_name = item.from_user || item.user.screen_name;
      // The actual user name is not returned by all Twitter APIs, so please do not
      // file an issue if it is empty:
      o.name = item.from_user_name || item.user.name;
      o.avatar_size = s.avatar_size;
      o.avatar_url = extract_avatar_url(item, (document.location.protocol === 'https:'));
      o.retweet = typeof(item.retweeted_status) != 'undefined';
      o.tweet_time = parse_date(item.created_at);
      o.join_text = s.join_text == "auto" ? build_auto_join_text(item.text) : s.join_text;
      o.tweet_id = item.id_str;
      o.twitter_base = "http://"+s.twitter_url+"/";
      o.user_url = o.twitter_base+o.screen_name;
      o.tweet_url = o.user_url+"/status/"+o.tweet_id;
      o.reply_url = o.twitter_base+"intent/tweet?in_reply_to="+o.tweet_id;
      o.retweet_url = o.twitter_base+"intent/retweet?tweet_id="+o.tweet_id;
      o.favorite_url = o.twitter_base+"intent/favorite?tweet_id="+o.tweet_id;
      o.retweeted_screen_name = o.retweet && item.retweeted_status.user.screen_name;
      o.tweet_relative_time = relative_time(o.tweet_time);
      o.entities = item.entities ? (item.entities.urls || []).concat(item.entities.media || []) : [];
      o.tweet_raw_text = o.retweet ? ('RT @'+o.retweeted_screen_name+' '+item.retweeted_status.text) : item.text; // avoid '...' in long retweets
      o.tweet_text = $([linkURLs(o.tweet_raw_text, o.entities)]).linkUser().linkHash()[0];
      o.tweet_text_fancy = $([o.tweet_text]).makeHeart()[0];

      // Default spans, and pre-formatted blocks for common layouts
      o.user = t('{screen_name}', o);
      o.join = s.join_text ? t(' {join_text} ', o) : ' ';
      o.avatar = o.avatar_size ?
        t('', o) : '';
      o.time = t('{tweet_relative_time}', o);
      o.text = t('{tweet_text_fancy}', o);
      o.reply_action = t('reply', o);
      o.retweet_action = t('retweet', o);
      o.favorite_action = t('favorite', o);
      return o;
    }

    function load(widget) {
      var intro = '
'+s.intro_text+'
';
      var outro = '
'+s.outro_text+'
';
      var loading = $('
'+s.loading_text+'
');
      if (s.loading_text) $(widget).not(":has(.tweet_list)").empty().append(loading);
      $.getJSON(build_api_url(), function(data){
        var list = $('
');
        var tweets = $.map(data.results || data, extract_template_data);
        tweets = $.grep(tweets, s.filter).sort(s.comparator).slice(0, s.count);
        list.append($.map(tweets, function(o) { return "
" + t(s.template, o) + "
"; }).join('')).
          children('li:first').addClass('tweet_first').end().
          children('li:odd').addClass('tweet_even').end().
          children('li:even').addClass('tweet_odd');

        $(widget).empty().append(list);
        if (s.intro_text) list.before(intro);
        if (s.outro_text) list.after(outro);

        $(widget).trigger("loaded").trigger((tweets.length === 0 ? "empty" : "full"));
        if (s.refresh_interval) {
          window.setTimeout(function() { $(widget).trigger("tweet:load"); }, 1000 * s.refresh_interval);
        }
      });
    }

    return this.each(function(i, widget){
      if(s.username && typeof(s.username) == "string"){
        s.username = [s.username];
      }

      $(widget).unbind("tweet:load").bind("tweet:load", function(){
        load(widget);
      }).trigger("tweet:load");
    });
  };
}));
<|endoftext|>


<commit_before>var path = require('path');
var _ = require('lodash');

function ManifestPlugin(opts) {
  this.opts = _.assign({
    basePath: '',
    fileName: 'manifest.json',
    stripSrc: null,
    transformExtensions: /^(gz|map)$/i,
    cache: null
  }, opts || {});
}

ManifestPlugin.prototype.getFileType = function(str) {
  str = str.replace(/\?.*/, '');
  var split = str.split('.');
  var ext = split.pop();
  if (this.opts.transformExtensions.test(ext)) {
    ext = split.pop() + '.' + ext;
  }
  return ext;
};

ManifestPlugin.prototype.apply = function(compiler) {
  var outputName = this.opts.fileName;
  var cache = this.opts.cache || {};
  var moduleAssets = {};

  compiler.plugin("compilation", function (compilation) {
    compilation.plugin('module-asset', function (module, file) {
      moduleAssets[file] = path.join(
          path.dirname(file),
          path.basename(module.userRequest)
      );
    });
  });

  compiler.plugin('emit', function(compilation, compileCallback){
    var stats = compilation.getStats().toJson();
    var manifest = {};

    _.merge(cache, compilation.chunks.reduce(function(memo, chunk){
      var chunkName = chunk.name.replace(this.opts.stripSrc, '');

      return chunk.files.reduce(function(memo, file){
        memo[chunkName + '.' + this.getFileType(file)] = file;
        return memo;
      }.bind(this), memo);
    }.bind(this), {}));

    // module assets don't show up in assetsByChunkName.
    // we're getting them this way;
    _.merge(cache, stats.assets.reduce(function(memo, asset){
      var name = moduleAssets[asset.name];
      if (name) {
        memo[name] = asset.name;
      }
      return memo;
    }, {}));

    // Append optional basepath onto all references.
    // This allows output path to be reflected in the manifest.
    if (this.opts.basePath) {
      cache = _.reduce(cache, function(memo, value, key) {
        memo[this.opts.basePath + key] = this.opts.basePath + value;
        return memo;
      }.bind(this), {});
    }

    Object.keys(cache).sort().forEach(function (key) {
      manifest[key] = cache[key];
    });

    var json = JSON.stringify(manifest, null, 2);

    compilation.assets[outputName] = {
      source: function() {
        return json;
      },
      size: function() {
        return json.length;
      }
    };

    compileCallback()

  }.bind(this));
};

module.exports = ManifestPlugin;
<commit_msg>nameless chunks are ignored.<commit_after>var path = require('path');
var _ = require('lodash');

function ManifestPlugin(opts) {
  this.opts = _.assign({
    basePath: '',
    fileName: 'manifest.json',
    stripSrc: null,
    transformExtensions: /^(gz|map)$/i,
    cache: null
  }, opts || {});
}

ManifestPlugin.prototype.getFileType = function(str) {
  str = str.replace(/\?.*/, '');
  var split = str.split('.');
  var ext = split.pop();
  if (this.opts.transformExtensions.test(ext)) {
    ext = split.pop() + '.' + ext;
  }
  return ext;
};

ManifestPlugin.prototype.apply = function(compiler) {
  var outputName = this.opts.fileName;
  var cache = this.opts.cache || {};
  var moduleAssets = {};

  compiler.plugin("compilation", function (compilation) {
    compilation.plugin('module-asset', function (module, file) {
      moduleAssets[file] = path.join(
          path.dirname(file),
          path.basename(module.userRequest)
      );
    });
  });

  compiler.plugin('emit', function(compilation, compileCallback){
    var stats = compilation.getStats().toJson();
    var manifest = {};

    _.merge(cache, compilation.chunks.reduce(function(memo, chunk){
      if (!chunk.name) return memo;
      var chunkName = chunk.name.replace(this.opts.stripSrc, '');

      return chunk.files.reduce(function(memo, file){
        memo[chunkName + '.' + this.getFileType(file)] = file;
        return memo;
      }.bind(this), memo);
    }.bind(this), {}));

    // module assets don't show up in assetsByChunkName.
    // we're getting them this way;
    _.merge(cache, stats.assets.reduce(function(memo, asset){
      var name = moduleAssets[asset.name];
      if (name) {
        memo[name] = asset.name;
      }
      return memo;
    }, {}));

    // Append optional basepath onto all references.
    // This allows output path to be reflected in the manifest.
    if (this.opts.basePath) {
      cache = _.reduce(cache, function(memo, value, key) {
        memo[this.opts.basePath + key] = this.opts.basePath + value;
        return memo;
      }.bind(this), {});
    }

    Object.keys(cache).sort().forEach(function (key) {
      manifest[key] = cache[key];
    });

    var json = JSON.stringify(manifest, null, 2);

    compilation.assets[outputName] = {
      source: function() {
        return json;
      },
      size: function() {
        return json.length;
      }
    };

    compileCallback()

  }.bind(this));
};

module.exports = ManifestPlugin;
<|endoftext|>


<commit_before>var util = require('../../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var extend = require('util')._extend;
var DNS_CACHE = {};
var DNS_CACHE_TIME = 60000;
var allowDnsCache = true;

var rules, hosts, heads, req, res, proxy, weinre;
rules = hosts = heads = req = res = proxy = weinre = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	weinre = [];
	proxy = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^https?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: isRegExp ? null : util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw,
			rawPattern: pattern
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^proxy:\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	var host = getRule(_url, hosts);
	if (host) {
		return callback(null, host.matcher);
	}

	var hostname = options.hostname;
	if (allowDnsCache) {
		var dnsCache = DNS_CACHE[hostname];
		if (dnsCache) {
			if (Date.now() - dnsCache.time < DNS_CACHE_TIME) {
				callback(null, dnsCache.ip);
				return;
			}
			DNS_CACHE[hostname] = null;
		}
	}

	try {
		dns.lookup(options.hostname, function(err, ip, addressType) {
			if (err) {
				callback(err);
				return;
			}

			ip = ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1');
			if (allowDnsCache) {
				DNS_CACHE[hostname] = {
						ip: ip,
						time: Date.now()
				};
			}
			callback(null, ip);
		  });
	} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
		callback(err);
	}
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, _rules) {
	var rules = _getRule(url, _rules);
	//支持域名匹配
	if (!rules && (rules = _getRule(url.replace(/^(https?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), _rules))
			&& util.removeProtocol(rules.rawPattern, true).indexOf('/') != -1) {
		rules = null;
	}
	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}
	var hasQueryString = url.indexOf('?') != -1;
	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (hasQueryString && url.indexOf(pattern) === 0) {
				var matcher = setProtocol(rule.matcher, url);
				return extend({
					url: matcher.indexOf('?') == -1 ? matcher : matcher + url.substring(pattern.length);
				}, rule);
			}

			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					return extend({
						url: join(setProtocol(rule.matcher, url), url.substring(len))
					}, rule);
				}
			}
		}
	}
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}

	return rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<commit_msg>supports querystring<commit_after>var util = require('../../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var extend = require('util')._extend;
var DNS_CACHE = {};
var DNS_CACHE_TIME = 60000;
var allowDnsCache = true;

var rules, hosts, heads, req, res, proxy, weinre;
rules = hosts = heads = req = res = proxy = weinre = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	weinre = [];
	proxy = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^https?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: isRegExp ? null : util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw,
			rawPattern: pattern
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^proxy:\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	var host = getRule(_url, hosts);
	if (host) {
		return callback(null, host.matcher);
	}

	var hostname = options.hostname;
	if (allowDnsCache) {
		var dnsCache = DNS_CACHE[hostname];
		if (dnsCache) {
			if (Date.now() - dnsCache.time < DNS_CACHE_TIME) {
				callback(null, dnsCache.ip);
				return;
			}
			DNS_CACHE[hostname] = null;
		}
	}

	try {
		dns.lookup(options.hostname, function(err, ip, addressType) {
			if (err) {
				callback(err);
				return;
			}

			ip = ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1');
			if (allowDnsCache) {
				DNS_CACHE[hostname] = {
						ip: ip,
						time: Date.now()
				};
			}
			callback(null, ip);
		  });
	} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
		callback(err);
	}
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, _rules) {
	var rules = _getRule(url, _rules);
	//支持域名匹配
	if (!rules && (rules = _getRule(url.replace(/^(https?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), _rules))
			&& util.removeProtocol(rules.rawPattern, true).indexOf('/') != -1) {
		rules = null;
	}
	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}
	var hasQueryString = url.indexOf('?') != -1;
	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (hasQueryString && url.indexOf(pattern) === 0) {
				var matcher = setProtocol(rule.matcher, url);
				return extend({
					url: matcher.indexOf('?') == -1 ? matcher : matcher + url.substring(pattern.length)
				}, rule);
			}

			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					return extend({
						url: join(setProtocol(rule.matcher, url), url.substring(len))
					}, rule);
				}
			}
		}
	}
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}

	return rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import EventMessage from '../../components/EventMessage';

const EventMessageContainer = ({ messages, clearMessages }) => (

        {messages.map(message =>
            (<EventMessage
                key={message.type}
                type={message.type}
                message={message.text}
                clearMessages={clearMessages}
            />)
        )}

);

const mapStateToProps = ({ eventMessage }) => ({
    messages: Object.keys(eventMessage).filter(key => eventMessage[key].active === true).map(key => eventMessage[key])
});

const mapDispatchToProps = dispatch => ({
    clearMessages: () => dispatch({ type: 'EVENT_MESSAGE_CLEAR' })
});

EventMessageContainer.propTypes = {
    messages: PropTypes.array.isRequired,
    clearMessages: PropTypes.func.isRequired
};

export default connect(mapStateToProps, mapDispatchToProps)(EventMessageContainer);
<commit_msg>Better keys for event messages<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import EventMessage from '../../components/EventMessage';

const EventMessageContainer = ({ messages, clearMessages }) => (

        {messages.map(message =>
            (<EventMessage
                key={`${message.type}-${message.key}`}
                type={message.type}
                message={message.text}
                clearMessages={clearMessages}
            />)
        )}

);

const mapStateToProps = ({ eventMessage }) => ({
    messages: Object.keys(eventMessage)
        .filter(key => eventMessage[key].active === true)
        .map(key => Object.assign({ key }, eventMessage[key]))
});

const mapDispatchToProps = dispatch => ({
    clearMessages: () => dispatch({ type: 'EVENT_MESSAGE_CLEAR' })
});

EventMessageContainer.propTypes = {
    messages: PropTypes.array.isRequired,
    clearMessages: PropTypes.func.isRequired
};

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


<commit_before>var config = require('../config'),
  moment = require('moment-timezone'),
  fetch = require('node-fetch')

function Prices() {
  this.request = fetch
  return this;
}

Prices.prototype.at = function(opts, callback) {
  opts.date = opts.date ? moment.tz(opts.date, config.dateFormats, config.timezone) : moment.tz(config.timezone)
  opts.url = config.priceUrlHourly
  opts.maxRange = {hours: 1}
  this.getValues(opts, function(error, results) {
    if (error) {
      callback(error)
      return
    }
    for (let i=0; i<results.length; i++) {
      let date = results[i].date
      if (date.get('dayOfYear') == opts.date.get('dayOfYear') &&
          date.get('hour') == opts.date.get('hour')) {
        callback(null, results[i])
        return
      }
    }
    callback(new Error('No results found for ' + opts.date.format()))
  })
}

Prices.prototype.hourly = function(opts, callback) {
  opts.url = config.priceUrlHourly
  opts.maxRange = {days: 1}
  this.getValues(opts, callback)
}

Prices.prototype.daily = function(opts, callback) {
  opts.url = config.priceUrlDaily
  opts.maxRange = {months: 1}
  this.getValues(opts, callback)
}

Prices.prototype.weekly = function(opts, callback) {
  opts.url = config.priceUrlWeekly
  opts.maxRange = {weeks: 24}
  this.getValues(opts, callback)
}

Prices.prototype.monthly = function(opts, callback) {
  opts.url = config.priceUrlMonthly
  opts.maxRange = {months: 53}
  this.getValues(opts, callback)
}

Prices.prototype.yearly = function(opts, callback) {
  opts.url = config.priceUrlYearly
  this.getValues(opts, callback)
}

Prices.prototype.getValues = async function(opts, callback) {
  if (!opts) {
    opts = {}
  }
  fromTime = opts.from ? moment.tz(opts.from, config.timeFormats, config.timezone) : null;
  toTime = opts.to ? moment.tz(opts.to, config.timeFormats, config.timezone) : null;
  if (fromTime && toTime && opts.maxRange) {
    minFromTime = moment(toTime).subtract(opts.maxRange);
    if (fromTime.isBefore(minFromTime)) {
      console.warn('Time span too long. Setting start time to ' + minFromTime.format())
      fromTime = minFromTime
    }
  }
  if (fromTime && !toTime && !opts.date && opts.maxRange) {
    opts.date = moment(fromTime).add(opts.maxRange)
  }
  else if ((fromTime || toTime) && !opts.date) {
    opts.date = toTime ? toTime : fromTime
  }
  opts.area = opts.area || null
  opts.currency = opts.currency || 'EUR'
  opts.date = opts.date || moment()
  var url = opts.url +
    '?currency=,' + opts.currency + ',' + opts.currency + ',' + opts.currency +
    '&endDate=' + moment(opts.date, config.dateFormats).format("DD-MM-YYYY")
  // console.log(url)
  var data
  try {
    data = (await this.request(url).then(res => res.json())).data
  } catch (error) {
      console.trace(error)
      callback(error)
  }
    // console.log(JSON.stringify(data, null, 2))
    if (data && data.Rows && data.Rows.length) {
      var values = []
      for (var i=0; i<data.Rows.length; i++) {
        var row = data.Rows[i]
        if (row.IsExtraRow) {
          continue
        }
        // intentionally create new moment object for each row (reference returned)
        var date = moment.tz(row.StartTime, "YYYY-MM-DD\Thh:mm:ss", config.timezone)
        if (!date.isValid()) {
          // console.log('invalid date ' + row.StartTime)
          continue
        }
        else if ((fromTime && date.isBefore(fromTime)) ||
                 (toTime && date.isAfter(toTime))) {
          // console.log('date out of given range ' + date.format(), fromTime, toTime)
          continue
        }
        for (var j=0; j<row.Columns.length; j++) {
          var column = row.Columns[j]
          var value = parseFloat(column.Value.replace(/,/, '.').replace(/ /g, ''))
          if (isNaN(value)) {
            // console.log('invalid value ' + column.Value)
            continue
          }
          var area = column.Name
          // console.log(JSON.stringify(column, null, 2))
          if (!opts.area || (area == opts.area) || (opts.area.indexOf && (opts.area.indexOf(area) >= 0))) {
            values.push({area: area, date: date, value: value})
          }
        }
      }
      // console.log(JSON.stringify(values, null, 2))
      callback(null, values)
    }
}

module.exports = Prices
<commit_msg>Import as constant values recommended<commit_after>const config = require('../config')
const moment = require('moment-timezone')
const fetch = require('node-fetch')

function Prices() {
  this.request = fetch
  return this;
}

Prices.prototype.at = function(opts, callback) {
  opts.date = opts.date ? moment.tz(opts.date, config.dateFormats, config.timezone) : moment.tz(config.timezone)
  opts.url = config.priceUrlHourly
  opts.maxRange = {hours: 1}
  this.getValues(opts, function(error, results) {
    if (error) {
      callback(error)
      return
    }
    for (let i=0; i<results.length; i++) {
      let date = results[i].date
      if (date.get('dayOfYear') == opts.date.get('dayOfYear') &&
          date.get('hour') == opts.date.get('hour')) {
        callback(null, results[i])
        return
      }
    }
    callback(new Error('No results found for ' + opts.date.format()))
  })
}

Prices.prototype.hourly = function(opts, callback) {
  opts.url = config.priceUrlHourly
  opts.maxRange = {days: 1}
  this.getValues(opts, callback)
}

Prices.prototype.daily = function(opts, callback) {
  opts.url = config.priceUrlDaily
  opts.maxRange = {months: 1}
  this.getValues(opts, callback)
}

Prices.prototype.weekly = function(opts, callback) {
  opts.url = config.priceUrlWeekly
  opts.maxRange = {weeks: 24}
  this.getValues(opts, callback)
}

Prices.prototype.monthly = function(opts, callback) {
  opts.url = config.priceUrlMonthly
  opts.maxRange = {months: 53}
  this.getValues(opts, callback)
}

Prices.prototype.yearly = function(opts, callback) {
  opts.url = config.priceUrlYearly
  this.getValues(opts, callback)
}

Prices.prototype.getValues = async function(opts, callback) {
  if (!opts) {
    opts = {}
  }
  fromTime = opts.from ? moment.tz(opts.from, config.timeFormats, config.timezone) : null;
  toTime = opts.to ? moment.tz(opts.to, config.timeFormats, config.timezone) : null;
  if (fromTime && toTime && opts.maxRange) {
    minFromTime = moment(toTime).subtract(opts.maxRange);
    if (fromTime.isBefore(minFromTime)) {
      console.warn('Time span too long. Setting start time to ' + minFromTime.format())
      fromTime = minFromTime
    }
  }
  if (fromTime && !toTime && !opts.date && opts.maxRange) {
    opts.date = moment(fromTime).add(opts.maxRange)
  }
  else if ((fromTime || toTime) && !opts.date) {
    opts.date = toTime ? toTime : fromTime
  }
  opts.area = opts.area || null
  opts.currency = opts.currency || 'EUR'
  opts.date = opts.date || moment()
  var url = opts.url +
    '?currency=,' + opts.currency + ',' + opts.currency + ',' + opts.currency +
    '&endDate=' + moment(opts.date, config.dateFormats).format("DD-MM-YYYY")
  // console.log(url)
  var data
  try {
    data = (await this.request(url).then(res => res.json())).data
  } catch (error) {
      console.trace(error)
      callback(error)
  }
    // console.log(JSON.stringify(data, null, 2))
    if (data && data.Rows && data.Rows.length) {
      var values = []
      for (var i=0; i<data.Rows.length; i++) {
        var row = data.Rows[i]
        if (row.IsExtraRow) {
          continue
        }
        // intentionally create new moment object for each row (reference returned)
        var date = moment.tz(row.StartTime, "YYYY-MM-DD\Thh:mm:ss", config.timezone)
        if (!date.isValid()) {
          // console.log('invalid date ' + row.StartTime)
          continue
        }
        else if ((fromTime && date.isBefore(fromTime)) ||
                 (toTime && date.isAfter(toTime))) {
          // console.log('date out of given range ' + date.format(), fromTime, toTime)
          continue
        }
        for (var j=0; j<row.Columns.length; j++) {
          var column = row.Columns[j]
          var value = parseFloat(column.Value.replace(/,/, '.').replace(/ /g, ''))
          if (isNaN(value)) {
            // console.log('invalid value ' + column.Value)
            continue
          }
          var area = column.Name
          // console.log(JSON.stringify(column, null, 2))
          if (!opts.area || (area == opts.area) || (opts.area.indexOf && (opts.area.indexOf(area) >= 0))) {
            values.push({area: area, date: date, value: value})
          }
        }
      }
      // console.log(JSON.stringify(values, null, 2))
      callback(null, values)
    }
}

module.exports = Prices
<|endoftext|>


<commit_before>// ==UserScript==
// @name  Nest Thermostat Vacation Saver UI
// @version 1.0
// @include https://home.nest.com/*
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==

$(document).ready(function() {
    var html = '
' +
      '
' +
      'You have saved $ from turning off your HVAC during your vacation! ' +
      'Now go treat yourself!' +
      '

';

    var interval = setInterval(function () {
        if ($(".add-device-deck-item").length) {
            clearInterval(interval);
            $(html).insertBefore(".add-device-deck-item");
        }
    });
});
<commit_msg>Cost is properly inserted into document-- used placeholder value for now<commit_after>// ==UserScript==
// @name         Nest Thermostat Vacation Saver UI
// @version      1.0
// @include      https://home.nest.com/*
// @require      http://code.jquery.com/jquery-latest.js
// ==/UserScript==

$(document).ready(function() {
    var cost = 100;
    var html = '
' +
        '
' +
        'You have saved $' + cost + ' from turning off your HVAC during your vacation! ' +
        'Now go treat yourself!' +
        '

';

    var interval = setInterval(function () {
        if ($(".add-device-deck-item").length) {
            clearInterval(interval);
            $(html).insertBefore(".add-device-deck-item");
        }
    });
});
<|endoftext|>


<commit_before>

var endpoint = "http://localhost:3000/sample/v01/event",
    appToken = null;

var Sample = {
    setEndpoint: function(newEndpoint) {
      endpoint = newEndpoint;
    },

    getEndpoint: function() {
      return endpoint;
    },

    setAppToken: function(newAppToken) {
      appToken = newAppToken;
    },

    track: function(eventName, params) {
      params.event_name = eventName;
      params.app_token = appToken;

      connector.add(params, function() { });
    }
};
<commit_msg>intentional error for testing jenkins<commit_after>

var endpoint = "http://localhost:3000/sample/v01/event",
    appToken = null;

var Sample = {
    setEndpoint: function(newEndpoint) {
      endpoint = newEndpoint;
    },

    getEndpoint: function() {
      return 1;
    },

    setAppToken: function(newAppToken) {
      appToken = newAppToken;
    },

    track: function(eventName, params) {
      params.event_name = eventName;
      params.app_token = appToken;

      connector.add(params, function() { });
    }
};
<|endoftext|>