<commit_before>define(function (require) {
  "use strict";

  var InstanceConstants = require('constants/InstanceConstants'),
      ProjectInstanceConstants = require('constants/ProjectInstanceConstants'),
      ProjectConstants = require('constants/ProjectConstants'),
      Instance = require('models/Instance'),
      Project = require('models/Project'),
      Router = require('Router'),
      actions = require('actions'),
      stores = require('stores'),
      ModalHelpers = require('components/modals/ModalHelpers'),
      InstanceLaunchModal = require('components/modals/instance/InstanceLaunchModal.react'),
      Utils = require('../Utils');

  var createProjectAndLaunchInstance = function(params){
    if(!params.projectName) throw new Error("Missing projectName");

    var projectName = params.projectName,
        project = new Project({
          name: projectName,
          description: projectName,
          instances: [],
          volumes:[]
        });

    Utils.dispatch(ProjectConstants.ADD_PROJECT, {project: project});

    project.save().done(function(){
      Utils.dispatch(ProjectConstants.UPDATE_PROJECT, {project: project});

      // launch the instance into the project
      params.project = project;
      delete params['projectName'];
      launchInstance(params);

      // Since this is triggered from the images page, navigate off
      // that page and back to the instance list so the user can see
      // their instance being created
      Router.getInstance().transitionTo("project-resources", {projectId: project.id});
    }).fail(function(response){
      Utils.dispatch(ProjectConstants.REMOVE_PROJECT, {project: project});
      Utils.displayError({title: "Project could not be created", response: response});
    });
  };

  var launchInstance = function(params){
    if(!params.project) throw new Error("Missing project");
    if(!params.instanceName) throw new Error("Missing instanceName");
    if(!params.identity) throw new Error("Missing identity");
    if(!params.size) throw new Error("Missing size");
    if(!params.machine) throw new Error("Missing machine");

    var project = params.project,
        instanceName = params.instanceName,
        identity = params.identity,
        size = params.size,
        machine = params.machine;

    var instance = new Instance({
      name: instanceName,
      size: {
        id: size.id,
        alias: size.get('alias')
      },
      status: "build - scheduling",
      provider: {
        id: identity.get('provider').id,
        uuid: identity.get('provider').uuid
      },
      identity: {
        id: identity.id,
        uuid: identity.get('uuid')
      }
    }, {parse: true});

    Utils.dispatch(InstanceConstants.ADD_INSTANCE, {instance: instance});
    // todo: hook this back up if experience seems to slow...not connected right now
    // Utils.dispatch(ProjectInstanceConstants.ADD_PENDING_INSTANCE_TO_PROJECT, {
    //   instance: instance,
    //   project: project
    // });

    instance.createOnV1Endpoint({
      name: instanceName,
      size_alias: size.get('alias'),
      machine_alias: machine.get('uuid')
    }).done(function(attrs, status, response) {
      instance.set('id', attrs.id);
      instance.fetch().done(function(){
        // todo: remove hack and start using ProjectInstance endpoint to discover
        // which project an instance is in
        instance.set('projects', [project.id]);

        Utils.dispatch(InstanceConstants.UPDATE_INSTANCE, {instance: instance});
        Utils.dispatch(InstanceConstants.POLL_INSTANCE, {instance: instance});

        // todo: hook this back up if experience seems to slow...not connected right now
        // Utils.dispatch(ProjectInstanceConstants.REMOVE_PENDING_INSTANCE_FROM_PROJECT, {
        //   instance: instance,
        //   project: project
        // });

        actions.ProjectInstanceActions.addInstanceToProject(instance, project);
      });
    }).fail(function (response) {
      Utils.dispatch(InstanceConstants.REMOVE_INSTANCE, {instance: instance});
      Utils.displayError({title: "Instance could not be launched", response: response});

      // todo: hook this back up if experience seems to slow...not connected right now
      // Utils.dispatch(ProjectInstanceConstants.REMOVE_PENDING_INSTANCE_FROM_PROJECT, {
      //   instance: instance,
      //   project: project
      // });
    });

    // Since this is triggered from the images page, navigate off
    // that page and back to the instance list so the user can see
    // their instance being created
    Router.getInstance().transitionTo("project-resources", {projectId: project.id});
  };

  return {

    launch: function(application){
      var modal = InstanceLaunchModal({
        application: application
      });

      ModalHelpers.renderModal(modal, function (identity, machineId, sizeId, instanceName, project) {
        var size = stores.SizeStore.get(sizeId),
            machine = application.get('machines').get(machineId);

        if(typeof project === "string"){
          createProjectAndLaunchInstance({
            projectName: project,
            instanceName: instanceName,
            identity: identity,
            size: size,
            machine: machine
          });
        }else{
          launchInstance({
            project: project,
            instanceName: instanceName,
            identity: identity,
            size: size,
            machine: machine
          });
        }
      })
    },

    launchIntoProject: launchInstance
  };

});
<commit_msg>can put instances in project again after launch<commit_after>define(function (require) {
  "use strict";

  var InstanceConstants = require('constants/InstanceConstants'),
      ProjectInstanceConstants = require('constants/ProjectInstanceConstants'),
      ProjectConstants = require('constants/ProjectConstants'),
      Instance = require('models/Instance'),
      Project = require('models/Project'),
      Router = require('Router'),
      actions = require('actions'),
      stores = require('stores'),
      ModalHelpers = require('components/modals/ModalHelpers'),
      InstanceLaunchModal = require('components/modals/instance/InstanceLaunchModal.react'),
      Utils = require('../Utils');

  var createProjectAndLaunchInstance = function(params){
    if(!params.projectName) throw new Error("Missing projectName");

    var projectName = params.projectName,
        project = new Project({
          name: projectName,
          description: projectName,
          instances: [],
          volumes:[]
        });

    Utils.dispatch(ProjectConstants.ADD_PROJECT, {project: project});

    project.save().done(function(){
      Utils.dispatch(ProjectConstants.UPDATE_PROJECT, {project: project});

      // launch the instance into the project
      params.project = project;
      delete params['projectName'];
      launchInstance(params);

      // Since this is triggered from the images page, navigate off
      // that page and back to the instance list so the user can see
      // their instance being created
      Router.getInstance().transitionTo("project-resources", {projectId: project.id});
    }).fail(function(response){
      Utils.dispatch(ProjectConstants.REMOVE_PROJECT, {project: project});
      Utils.displayError({title: "Project could not be created", response: response});
    });
  };

  var launchInstance = function(params){
    if(!params.project) throw new Error("Missing project");
    if(!params.instanceName) throw new Error("Missing instanceName");
    if(!params.identity) throw new Error("Missing identity");
    if(!params.size) throw new Error("Missing size");
    if(!params.machine) throw new Error("Missing machine");

    var project = params.project,
        instanceName = params.instanceName,
        identity = params.identity,
        size = params.size,
        machine = params.machine;

    var instance = new Instance({
      name: instanceName,
      size: {
        id: size.id,
        alias: size.get('alias')
      },
      status: "build - scheduling",
      provider: {
        id: identity.get('provider').id,
        uuid: identity.get('provider').uuid
      },
      identity: {
        id: identity.id,
        uuid: identity.get('uuid')
      }
    }, {parse: true});

    Utils.dispatch(InstanceConstants.ADD_INSTANCE, {instance: instance});
    // todo: hook this back up if experience seems to slow...not connected right now
    // Utils.dispatch(ProjectInstanceConstants.ADD_PENDING_INSTANCE_TO_PROJECT, {
    //   instance: instance,
    //   project: project
    // });

    instance.createOnV1Endpoint({
      name: instanceName,
      size_alias: size.get('alias'),
      machine_alias: machine.get('uuid')
    }).done(function(attrs, status, response) {
      instance.set('id', attrs.id);
      instance.fetch().done(function(){
        // todo: remove hack and start using ProjectInstance endpoint to discover
        // which project an instance is in
        instance.set('projects', [project.id]);

        Utils.dispatch(InstanceConstants.UPDATE_INSTANCE, {instance: instance});
        Utils.dispatch(InstanceConstants.POLL_INSTANCE, {instance: instance});

        // todo: hook this back up if experience seems to slow...not connected right now
        // Utils.dispatch(ProjectInstanceConstants.REMOVE_PENDING_INSTANCE_FROM_PROJECT, {
        //   instance: instance,
        //   project: project
        // });

        actions.ProjectInstanceActions.addInstanceToProject({
          project: project,
          instance: instance
        });
      });
    }).fail(function (response) {
      Utils.dispatch(InstanceConstants.REMOVE_INSTANCE, {instance: instance});
      Utils.displayError({title: "Instance could not be launched", response: response});

      // todo: hook this back up if experience seems to slow...not connected right now
      // Utils.dispatch(ProjectInstanceConstants.REMOVE_PENDING_INSTANCE_FROM_PROJECT, {
      //   instance: instance,
      //   project: project
      // });
    });

    // Since this is triggered from the images page, navigate off
    // that page and back to the instance list so the user can see
    // their instance being created
    Router.getInstance().transitionTo("project-resources", {projectId: project.id});
  };

  return {

    launch: function(application){
      var modal = InstanceLaunchModal({
        application: application
      });

      ModalHelpers.renderModal(modal, function (identity, machineId, sizeId, instanceName, project) {
        var size = stores.SizeStore.get(sizeId),
            machine = application.get('machines').get(machineId);

        if(typeof project === "string"){
          createProjectAndLaunchInstance({
            projectName: project,
            instanceName: instanceName,
            identity: identity,
            size: size,
            machine: machine
          });
        }else{
          launchInstance({
            project: project,
            instanceName: instanceName,
            identity: identity,
            size: size,
            machine: machine
          });
        }
      })
    },

    launchIntoProject: launchInstance
  };

});
<|endoftext|>


<commit_before>(function() {
    var m = angular.module('parkandride.hubMap', [
        'parkandride.MapService',
        'parkandride.FacilityResource'
    ]);

    m.factory("HubMapCommon", function(MapService) {
        return {
            setPoint: function (point, layer) {
                point.transform(MapService.targetCRS, MapService.mapCRS);
                var feature = new ol.Feature(point);
                var source = layer.getSource();
                source.clear();
                source.addFeature(feature);
            }
        };
    });

    m.directive('editHubMap', function(MapService, HubMapCommon, FacilityResource) {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                hub: '=ngModel',
                facilities: '=',
                noTiles: '@'
            },
            template: '

',
            transclude: false,
            link: function(scope, element, attrs, ctrl) {
                var mapCRS = MapService.mapCRS;
                var targetCRS = MapService.targetCRS;

                var facilitiesLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.facilityStyle
                });

                var hubLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.hubStyle
                });

                var map = MapService.createMap(element, {
                    layers: [ facilitiesLayer, hubLayer ],
                    readOnly: false,
                    noTiles: attrs.noTiles === "true" });

                var view = map.getView();

                function addFeatureAsFacility(feature) {
                    var facility = feature.getProperties();
                    facility.id = feature.getId();
                    var indx = _.sortedIndex(scope.facilities, facility, function(f) { return f._index; });
                    scope.facilities.splice(indx, 0, facility);
                }

                if (scope.hub.location) {
                    var point = new ol.format.GeoJSON().readGeometry(scope.hub.location);
                    HubMapCommon.setPoint(point, hubLayer);
                    view.setCenter(point.getCoordinates());
                    view.setZoom(14);
                } else {
                    ctrl.$setValidity("required", false);
                }

                map.on('dblclick', function(event) {
                    var point = new ol.geom.Point(event.coordinate).transform(mapCRS, targetCRS);
                    scope.hub.location = new ol.format.GeoJSON().writeGeometry(point);
                    ctrl.$setValidity("required", true);
                    ctrl.$setTouched();
                    scope.$apply();
                    HubMapCommon.setPoint(point, hubLayer);
                    return false;
                });

                var selectFeatures = new ol.interaction.Select({
                    toggleCondition: function() { return true; },
                    style: MapService.selectedFacilityStyle,
                    layers: [ facilitiesLayer ]
                });
                map.addInteraction(selectFeatures);
                var selectedFeatures = selectFeatures.getFeatures();

                FacilityResource.findFacilitiesAsFeatureCollection().then(function(geojson) {
                    var features = new ol.format.GeoJSON().readFeatures(geojson);
                    var extent = hubLayer.getSource().getExtent();

                    _.forEach(features, function (feature) {
                        feature.getGeometry().transform(targetCRS, mapCRS);
                        facilitiesLayer.getSource().addFeature(feature);
                        if (_.contains(scope.hub.facilityIds, feature.getId())) {
                            selectedFeatures.push(feature);
                            addFeatureAsFacility(feature);
                            extent = ol.extent.extend(extent, feature.getGeometry().getExtent());
                        }
                    });
                    if (!_.isEmpty(scope.hub.facilityIds)) {
                        view.fitExtent(extent, map.getSize());
                    }
                    selectedFeatures.on("add", function (collectionEvent) {
                        var feature = collectionEvent.element;
                        scope.hub.facilityIds.push(feature.getId());
                        addFeatureAsFacility(feature);
                        scope.$apply();
                        return true;
                    });
                    selectedFeatures.on("remove", function (collectionEvent) {
                        var facilityId = collectionEvent.element.getId();
                        scope.hub.facilityIds = _.without(scope.hub.facilityIds, facilityId);
                        var index = _.findIndex(scope.facilities, function(facility) { return facility.id === facilityId; });
                        if (index >= 0) {
                            scope.facilities.splice(index, 1);
                        }
                        scope.$apply();
                        return true;
                    });
                });
            }
        };
    });

    m.directive('viewHubMap', function(MapService, HubMapCommon, FacilityResource) {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                hub: '=ngModel',
                editable: '@',
                noTiles: '@'
            },
            template: '

',
            transclude: false,
            link: function(scope, element, attrs, ctrl) {
                var mapCRS = MapService.mapCRS;
                var targetCRS = MapService.targetCRS;

                var facilitiesLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.selectedFacilityStyle
                });

                var width = 3;
                var hubLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.hubStyle
                });

                var map = MapService.createMap(element, { layers: [ facilitiesLayer, hubLayer ], readOnly: true, noTiles: attrs.noTiles === "true"});
                var view = map.getView();

                var point = new ol.format.GeoJSON().readGeometry(scope.hub.location);
                HubMapCommon.setPoint(point, hubLayer);
                view.setCenter(point.getCoordinates());
                view.setZoom(14);

                if (!_.isEmpty(scope.hub.facilityIds)) {
                    FacilityResource.findFacilitiesAsFeatureCollection({ ids: scope.hub.facilityIds }).then(function(geojson) {
                        var features = new ol.format.GeoJSON().readFeatures(geojson);
                        _.forEach(features, function (feature) {
                            feature.getGeometry().transform(targetCRS, mapCRS);
                            facilitiesLayer.getSource().addFeature(feature);
                        });
                        var extent = ol.extent.extend(hubLayer.getSource().getExtent(), facilitiesLayer.getSource().getExtent());
                        if (!ol.extent.isEmpty(extent)) {
                            view.fitExtent(extent, map.getSize());
                        }
                    });
                }
                if (console.log) {
                    map.on('dblclick', function(event) {
                        console.log("{x: "+ event.pixel[0] + ", y: " + event.pixel[1] + "}");
                        return true;
                    });
                }
            }
        };
    });
})();<commit_msg>refactor(hub-map): removeFacility function<commit_after>(function() {
    var m = angular.module('parkandride.hubMap', [
        'parkandride.MapService',
        'parkandride.FacilityResource'
    ]);

    m.factory("HubMapCommon", function(MapService) {
        return {
            setPoint: function (point, layer) {
                point.transform(MapService.targetCRS, MapService.mapCRS);
                var feature = new ol.Feature(point);
                var source = layer.getSource();
                source.clear();
                source.addFeature(feature);
            }
        };
    });

    m.directive('editHubMap', function(MapService, HubMapCommon, FacilityResource) {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                hub: '=ngModel',
                facilities: '=',
                noTiles: '@'
            },
            template: '

',
            transclude: false,
            link: function(scope, element, attrs, ctrl) {
                var mapCRS = MapService.mapCRS;
                var targetCRS = MapService.targetCRS;

                var facilitiesLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.facilityStyle
                });

                var hubLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.hubStyle
                });

                var map = MapService.createMap(element, {
                    layers: [ facilitiesLayer, hubLayer ],
                    readOnly: false,
                    noTiles: attrs.noTiles === "true" });

                var view = map.getView();

                function addFeatureAsFacility(feature) {
                    var facility = feature.getProperties();
                    facility.id = feature.getId();
                    var indx = _.sortedIndex(scope.facilities, facility, function(f) { return f._index; });
                    scope.facilities.splice(indx, 0, facility);
                }

                function removeFacility(facilityId) {
                    var index = _.findIndex(scope.facilities, function(facility) { return facility.id === facilityId; });
                    if (index >= 0) {
                        scope.facilities.splice(index, 1);
                    }
                }

                if (scope.hub.location) {
                    var point = new ol.format.GeoJSON().readGeometry(scope.hub.location);
                    HubMapCommon.setPoint(point, hubLayer);
                    view.setCenter(point.getCoordinates());
                    view.setZoom(14);
                } else {
                    ctrl.$setValidity("required", false);
                }

                map.on('dblclick', function(event) {
                    var point = new ol.geom.Point(event.coordinate).transform(mapCRS, targetCRS);
                    scope.hub.location = new ol.format.GeoJSON().writeGeometry(point);
                    ctrl.$setValidity("required", true);
                    ctrl.$setTouched();
                    scope.$apply();
                    HubMapCommon.setPoint(point, hubLayer);
                    return false;
                });

                var selectFeatures = new ol.interaction.Select({
                    toggleCondition: function() { return true; },
                    style: MapService.selectedFacilityStyle,
                    layers: [ facilitiesLayer ]
                });
                map.addInteraction(selectFeatures);
                var selectedFeatures = selectFeatures.getFeatures();

                FacilityResource.findFacilitiesAsFeatureCollection().then(function(geojson) {
                    var features = new ol.format.GeoJSON().readFeatures(geojson);
                    var extent = hubLayer.getSource().getExtent();

                    _.forEach(features, function (feature) {
                        feature.getGeometry().transform(targetCRS, mapCRS);
                        facilitiesLayer.getSource().addFeature(feature);
                        if (_.contains(scope.hub.facilityIds, feature.getId())) {
                            selectedFeatures.push(feature);
                            addFeatureAsFacility(feature);
                            extent = ol.extent.extend(extent, feature.getGeometry().getExtent());
                        }
                    });
                    if (!_.isEmpty(scope.hub.facilityIds)) {
                        view.fitExtent(extent, map.getSize());
                    }
                    selectedFeatures.on("add", function (collectionEvent) {
                        var feature = collectionEvent.element;
                        scope.hub.facilityIds.push(feature.getId());
                        addFeatureAsFacility(feature);
                        scope.$apply();
                        return true;
                    });
                    selectedFeatures.on("remove", function (collectionEvent) {
                        var facilityId = collectionEvent.element.getId();
                        scope.hub.facilityIds = _.without(scope.hub.facilityIds, facilityId);
                        removeFacility(facilityId);
                        scope.$apply();
                        return true;
                    });
                });
            }
        };
    });

    m.directive('viewHubMap', function(MapService, HubMapCommon, FacilityResource) {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                hub: '=ngModel',
                editable: '@',
                noTiles: '@'
            },
            template: '

',
            transclude: false,
            link: function(scope, element, attrs, ctrl) {
                var mapCRS = MapService.mapCRS;
                var targetCRS = MapService.targetCRS;

                var facilitiesLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.selectedFacilityStyle
                });

                var width = 3;
                var hubLayer = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    style: MapService.hubStyle
                });

                var map = MapService.createMap(element, { layers: [ facilitiesLayer, hubLayer ], readOnly: true, noTiles: attrs.noTiles === "true"});
                var view = map.getView();

                var point = new ol.format.GeoJSON().readGeometry(scope.hub.location);
                HubMapCommon.setPoint(point, hubLayer);
                view.setCenter(point.getCoordinates());
                view.setZoom(14);

                if (!_.isEmpty(scope.hub.facilityIds)) {
                    FacilityResource.findFacilitiesAsFeatureCollection({ ids: scope.hub.facilityIds }).then(function(geojson) {
                        var features = new ol.format.GeoJSON().readFeatures(geojson);
                        _.forEach(features, function (feature) {
                            feature.getGeometry().transform(targetCRS, mapCRS);
                            facilitiesLayer.getSource().addFeature(feature);
                        });
                        var extent = ol.extent.extend(hubLayer.getSource().getExtent(), facilitiesLayer.getSource().getExtent());
                        if (!ol.extent.isEmpty(extent)) {
                            view.fitExtent(extent, map.getSize());
                        }
                    });
                }
                if (console.log) {
                    map.on('dblclick', function(event) {
                        console.log("{x: "+ event.pixel[0] + ", y: " + event.pixel[1] + "}");
                        return true;
                    });
                }
            }
        };
    });
})();<|endoftext|>


<commit_before>// ==========================================================================
//                        DG.GuideConfigurationView
//
//  Implements a modal dialog used to configure the Guide menu in the tool shelf
//
//  Authors:  William Finzer
//
//  Copyright ©2014 Concord Consortium
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class

  Implements a modal dialog used to configure the Guide menu in the tool shelf

 @extends SC.PalettePane
 */
DG.GuideConfigurationView = SC.PalettePane.extend(
  /** @scope DG.GuideConfigurationView.prototype */ {

    itemRow: SC.View.extend({
      blurAction: null,
      blurTarget: null,
      childViews: 'itemTitle itemURL'.w(),
        itemTitle: SC.TextFieldView.design({
          hint: 'DG.GuideConfigView.itemTitleHint'
        }),
        itemURL: SC.TextFieldView.design({
          hint: 'DG.GuideConfigView.itemURLHint'
        }),
      init: function() {
        sc_super();
        var tBlurAction = this.get('blurAction' ),
            tBlurTarget = this.get('blurTarget');
        this.setPath( 'itemTitle.layout', { left: 5, width: 140 });
        this.setPath( 'itemURL.layout', { left: 150, right: 5 });
        this.get('childViews' ).forEach( function( iView) {
          iView.set('fieldDidBlur', function() {
            if( tBlurAction)
              tBlurAction.call( tBlurTarget);
          });
        });
      },
      rowObject: function() {
        var tTitle = this.getPath('itemTitle.value' ),
            tURL = this.getPath('itemURL.value' );
        if(!SC.empty(tTitle) || !SC.empty( tURL)) {
          return { itemTitle: tTitle, url: tURL};
        }
        return null;
      }.property(),

      /**
       *
       * @param iItem {Object {itemTitle: {String} url: {String}}
       */
      setItem: function( iItem) {
        if( iItem) {
          this.setPath('itemTitle.value', iItem.itemTitle);
          this.setPath('itemURL.value', iItem.url);
        }
      }
    }),

    model: null,

    isModal: YES,

    layout: { width: 400, height: 400, centerX: 0, centerY: 0 },

    contentView: SC.View.extend( {
      childViews: 'titleField menuItems okButton cancel warning'.w(),
      titleField: SC.TextFieldView.design( {
        layout: { top: 5, left: 5, right: 25, height: 24 },
        value: '',
        spellCheckEnabled: true,
        hint: 'DG.GuideConfigView.titleHint',
        leftAccessoryView: SC.LabelView.create( {
          layout: { left: 1, width: 95, height: 22, centerY: 0 },
          value: 'DG.GuideConfigView.titlePrompt',  // "Guide Title"
          backgroundColor: 'lightgray',
          localize: true
        } )
      } ),
      menuItems: SC.View.design( {
        layout: { left: 5, right: 5, top: 34, bottom: 34 },
        backgroundColor: 'white'
      } ),
      okButton: SC.ButtonView.design( {
        layout: { bottom: 5, right: 5, height: 24, width: 90 },
        titleMinWidth: 0,
        title: 'DG.GuideConfigView.okBtnTitle', // "OK"
        toolTip: 'DG.GuideConfigView.okBtnToolTip',
        target: null,
        action: null,
        localize: true,
        isDefault: true
      } ),
      cancel: SC.ButtonView.design( {
        layout: { bottom: 5, right: 115, height: 24, width: 90 },
        titleMinWidth: 0,
        title: 'DG.GuideConfigView.cancelBtnTitle',  // "Cancel"
        target: null,
        action: null,
        toolTip: 'DG.GuideConfigView.cancelBtnTooltip',  // "Dismiss the dialog without making any changes"
        localize: true,
        isCancel: true
      } ),
      warning: SC.LabelView.design( {
        value: 'DG.GuideConfigView.httpWarning',  // "The URL must start with either http:// or https://"
        classNames: 'dg-guide-alert',
        isVisible: false,
        transitionShow: SC.View.FADE_IN,
        localize: true
      } )
    } ),

    title: function() {
      return this.getPath('contentView.titleField.value');
    }.property(),

    items: function() {
      return this.get('rowObjects');
    }.property(),

    addMenuItemRow: function( iItem) {
      var tMenuItemsView = this.getPath('contentView.menuItems' ),
          tNumRows = this.get('rowViews' ).length,
          tTop = 5 + tNumRows * 27,
          tSection = this.itemRow.create({
            layout: { border: 1, left: 5, right: 5, top: tTop + 5, height: 22 },
            blurAction: this.configureRows,
            blurTarget: this
          });
      tSection.setItem( iItem);
      tMenuItemsView.appendChild( tSection);
    },

    rowViews: function() {
      return this.getPath('contentView.menuItems.childViews' );
    }.property(),

    rowObjects: function() {
      return this.get('rowViews' )
        .map( function( iRowView) {
                return iRowView.get('rowObject');
              } )
        .filter( function( iObject) {
                  return iObject ? true : false;
                });
    }.property(),

    configureRows: function() {

      var checkURLs = function() {
        var tRowObjects = this.get('rowObjects'),
            tWarning = this.getPath('contentView.warning'),
            tTitleLayout = this.getPath('contentView.titleField.layout');
        tWarning.set('isVisible', false);
        tRowObjects.forEach( function( iObject, iIndex) {
          var tUrl = iObject.url.toLowerCase();
          if( !SC.empty( tUrl) && (tUrl.indexOf('http://') !== 0) && (tUrl.indexOf('https://') !== 0)) {
            var tItemLayout = this.get('rowViews')[iIndex].get('layout'),
                tItemViewBottom = tTitleLayout.top + tTitleLayout.height + tItemLayout.top + tItemLayout.height - 3;
            tWarning.set('layout', { left: 160, top: tItemViewBottom, height: 12 });
            tWarning.set('isVisible', true);
          }
        }.bind( this));
      }.bind( this);

      var tNumRowViews = this.get('rowViews' ).length,
          tNumRowObjects = this.get('rowObjects').length;

      checkURLs();

      while( tNumRowViews < tNumRowObjects + 1) {
        this.addMenuItemRow();
        tNumRowViews++;
      }
    },

    /**
     *
     * @param iModel {DG.GuideModel}
     */
    setupFromModel: function( iModel) {
      var tModel = this.get('model');
      this.setPath('contentView.titleField.value', tModel.get('title'));
      tModel.get('items' ).forEach( function( iItem) {
        this.addMenuItemRow( iItem);
      }.bind( this));
    },

    /**
     Initialization function.
     */
    init: function () {
      sc_super();
      this.setPath( 'contentView.cancel.target', this );
      this.setPath( 'contentView.cancel.action', 'close' );
      this.setupFromModel();
      this.configureRows();
    },

    /**
     Close the dialog.
     */
    close: function () {
      this.remove();
      this.destroy();
    }
  } );

DG.CreateGuideConfigurationView = function ( iProperties ) {
  var tDialog = DG.GuideConfigurationView.create( iProperties ),
      tContentView = tDialog.get('contentView' ),
      tEditView = tDialog.getPath( 'contentView.titleField' );

  tContentView.setPath('okButton.target', iProperties.okTarget);
  tContentView.setPath('okButton.action', iProperties.okAction);
  tDialog.append();
  tEditView.becomeFirstResponder();

  return tDialog;
};
<commit_msg>Fixed crashing bug caused by presence of null url.<commit_after>// ==========================================================================
//                        DG.GuideConfigurationView
//
//  Implements a modal dialog used to configure the Guide menu in the tool shelf
//
//  Authors:  William Finzer
//
//  Copyright ©2014 Concord Consortium
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class

  Implements a modal dialog used to configure the Guide menu in the tool shelf

 @extends SC.PalettePane
 */
DG.GuideConfigurationView = SC.PalettePane.extend(
  /** @scope DG.GuideConfigurationView.prototype */ {

    itemRow: SC.View.extend({
      blurAction: null,
      blurTarget: null,
      childViews: 'itemTitle itemURL'.w(),
        itemTitle: SC.TextFieldView.design({
          hint: 'DG.GuideConfigView.itemTitleHint'
        }),
        itemURL: SC.TextFieldView.design({
          hint: 'DG.GuideConfigView.itemURLHint'
        }),
      init: function() {
        sc_super();
        var tBlurAction = this.get('blurAction' ),
            tBlurTarget = this.get('blurTarget');
        this.setPath( 'itemTitle.layout', { left: 5, width: 140 });
        this.setPath( 'itemURL.layout', { left: 150, right: 5 });
        this.get('childViews' ).forEach( function( iView) {
          iView.set('fieldDidBlur', function() {
            if( tBlurAction)
              tBlurAction.call( tBlurTarget);
          });
        });
      },
      rowObject: function() {
        var tTitle = this.getPath('itemTitle.value' ),
            tURL = this.getPath('itemURL.value' );
        if(!SC.empty(tTitle) || !SC.empty( tURL)) {
          return { itemTitle: tTitle, url: tURL};
        }
        return null;
      }.property(),

      /**
       *
       * @param iItem {Object {itemTitle: {String} url: {String}}
       */
      setItem: function( iItem) {
        if( iItem) {
          this.setPath('itemTitle.value', iItem.itemTitle);
          this.setPath('itemURL.value', iItem.url);
        }
      }
    }),

    model: null,

    isModal: YES,

    layout: { width: 400, height: 400, centerX: 0, centerY: 0 },

    contentView: SC.View.extend( {
      childViews: 'titleField menuItems okButton cancel warning'.w(),
      titleField: SC.TextFieldView.design( {
        layout: { top: 5, left: 5, right: 25, height: 24 },
        value: '',
        spellCheckEnabled: true,
        hint: 'DG.GuideConfigView.titleHint',
        leftAccessoryView: SC.LabelView.create( {
          layout: { left: 1, width: 95, height: 22, centerY: 0 },
          value: 'DG.GuideConfigView.titlePrompt',  // "Guide Title"
          backgroundColor: 'lightgray',
          localize: true
        } )
      } ),
      menuItems: SC.View.design( {
        layout: { left: 5, right: 5, top: 34, bottom: 34 },
        backgroundColor: 'white'
      } ),
      okButton: SC.ButtonView.design( {
        layout: { bottom: 5, right: 5, height: 24, width: 90 },
        titleMinWidth: 0,
        title: 'DG.GuideConfigView.okBtnTitle', // "OK"
        toolTip: 'DG.GuideConfigView.okBtnToolTip',
        target: null,
        action: null,
        localize: true,
        isDefault: true
      } ),
      cancel: SC.ButtonView.design( {
        layout: { bottom: 5, right: 115, height: 24, width: 90 },
        titleMinWidth: 0,
        title: 'DG.GuideConfigView.cancelBtnTitle',  // "Cancel"
        target: null,
        action: null,
        toolTip: 'DG.GuideConfigView.cancelBtnTooltip',  // "Dismiss the dialog without making any changes"
        localize: true,
        isCancel: true
      } ),
      warning: SC.LabelView.design( {
        value: 'DG.GuideConfigView.httpWarning',  // "The URL must start with either http:// or https://"
        classNames: 'dg-guide-alert',
        isVisible: false,
        transitionShow: SC.View.FADE_IN,
        localize: true
      } )
    } ),

    title: function() {
      return this.getPath('contentView.titleField.value');
    }.property(),

    items: function() {
      return this.get('rowObjects');
    }.property(),

    addMenuItemRow: function( iItem) {
      var tMenuItemsView = this.getPath('contentView.menuItems' ),
          tNumRows = this.get('rowViews' ).length,
          tTop = 5 + tNumRows * 27,
          tSection = this.itemRow.create({
            layout: { border: 1, left: 5, right: 5, top: tTop + 5, height: 22 },
            blurAction: this.configureRows,
            blurTarget: this
          });
      tSection.setItem( iItem);
      tMenuItemsView.appendChild( tSection);
    },

    rowViews: function() {
      return this.getPath('contentView.menuItems.childViews' );
    }.property(),

    rowObjects: function() {
      return this.get('rowViews' )
        .map( function( iRowView) {
                return iRowView.get('rowObject');
              } )
        .filter( function( iObject) {
                  return iObject ? true : false;
                });
    }.property(),

    configureRows: function() {

      var checkURLs = function() {
        var tRowObjects = this.get('rowObjects'),
            tWarning = this.getPath('contentView.warning'),
            tTitleLayout = this.getPath('contentView.titleField.layout');
        tWarning.set('isVisible', false);
        tRowObjects.forEach( function( iObject, iIndex) {
          var tUrl = SC.empty(iObject.url) ? '' : iObject.url.toLowerCase();
          if( !SC.empty( tUrl) && (tUrl.indexOf('http://') !== 0) && (tUrl.indexOf('https://') !== 0)) {
            var tItemLayout = this.get('rowViews')[iIndex].get('layout'),
                tItemViewBottom = tTitleLayout.top + tTitleLayout.height + tItemLayout.top + tItemLayout.height - 3;
            tWarning.set('layout', { left: 160, top: tItemViewBottom, height: 12 });
            tWarning.set('isVisible', true);
          }
        }.bind( this));
      }.bind( this);

      var tNumRowViews = this.get('rowViews' ).length,
          tNumRowObjects = this.get('rowObjects').length;

      checkURLs();

      while( tNumRowViews < tNumRowObjects + 1) {
        this.addMenuItemRow();
        tNumRowViews++;
      }
    },

    /**
     *
     * @param iModel {DG.GuideModel}
     */
    setupFromModel: function( iModel) {
      var tModel = this.get('model');
      this.setPath('contentView.titleField.value', tModel.get('title'));
      tModel.get('items' ).forEach( function( iItem) {
        this.addMenuItemRow( iItem);
      }.bind( this));
    },

    /**
     Initialization function.
     */
    init: function () {
      sc_super();
      this.setPath( 'contentView.cancel.target', this );
      this.setPath( 'contentView.cancel.action', 'close' );
      this.setupFromModel();
      this.configureRows();
    },

    /**
     Close the dialog.
     */
    close: function () {
      this.remove();
      this.destroy();
    }
  } );

DG.CreateGuideConfigurationView = function ( iProperties ) {
  var tDialog = DG.GuideConfigurationView.create( iProperties ),
      tContentView = tDialog.get('contentView' ),
      tEditView = tDialog.getPath( 'contentView.titleField' );

  tContentView.setPath('okButton.target', iProperties.okTarget);
  tContentView.setPath('okButton.action', iProperties.okAction);
  tDialog.append();
  tEditView.becomeFirstResponder();

  return tDialog;
};
<|endoftext|>


<commit_before>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2012-2014 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function(){

var ui = angular.module('axelor.ui');

// this directive is used as a replacement for ng-transclude directive
// which fails to keep scope hierarchy (see: https://github.com/angular/angular.js/issues/1809)
ui.directive('uiTransclude', function() {
	return {
		compile: function(tElement, tAttrs, transclude) {
			return function(scope, element, attrs, ctrl) {
				transclude(scope.$new(), function(clone) {
					element.append(clone);
				});
			};
		}
	};
});

/**
 * The Group widget.
 *
 */
ui.formWidget('Group', {

	css: 'form-item-group',
	cellCss: 'form-item v-align-top',

	link: function(scope, element, attrs) {

		var props = scope.field;

		scope.collapsed = false;

		scope.canCollapse = function() {
			return props.canCollapse || props.collapseIf;
		};

		scope.setCollapsed = function(collapsed) {
			scope.collapsed = collapsed;
			element.children('legend').nextAll(':not(br)')[collapsed ? 'hide' : 'show']();
			axelor.$adjustSize();
		};

		scope.toggle = function() {
			scope.collapsed = !scope.collapsed;
			scope.setCollapsed(scope.collapsed);
		};

		scope.$watch("attr('collapse')", function(collapsed) {
			scope.setCollapsed(collapsed);
		});

		// if auto title, then don't show it
		if (attrs.title === attrs.field) {
			attrs.$set('title', '');
		}

		if (props.showTitle !== false) {
			attrs.$observe('title', function(value){
				scope.title = value;
			});
		}
	},
	transclude: true,
	template:
		'<fieldset ng-class="{\'bordered-box\': title, \'has-title\': title}" x-layout-selector="> div:first">'+
			'<legend ng-show="title">'+
				''+
				'</legend>'+
			'

'+
		'</fieldset>'
});

ui.formWidget('Portlet', {

	css: 'form-item-portlet',
	cellCss: 'form-item v-align-top',

	showTitle: false,

	link: function(scope, element, attrs) {

		var field = scope.field;

		scope.canSearch = field.canSearch !== "false";
		scope.actionName = field.action;

		if (field.name) {
			scope.formPath = field.name;
		}

		if (field.height) {
			element.height(field.height);
		}

		element.resizable({
			handles: 's',
			resize: _.debounce(function() {
				axelor.$adjustSize();
				element.width('auto');
			}, 100)
		});
	},

	template:
	'
'+
		'

'+
	'
'
});

/**
 * The Tabs widget (notebook).
 */
ui.formWidget('Tabs', {

	cellCss: 'form-item v-align-top',

	widgets: ['Notebook'],

	controller: ['$scope', '$element', function($scope, $element) {

		var tabs = $scope.tabs = [],
			selected = -1;

		var doSelect = _.debounce(function doSelect() {
			var select = tabs[selected];
			if (select) {
				select.handleSelect();
			}
		}, 100);

		$scope.select = function(tab) {

			var current = selected;

			angular.forEach(tabs, function(tab, i){
				tab.tabSelected = false;
			});

			tab.tabSelected = true;
			selected = _.indexOf(tabs, tab);

			if (current === selected) {
				return;
			}

			setTimeout(function() {
				if ($scope.$tabs) {
					$scope.$tabs.trigger('adjust');
				}
				axelor.$adjustSize();
				if(current != selected){
					doSelect();
				}
			});
		};

		this.addTab = function(tab) {
			if (tabs.length === 0) $scope.select(tab);
			tab.index = tabs.length;
			tabs.push(tab);
		};

		function inRange(index) {
			return index > -1 && index < tabs.length;
		}

		function findItem(index) {
			return $element.find('ul.nav-tabs:first > li:nth-child(' + (index+1) + ')');
		}

		this.showTab = function(index) {

			if (!inRange(index)) {
				return;
			}

			var tab = tabs[index];
			var item = findItem(index);

			tab.hidden = false;
			item.show();

			if (selected == -1 || selected === index) {
				return $scope.select(tabs[index]);
			}

			axelor.$adjustSize();
		};

		this.hideTab = function(index) {

			if (!inRange(index))
				return;

			var item = findItem(index),
				tab = tabs[index];

			var wasHidden = item.is(":hidden");

			item.hide();
			item.removeClass('active');

			tab.hidden = true;
			tab.tabSelected = false;

			if (!wasHidden && selected > -1 && selected !== index)
				return axelor.$adjustSize();

			for(var i = 0 ; i < tabs.length ; i++) {
				var tab = tabs[i];
				if (!tab.hidden) {
					return $scope.select(tabs[i]);
				}
			}
			selected = -1;
		};

		$scope.setTitle = function(value,index){
			var item = findItem(index),
				pageScope = item.first().data('$scope');

			pageScope.tab.title = value;
		};

		$scope.$on('on:edit', doSelect);
	}],

	link: function(scope, elem, attrs) {

		var props = scope.field;

		scope.$tabs = $(elem).bsTabs({
			closable: false
		});

		elem.on('click', '.dropdown-toggle', function(e){
			axelor.$adjustSize();
		});

		// set height (#1011)
		if (props.height) {
			elem.children('.tab-content:first').height(props.height);
		}
	},
	transclude: true,
	template:
		'
' +
			'
' +
				'

' +
				'

' +
				'
' +
					'
' +
						'
'+
							''+
								''+
								''+
							'' +
						'
' +
					'
' +
				'
' +
				'
'+
					'
'+
						''+
							'
'+
							    '
'+
							    	''+
							    '
' +
							'
' +
						''+
					'
'+
				'
'+
			'
' +
			'

' +
		'
'
});

/**
 * The Tab widget (notebook page).
 */
ui.formWidget('Tab', {

	require: '^uiTabs',

	widgets: ['Page'],

	handles: ['isHidden'],

	link: function(scope, elem, attrs, tabs) {

		scope.tabSelected = false;
		scope.icon = scope.field && scope.field.icon;

		tabs.addTab(scope);

		attrs.$observe('title', function(value){
			scope.title = value;
		});

		scope.$watch("isHidden()", function(hidden, old) {
			if (hidden) {
				return tabs.hideTab(scope.index);
			}
			return tabs.showTab(scope.index);
		});

		scope.handleSelect = function () {
			var onSelect = scope.$events.onSelect;
			if (onSelect && !elem.is(":hidden")) {
				onSelect();
			}
		};
	},
	cellCss: 'form-item v-align-top',
	transclude: true,
	template: '
'+
		'

'+
	'
'
});

ui.formWidget('ButtonGroup', {
	transclude: true,
	template_editable: null,
	template_readonly: null,
	template:
		"

"
});

ui.formWidget('Panel', {

	showTitle: false,
	widgets: ['PanelSide'],

	link: function (scope, element, attrs) {
		var field = scope.field || {};
		element.addClass(field.serverType);
		if (field.serverType === 'panel-side' && !attrs.itemspan) {
			attrs.$set('itemspan', 12);
		}
		scope.menus = null;
		if (field.menu) {
			scope.menus = [field.menu];
		}

		var nested = element.parents('.panel:first').size() > 0;
		if (nested) {
			element.addClass("panel-nested");
		}
		if (field.noframe) {
			element.addClass('noframe');
		}
	},

	transclude: true,
	template:
		"
" +
			"
" +
				"

" +
				"
{{field.title}}
" +
			"
" +
			"

" +
		"
"
});

ui.formWidget('PanelStack', {
	transclude: true,
	template: "

"
});

ui.formWidget('PanelTabs', {

	link: function (scope, element, attrs) {
		scope.tabs = [];
		element.find('> .tab-content > div').each(function () {
			var elem = $(this);
			var tab = {
				title: elem.attr('x-title'),
				selected: false,
				elem: elem
			}
			scope.tabs.push(tab);
		});

		scope.selectTab = function(tab) {
			scope.tabs.forEach(function (current) {
				current.selected = false;
				current.elem.hide();
			})
			tab.selected = true;
			tab.elem.show();
		}

		scope.$timeout(function() {
			scope.selectTab(_.first(scope.tabs));
		})
	},

	transclude: true,
	template:
		"
" +
			"
" +
			"
" +
			"
" +
				"
" +
					"" +
				"
" +
			"
" +
			"
" +
			"
" +
			"

" +
		"
"
});

})(this);
<commit_msg>Support for hiding panel title.<commit_after>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2012-2014 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function(){

var ui = angular.module('axelor.ui');

// this directive is used as a replacement for ng-transclude directive
// which fails to keep scope hierarchy (see: https://github.com/angular/angular.js/issues/1809)
ui.directive('uiTransclude', function() {
	return {
		compile: function(tElement, tAttrs, transclude) {
			return function(scope, element, attrs, ctrl) {
				transclude(scope.$new(), function(clone) {
					element.append(clone);
				});
			};
		}
	};
});

/**
 * The Group widget.
 *
 */
ui.formWidget('Group', {

	css: 'form-item-group',
	cellCss: 'form-item v-align-top',

	link: function(scope, element, attrs) {

		var props = scope.field;

		scope.collapsed = false;

		scope.canCollapse = function() {
			return props.canCollapse || props.collapseIf;
		};

		scope.setCollapsed = function(collapsed) {
			scope.collapsed = collapsed;
			element.children('legend').nextAll(':not(br)')[collapsed ? 'hide' : 'show']();
			axelor.$adjustSize();
		};

		scope.toggle = function() {
			scope.collapsed = !scope.collapsed;
			scope.setCollapsed(scope.collapsed);
		};

		scope.$watch("attr('collapse')", function(collapsed) {
			scope.setCollapsed(collapsed);
		});

		// if auto title, then don't show it
		if (attrs.title === attrs.field) {
			attrs.$set('title', '');
		}

		if (props.showTitle !== false) {
			attrs.$observe('title', function(value){
				scope.title = value;
			});
		}
	},
	transclude: true,
	template:
		'<fieldset ng-class="{\'bordered-box\': title, \'has-title\': title}" x-layout-selector="> div:first">'+
			'<legend ng-show="title">'+
				''+
				'</legend>'+
			'

'+
		'</fieldset>'
});

ui.formWidget('Portlet', {

	css: 'form-item-portlet',
	cellCss: 'form-item v-align-top',

	showTitle: false,

	link: function(scope, element, attrs) {

		var field = scope.field;

		scope.canSearch = field.canSearch !== "false";
		scope.actionName = field.action;

		if (field.name) {
			scope.formPath = field.name;
		}

		if (field.height) {
			element.height(field.height);
		}

		element.resizable({
			handles: 's',
			resize: _.debounce(function() {
				axelor.$adjustSize();
				element.width('auto');
			}, 100)
		});
	},

	template:
	'
'+
		'

'+
	'
'
});

/**
 * The Tabs widget (notebook).
 */
ui.formWidget('Tabs', {

	cellCss: 'form-item v-align-top',

	widgets: ['Notebook'],

	controller: ['$scope', '$element', function($scope, $element) {

		var tabs = $scope.tabs = [],
			selected = -1;

		var doSelect = _.debounce(function doSelect() {
			var select = tabs[selected];
			if (select) {
				select.handleSelect();
			}
		}, 100);

		$scope.select = function(tab) {

			var current = selected;

			angular.forEach(tabs, function(tab, i){
				tab.tabSelected = false;
			});

			tab.tabSelected = true;
			selected = _.indexOf(tabs, tab);

			if (current === selected) {
				return;
			}

			setTimeout(function() {
				if ($scope.$tabs) {
					$scope.$tabs.trigger('adjust');
				}
				axelor.$adjustSize();
				if(current != selected){
					doSelect();
				}
			});
		};

		this.addTab = function(tab) {
			if (tabs.length === 0) $scope.select(tab);
			tab.index = tabs.length;
			tabs.push(tab);
		};

		function inRange(index) {
			return index > -1 && index < tabs.length;
		}

		function findItem(index) {
			return $element.find('ul.nav-tabs:first > li:nth-child(' + (index+1) + ')');
		}

		this.showTab = function(index) {

			if (!inRange(index)) {
				return;
			}

			var tab = tabs[index];
			var item = findItem(index);

			tab.hidden = false;
			item.show();

			if (selected == -1 || selected === index) {
				return $scope.select(tabs[index]);
			}

			axelor.$adjustSize();
		};

		this.hideTab = function(index) {

			if (!inRange(index))
				return;

			var item = findItem(index),
				tab = tabs[index];

			var wasHidden = item.is(":hidden");

			item.hide();
			item.removeClass('active');

			tab.hidden = true;
			tab.tabSelected = false;

			if (!wasHidden && selected > -1 && selected !== index)
				return axelor.$adjustSize();

			for(var i = 0 ; i < tabs.length ; i++) {
				var tab = tabs[i];
				if (!tab.hidden) {
					return $scope.select(tabs[i]);
				}
			}
			selected = -1;
		};

		$scope.setTitle = function(value,index){
			var item = findItem(index),
				pageScope = item.first().data('$scope');

			pageScope.tab.title = value;
		};

		$scope.$on('on:edit', doSelect);
	}],

	link: function(scope, elem, attrs) {

		var props = scope.field;

		scope.$tabs = $(elem).bsTabs({
			closable: false
		});

		elem.on('click', '.dropdown-toggle', function(e){
			axelor.$adjustSize();
		});

		// set height (#1011)
		if (props.height) {
			elem.children('.tab-content:first').height(props.height);
		}
	},
	transclude: true,
	template:
		'
' +
			'
' +
				'

' +
				'

' +
				'
' +
					'
' +
						'
'+
							''+
								''+
								''+
							'' +
						'
' +
					'
' +
				'
' +
				'
'+
					'
'+
						''+
							'
'+
							    '
'+
							    	''+
							    '
' +
							'
' +
						''+
					'
'+
				'
'+
			'
' +
			'

' +
		'
'
});

/**
 * The Tab widget (notebook page).
 */
ui.formWidget('Tab', {

	require: '^uiTabs',

	widgets: ['Page'],

	handles: ['isHidden'],

	link: function(scope, elem, attrs, tabs) {

		scope.tabSelected = false;
		scope.icon = scope.field && scope.field.icon;

		tabs.addTab(scope);

		attrs.$observe('title', function(value){
			scope.title = value;
		});

		scope.$watch("isHidden()", function(hidden, old) {
			if (hidden) {
				return tabs.hideTab(scope.index);
			}
			return tabs.showTab(scope.index);
		});

		scope.handleSelect = function () {
			var onSelect = scope.$events.onSelect;
			if (onSelect && !elem.is(":hidden")) {
				onSelect();
			}
		};
	},
	cellCss: 'form-item v-align-top',
	transclude: true,
	template: '
'+
		'

'+
	'
'
});

ui.formWidget('ButtonGroup', {
	transclude: true,
	template_editable: null,
	template_readonly: null,
	template:
		"

"
});

ui.formWidget('Panel', {

	showTitle: false,
	widgets: ['PanelSide'],

	link: function (scope, element, attrs) {
		var field = scope.field || {};
		element.addClass(field.serverType);
		if (field.serverType === 'panel-side' && !attrs.itemspan) {
			attrs.$set('itemspan', 12);
		}
		scope.menus = null;
		if (field.menu) {
			scope.menus = [field.menu];
		}

		var nested = element.parents('.panel:first').size() > 0;
		if (nested) {
			element.addClass("panel-nested");
		}
		if (field.noframe) {
			element.addClass('noframe');
		}
		scope.notitle = field.noframe || field.showTitle === false;
	},

	transclude: true,
	template:
		"
" +
			"
" +
				"

" +
				"
{{field.title}}
" +
			"
" +
			"

" +
		"
"
});

ui.formWidget('PanelStack', {
	transclude: true,
	template: "

"
});

ui.formWidget('PanelTabs', {

	link: function (scope, element, attrs) {
		scope.tabs = [];
		element.find('> .tab-content > div').each(function () {
			var elem = $(this);
			var tab = {
				title: elem.attr('x-title'),
				selected: false,
				elem: elem
			}
			scope.tabs.push(tab);
		});

		scope.selectTab = function(tab) {
			scope.tabs.forEach(function (current) {
				current.selected = false;
				current.elem.hide();
			})
			tab.selected = true;
			tab.elem.show();
		}

		scope.$timeout(function() {
			scope.selectTab(_.first(scope.tabs));
		})
	},

	transclude: true,
	template:
		"
" +
			"
" +
			"
" +
			"
" +
				"
" +
					"" +
				"
" +
			"
" +
			"
" +
			"
" +
			"

" +
		"
"
});

})(this);
<|endoftext|>


<commit_before>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2014 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

var ui = angular.module('axelor.ui');

DashboardCtrl.$inject = ['$scope', '$element'];
function DashboardCtrl($scope, $element) {

	var view = $scope._views['dashboard'];
	if (view.items) {
		$scope.$timeout(function () {
			$scope.parse(view);
		});
	} else {
		$scope.loadView('dashboard', view.name).success(function(fields, schema){
			$scope.parse(schema);
		});
	}

	$scope.applyLater(function(){
		if (view.deferred)
			view.deferred.resolve($scope);
	}, 0);

	$scope.show = function(promise) {
		$scope.updateRoute();
	};

	$scope.onShow = function() {

	};

	$scope.getContext = function() {
		return _.extend({}, $scope._context);
	};

	$scope.getRouteOptions = function() {
		return {
			mode: 'dashboard',
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	$scope.parse = function(schema) {
		var items = schema.items || [];
		var rows = [[]];
		var last = _.last(rows);
		var lastCol = 0;

		items.forEach(function (item) {
			var span = item.colSpan || 6;

			if (lastCol + span > 12) {
				lastCol = 0;
				last = [];
				rows.push(last);
			}

			item.spanCss = {};
			item.spanCss['dashlet-cs' + span] = true;

			last.push(item);
			lastCol += span;
		});

		$scope.rows = rows;
	};
}

ui.directive('uiViewDashboard', ['$compile', function($compile) {

	return {
		scope: true,
		controller: DashboardCtrl,
		link: function(scope, element, attrs) {

		},
		replace: true,
		transclude: true,
		template:
		"
" +
			"
" +
				"
" +
					"

" +
				"
" +
			"
" +
		"
"
	};
}]);

DashletCtrl.$inject = ['$scope', '$element', 'MenuService', 'DataSource', 'ViewService'];
function DashletCtrl($scope, $element, MenuService, DataSource, ViewService) {

	var self = this;

	function init() {

		ViewCtrl.call(self, $scope, DataSource, ViewService);

		$scope.show = function() {

		};

		$scope.onShow = function() {

		};
	}

	$scope.initDashlet = function(dashlet, options) {

		var action = dashlet.action;
		if (!action) {
			return init();
		}

		MenuService.action(action, options).success(function(result){
			if (_.isEmpty(result.data)) {
				return;
			}
			var view = result.data[0].view;

			$scope._viewParams = view;
			$scope._viewAction = action;

			init();

			$scope.title = dashlet.title || view.title;
			$scope.parseDashlet(dashlet, view);
		});
	};

	$scope.$on('on:attrs-change:refresh', function(e) {
		e.preventDefault();
		if ($scope.onRefresh) {
			$scope.onRefresh();
		}
	});

	$scope.$on('on:tab-reload', function(e) {
		if ($scope.onRefresh) {
			$scope.onRefresh();
		}
	});
}

ui.directive('uiViewDashlet', ['$compile', function($compile){
	return {
		scope: true,
		controller: DashletCtrl,
		link: function(scope, element, attrs) {

			var lazy = true;
			scope.waitForActions(function () {
				var unwatch = scope.$watch(function () {
					var dashlet = scope.dashlet;
					if (!dashlet) {
						return;
					}

					if (element.parent().is(":hidden")) {
						return lazy = true;
					}

					unwatch();
					unwatch = null;

					var ctx = undefined;
					if (scope.getContext) {
						ctx = scope.getContext();
					}
					scope.initDashlet(dashlet, {
						context: ctx
					});
				});
			});

			scope.parseDashlet = _.once(function(dashlet, view) {
				var body = element.find('.dashlet-body:first');
				var template = $('

');

				scope.noFilter = !dashlet.canSearch;

				template = $compile(template)(scope);
				body.append(template);

				element.removeClass('hidden');

				scope.show();

				// if lazy, load data
				if (scope.onRefresh && lazy) {
					scope.onRefresh();
				}
			});

			scope.onDashletToggle = function(event) {
				var e = $(event.target);
				e.toggleClass('fa-chevron-up fa-chevron-down');
				element.toggleClass('dashlet-minimized');
				if (e.hasClass('fa-chevron-up')) {
					axelor.$adjustSize();
				}
			};

			scope.doNext = function() {
				if (this.canNext()) this.onNext();
			};

			scope.doPrev = function() {
				if (this.canPrev()) this.onPrev();
			};
		},
		replace: true,
		template:
			"
" +
				"
" +
					"
{{title}}
" +
					"
" +
						"" +
					"
" +
					"
" +
						"{{pagerText()}}" +
						"" +
						"" +
					"
" +
				"
" +
				"

" +
			"
"
	};
}]);

})();<commit_msg>Fix dashboard/portal initialization issue<commit_after>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2014 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

var ui = angular.module('axelor.ui');

DashboardCtrl.$inject = ['$scope', '$element'];
function DashboardCtrl($scope, $element) {

	var view = $scope._views['dashboard'];
	if (view.items) {
		$scope.$timeout(function () {
			$scope.parse(view);
		});
	} else {
		$scope.loadView('dashboard', view.name).success(function(fields, schema){
			$scope.parse(schema);
		});
	}

	$scope.applyLater(function(){
		if (view.deferred)
			view.deferred.resolve($scope);
	}, 0);

	$scope.show = function(promise) {
		$scope.updateRoute();
	};

	$scope.onShow = function() {

	};

	$scope.getContext = function() {
		return _.extend({}, $scope._context);
	};

	$scope.getRouteOptions = function() {
		return {
			mode: 'dashboard',
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	$scope.parse = function(schema) {
		var items = schema.items || [];
		var rows = [[]];
		var last = _.last(rows);
		var lastCol = 0;

		items.forEach(function (item) {
			var span = item.colSpan || 6;

			if (lastCol + span > 12) {
				lastCol = 0;
				last = [];
				rows.push(last);
			}

			item.spanCss = {};
			item.spanCss['dashlet-cs' + span] = true;

			last.push(item);
			lastCol += span;
		});

		$scope.rows = rows;
	};
}

ui.directive('uiViewDashboard', ['$compile', function($compile) {

	return {
		scope: true,
		controller: DashboardCtrl,
		link: function(scope, element, attrs) {

		},
		replace: true,
		transclude: true,
		template:
		"
" +
			"
" +
				"
" +
					"

" +
				"
" +
			"
" +
		"
"
	};
}]);

DashletCtrl.$inject = ['$scope', '$element', 'MenuService', 'DataSource', 'ViewService'];
function DashletCtrl($scope, $element, MenuService, DataSource, ViewService) {

	var self = this;

	function init() {

		ViewCtrl.call(self, $scope, DataSource, ViewService);

		$scope.show = function() {

		};

		$scope.onShow = function() {

		};
	}

	$scope.initDashlet = function(dashlet, options) {

		var action = dashlet.action;
		if (!action) {
			return init();
		}

		MenuService.action(action, options).success(function(result){
			if (_.isEmpty(result.data)) {
				return;
			}
			var view = result.data[0].view;

			$scope._viewParams = view;
			$scope._viewAction = action;

			init();

			$scope.title = dashlet.title || view.title;
			$scope.parseDashlet(dashlet, view);
		});
	};

	$scope.$on('on:attrs-change:refresh', function(e) {
		e.preventDefault();
		if ($scope.onRefresh) {
			$scope.onRefresh();
		}
	});

	$scope.$on('on:tab-reload', function(e) {
		if ($scope.onRefresh) {
			$scope.onRefresh();
		}
	});
}

ui.directive('uiViewDashlet', ['$compile', function($compile){
	return {
		scope: true,
		controller: DashletCtrl,
		link: function(scope, element, attrs) {

			var lazy = true;

			(function () {
				var counter = 0;
				return function checkLoading() {
					if (counter < 10 && element.parent().is(":hidden")) {
						counter++;
						return setTimeout(checkLoading, 100);
					}
					scope.waitForActions(function () {
						var unwatch = scope.$watch(function () {
							var dashlet = scope.dashlet;
							if (!dashlet) {
								return;
							}

							if (element.parent().is(":hidden")) {
								return lazy = true;
							}

							unwatch();
							unwatch = null;

							var ctx = undefined;
							if (scope.getContext) {
								ctx = scope.getContext();
							}
							scope.initDashlet(dashlet, {
								context: ctx
							});
						});
					});
				}
			})()();

			scope.parseDashlet = _.once(function(dashlet, view) {
				var body = element.find('.dashlet-body:first');
				var template = $('

');

				scope.noFilter = !dashlet.canSearch;

				template = $compile(template)(scope);
				body.append(template);

				element.removeClass('hidden');

				scope.show();

				// if lazy, load data
				if (scope.onRefresh && lazy) {
					scope.onRefresh();
				}
			});

			scope.onDashletToggle = function(event) {
				var e = $(event.target);
				e.toggleClass('fa-chevron-up fa-chevron-down');
				element.toggleClass('dashlet-minimized');
				if (e.hasClass('fa-chevron-up')) {
					axelor.$adjustSize();
				}
			};

			scope.doNext = function() {
				if (this.canNext()) this.onNext();
			};

			scope.doPrev = function() {
				if (this.canPrev()) this.onPrev();
			};
		},
		replace: true,
		template:
			"
" +
				"
" +
					"
{{title}}
" +
					"
" +
						"" +
					"
" +
					"
" +
						"{{pagerText()}}" +
						"" +
						"" +
					"
" +
				"
" +
				"

" +
			"
"
	};
}]);

})();<|endoftext|>


<commit_before>/**
 *  Copyright (C) 2022 3D Repo Ltd
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const { src } = require('../../../helper/path');
const { generateRandomString } = require('../../../helper/services');
const BaseTemplate = require('../../../../../src/v5/services/mailer/templates/baseTemplate');

const config = require(`${src}/utils/config`);
const { templates: emailTemplates } = require(`${src}/services/mailer/mailer.constants`);
const { templates } = require(`${src}/utils/responseCodes`);

const sendMailMock = jest.fn();
jest.mock('nodemailer');
const nodemailer = require('nodemailer');

nodemailer.createTransport.mockImplementation(() => ({ sendMail: sendMailMock }));
nodemailer.createTestAccount.mockImplementation(() => ({ user: generateRandomString(), pass: generateRandomString() }));
const Mailer = require(`${src}/services/mailer`);

const testSendEmail = () => {
	describe('send email', () => {
		const recipient = 'example@email.com';
		const attachments = { attachment: generateRandomString() };
		const data = {
			username: generateRandomString(),
			token: generateRandomString(),
			firstName: generateRandomString(),
		};

		const mailerConfig = { ...config.mail };

		beforeEach(() => {
			config.mail = mailerConfig;
		});

		test('should fail if config.mail.sender is not set', async () => {
			Mailer.reset();
			const { sender } = config.mail;
			config.mail.sender = undefined;

			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toThrow('config.mail.sender is not set');
			config.mail.sender = sender;
			Mailer.reset();
		});

		test('should fail if config.mail.smtpConfig is not set and config.mail.generateCredentials is false', async () => {
			Mailer.reset();
			config.mail.generateCredentials = false;
			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toThrow('config.mail.smtpConfig is not set');
			config.mail.generateCredentials = true;
			Mailer.reset();
		});

		test('should send email if attachments are provided', async () => {
			await Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments);
			expect(sendMailMock).toBeCalledTimes(1);
			expect(sendMailMock).toBeCalledWith({
				from: config.mail.sender,
				to: recipient,
				subject: emailTemplates.FORGOT_PASSWORD.subject(),
				html: await BaseTemplate.html({
					...data,
					emailContent: await emailTemplates.FORGOT_PASSWORD.html(data),
				}),
				attachments,
			});
		});

		test('should send email if attachments are not provided', async () => {
			await Mailer.sendEmail(emailTemplates.VERIFY_USER.name, recipient, data);
			expect(sendMailMock).toBeCalledTimes(1);
			expect(sendMailMock).toBeCalledWith({
				from: config.mail.sender,
				to: recipient,
				subject: emailTemplates.VERIFY_USER.subject(),
				html: await BaseTemplate.html({ ...data, emailContent: await emailTemplates.VERIFY_USER.html(data) }),
			});
		});

		test('should log the error and throw it back if sendMail fails', async () => {
			sendMailMock.mockImplementationOnce(() => { throw templates.unknown; });

			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toEqual(templates.unknown);
		});

		test('should throw error if the template name is not recognised', async () => {
			await expect(Mailer.sendEmail(generateRandomString(), recipient, data, attachments))
				.rejects.toEqual(templates.unknown);
		});

		test('should throw error if the data does not provide sufficient info for the template', async () => {
			await expect(Mailer.sendEmail(emailTemplates.VERIFY_USER.name, recipient, undefined, attachments))
				.rejects.toThrow();
		});
	});
};

describe('services/mailer/index', () => {
	testSendEmail();
});
<commit_msg>ISSUE #3248 fix test<commit_after>/**
 *  Copyright (C) 2022 3D Repo Ltd
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const { src } = require('../../../helper/path');
const { generateRandomString } = require('../../../helper/services');
const BaseTemplate = require('../../../../../src/v5/services/mailer/templates/baseTemplate');

const config = require(`${src}/utils/config`);
const { templates: emailTemplates } = require(`${src}/services/mailer/mailer.constants`);
const { templates } = require(`${src}/utils/responseCodes`);

const sendMailMock = jest.fn();
jest.mock('nodemailer');
const nodemailer = require('nodemailer');

nodemailer.createTransport.mockImplementation(() => ({ sendMail: sendMailMock }));
nodemailer.createTestAccount.mockImplementation(() => ({ user: generateRandomString(), pass: generateRandomString() }));
const Mailer = require(`${src}/services/mailer`);

const testSendEmail = () => {
	describe('send email', () => {
		const recipient = 'example@email.com';
		const attachments = { attachment: generateRandomString() };
		const data = {
			username: generateRandomString(),
			token: generateRandomString(),
			firstName: generateRandomString(),
		};

		const mailerConfig = { ...config.mail };

		beforeEach(() => {
			config.mail = mailerConfig;
		});

		test('should fail if config.mail.sender is not set', async () => {
			Mailer.reset();
			const { sender } = config.mail;
			config.mail.sender = undefined;

			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toThrow('config.mail.sender is not set');
			config.mail.sender = sender;
			Mailer.reset();
		});

		test('should fail if config.mail.smtpConfig is not set and config.mail.generateCredentials is false', async () => {
			Mailer.reset();
			config.mail.generateCredentials = false;
			delete config.mail.smtpConfig;
			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toThrow('config.mail.smtpConfig is not set');
			config.mail.generateCredentials = true;
			Mailer.reset();
		});

		test('should send email if attachments are provided', async () => {
			await Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments);
			expect(sendMailMock).toBeCalledTimes(1);
			expect(sendMailMock).toBeCalledWith({
				from: config.mail.sender,
				to: recipient,
				subject: emailTemplates.FORGOT_PASSWORD.subject(),
				html: await BaseTemplate.html({
					...data,
					emailContent: await emailTemplates.FORGOT_PASSWORD.html(data),
				}),
				attachments,
			});
		});

		test('should send email if attachments are not provided', async () => {
			await Mailer.sendEmail(emailTemplates.VERIFY_USER.name, recipient, data);
			expect(sendMailMock).toBeCalledTimes(1);
			expect(sendMailMock).toBeCalledWith({
				from: config.mail.sender,
				to: recipient,
				subject: emailTemplates.VERIFY_USER.subject(),
				html: await BaseTemplate.html({ ...data, emailContent: await emailTemplates.VERIFY_USER.html(data) }),
			});
		});

		test('should log the error and throw it back if sendMail fails', async () => {
			sendMailMock.mockImplementationOnce(() => { throw templates.unknown; });

			await expect(Mailer.sendEmail(emailTemplates.FORGOT_PASSWORD.name, recipient, data, attachments))
				.rejects.toEqual(templates.unknown);
		});

		test('should throw error if the template name is not recognised', async () => {
			await expect(Mailer.sendEmail(generateRandomString(), recipient, data, attachments))
				.rejects.toEqual(templates.unknown);
		});

		test('should throw error if the data does not provide sufficient info for the template', async () => {
			await expect(Mailer.sendEmail(emailTemplates.VERIFY_USER.name, recipient, undefined, attachments))
				.rejects.toThrow();
		});
	});
};

describe('services/mailer/index', () => {
	testSendEmail();
});
<|endoftext|>


<commit_before>'use strict';
require('mocha');
import {it} from 'mocha';
require('co-mocha');
const _ = require('lodash');
const chai = require('chai');
const assert = chai.assert;
const should = chai.should();

const r = require('rethinkdbdash')({
    db: process.env.MCDB || 'materialscommons',
    port: process.env.MCDB_PORT || 30815
});
const mcapi_base = '../../../../servers/mcapi';
const backend_base = mcapi_base + "/db/model";
const build_project_base = mcapi_base + "/build-demo";

const dbModelUsers = require(backend_base + '/users');
const projects = require(backend_base + '/projects');
const directories = require(backend_base + '/directories');
const experiments = require(backend_base + '/experiments');
const processes = require(backend_base + '/processes');
const experimentDatasets = require(backend_base + '/experiment-datasets');

const helper = require(build_project_base + '/build-demo-project-helper');
const demoProjectConf = require(build_project_base + '/build-demo-project-conf');
const buildDemoProject = require(build_project_base + '/build-demo-project');

const testHelpers = require('./test-helpers');

const base_project_name = "Test directory";

let random_name = function(){
    let number = Math.floor(Math.random()*10000);
    return base_project_name + number;
};

let userId = "test@test.mc";

let project = null;
let experiment = null;
let process_list = null;
let sample_list = null;
let file_list = null;

before(function*() {

    this.timeout(8000); // this test suite can take up to 8 seconds

    let user = yield dbModelUsers.getUser(userId);
    assert.isOk(user, "No test user available = " + userId);
    assert.equal(userId,user.id);

    let valOrError = yield buildDemoProject.findOrBuildAllParts(user,demoProjectConf.datapathPrefix);
    assert.isUndefined(valOrError.error, "Unexpected error from createDemoProjectForUser: " + valOrError.error);
    let results = valOrError.val;
    project = results.project;
    experiment = results.experiment;
    process_list = results.processes;
    sample_list = results.samples;
    file_list = results.files;

    let projectId = project.id;
    let experimentId = experiment.id;

    let name = random_name();
    let description = "Changed the name of the demo project to " + name;
    let updateData = {
        name: name,
        description: description
    };
    let updated_project = yield projects.update(project.id, updateData);
    assert.equal(updated_project.otype, "project");
    assert.equal(updated_project.owner, userId);
    assert.equal(updated_project.name, name);
    assert.equal(updated_project.description, description);
    assert.equal(updated_project.id,projectId);
    project = updated_project;

    for (let i = 0; i < process_list.length; i++) {
        let process = process_list[i];
        yield processes.deleteProcess(project.id,process.id);
    }

    let simple = true;
    results = yield experiments.getProcessesForExperiment(experimentId, simple);
    assert.equal(results.val.length,0);

    let rv = yield r.table('experiment2sample')
        .getAll(experimentId,{index:'experiment_id'}).delete();

    assert.equal(rv.deleted, 7);

    yield testHelpers.setUpFakeExperimentNoteData(experimentId,userId);
    yield testHelpers.setUpAdditionalExperimentTaskData(experimentId,userId);

});

describe('Feature - Experiments: ', function() {
    describe('Delete Experiment - basic parts: ', function () {
        it('deletes experiment part: experiment-notes', function* (){
            // ---- experimentnote ----
            // experiment2experimentnote
            // experimentnotes

            let projectId = project.id;
            assert.isOk(projectId);
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            let idList = yield r.table('experiment2experimentnote')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_note_id',r.table('experimentnotes'))
                .zip().getField('experiment_note_id');

            let delete_msg = yield r.table('experimentnotes').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experiment2experimentnote')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 1);
        });
        it('deletes experiment part: experiment-tasks', function* (){
            let projectId = project.id;
            assert.isOk(projectId);
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            // get taskId and processId
            let idList = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_task_id',r.table('experimenttasks'))
                .zip().getField('experiment_task_id');
            assert.isOk(idList);
            assert.equal(idList.length,1);
            let taskId = idList[0];
            assert.isOk(taskId);

            let task = yield r.table('experimenttasks').get(taskId);
            assert.isOk(task);
            assert.isOk(task.process_id);
            let processId = task.process_id;

            // experiment2experimenttask
            // experimenttask2process
            // experimenttasks
            // processes

            idList = yield r.table('experimenttask2process')
                .getAll(taskId,{index:'experiment_task_id'})
                .eqJoin('process_id',r.table('processes'))
                .zip().getField('process_id');
            delete_msg = yield r.table('processes').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experimenttask2process')
                .getAll(taskId,{index:'experiment_task_id'}).delete();
            assert.equal(delete_msg.deleted, 1);

            idList = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_task_id',r.table('experimenttasks'))
                .zip().getField('experiment_task_id');
            let delete_msg = yield r.table('experimenttasks').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 1);

        });
        it('deletes links between files and experiment', function* (){
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            let delete_msg = yield r.table('experiment2datafile')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 16);
        });
    });
});
<commit_msg>Corrected minor syntax problme<commit_after>'use strict';
require('mocha');
import {it} from 'mocha';
require('co-mocha');
const _ = require('lodash');
const chai = require('chai');
const assert = chai.assert;
const should = chai.should();

const r = require('rethinkdbdash')({
    db: process.env.MCDB || 'materialscommons',
    port: process.env.MCDB_PORT || 30815
});
const mcapi_base = '../../../../servers/mcapi';
const backend_base = mcapi_base + "/db/model";
const build_project_base = mcapi_base + "/build-demo";

const dbModelUsers = require(backend_base + '/users');
const projects = require(backend_base + '/projects');
const directories = require(backend_base + '/directories');
const experiments = require(backend_base + '/experiments');
const processes = require(backend_base + '/processes');
const experimentDatasets = require(backend_base + '/experiment-datasets');

const helper = require(build_project_base + '/build-demo-project-helper');
const demoProjectConf = require(build_project_base + '/build-demo-project-conf');
const buildDemoProject = require(build_project_base + '/build-demo-project');

const testHelpers = require('./test-helpers');

const base_project_name = "Test directory";

let random_name = function(){
    let number = Math.floor(Math.random()*10000);
    return base_project_name + number;
};

let userId = "test@test.mc";

let project = null;
let experiment = null;
let process_list = null;
let sample_list = null;
let file_list = null;

before(function*() {

    this.timeout(8000); // this test suite can take up to 8 seconds

    let user = yield dbModelUsers.getUser(userId);
    assert.isOk(user, "No test user available = " + userId);
    assert.equal(userId,user.id);

    let valOrError = yield buildDemoProject.findOrBuildAllParts(user,demoProjectConf.datapathPrefix);
    assert.isUndefined(valOrError.error, "Unexpected error from createDemoProjectForUser: " + valOrError.error);
    let results = valOrError.val;
    project = results.project;
    experiment = results.experiment;
    process_list = results.processes;
    sample_list = results.samples;
    file_list = results.files;

    let projectId = project.id;
    let experimentId = experiment.id;

    let name = random_name();
    let description = "Changed the name of the demo project to " + name;
    let updateData = {
        name: name,
        description: description
    };
    let updated_project = yield projects.update(project.id, updateData);
    assert.equal(updated_project.otype, "project");
    assert.equal(updated_project.owner, userId);
    assert.equal(updated_project.name, name);
    assert.equal(updated_project.description, description);
    assert.equal(updated_project.id,projectId);
    project = updated_project;

    for (let i = 0; i < process_list.length; i++) {
        let process = process_list[i];
        yield processes.deleteProcess(project.id,process.id);
    }

    let simple = true;
    results = yield experiments.getProcessesForExperiment(experimentId, simple);
    assert.equal(results.val.length,0);

    let rv = yield r.table('experiment2sample')
        .getAll(experimentId,{index:'experiment_id'}).delete();

    assert.equal(rv.deleted, 7);

    yield testHelpers.setUpFakeExperimentNoteData(experimentId,userId);
    yield testHelpers.setUpAdditionalExperimentTaskData(experimentId,userId);

});

describe('Feature - Experiments: ', function() {
    describe('Delete Experiment - basic parts: ', function () {
        it('deletes experiment part: experiment-notes', function* (){
            // ---- experimentnote ----
            // experiment2experimentnote
            // experimentnotes

            let projectId = project.id;
            assert.isOk(projectId);
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            let idList = yield r.table('experiment2experimentnote')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_note_id',r.table('experimentnotes'))
                .zip().getField('experiment_note_id');

            let delete_msg = yield r.table('experimentnotes').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experiment2experimentnote')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 1);
        });
        it('deletes experiment part: experiment-tasks', function* (){
            let projectId = project.id;
            assert.isOk(projectId);
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            // get taskId and processId
            let idList = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_task_id',r.table('experimenttasks'))
                .zip().getField('experiment_task_id');
            assert.isOk(idList);
            assert.equal(idList.length,1);
            let taskId = idList[0];
            assert.isOk(taskId);

            let task = yield r.table('experimenttasks').get(taskId);
            assert.isOk(task);
            assert.isOk(task.process_id);
            let processId = task.process_id;

            // experiment2experimenttask
            // experimenttask2process
            // experimenttasks
            // processes

            idList = yield r.table('experimenttask2process')
                .getAll(taskId,{index:'experiment_task_id'})
                .eqJoin('process_id',r.table('processes'))
                .zip().getField('process_id');
            let delete_msg = yield r.table('processes').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experimenttask2process')
                .getAll(taskId,{index:'experiment_task_id'}).delete();
            assert.equal(delete_msg.deleted, 1);

            idList = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'})
                .eqJoin('experiment_task_id',r.table('experimenttasks'))
                .zip().getField('experiment_task_id');
            delete_msg = yield r.table('experimenttasks').getAll(r.args([...idList])).delete();
            assert.equal(delete_msg.deleted, 1);

            delete_msg = yield r.table('experiment2experimenttask')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 1);

        });
        it('deletes links between files and experiment', function* (){
            let experimentId = experiment.id;
            assert.isOk(experimentId);

            let delete_msg = yield r.table('experiment2datafile')
                .getAll(experimentId,{index:'experiment_id'}).delete();
            assert.equal(delete_msg.deleted, 16);
        });
    });
});
<|endoftext|>


<commit_before>import getRange from 'shared/parsing/getRangeForDygraph'

describe('getRangeForDygraphSpec', () => {
  it('gets the range for one timeSeries', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const actual = getRange(timeSeries)
    const expected = [1, 3]

    expect(actual).to.deep.equal(expected)
  })

  it('does not get range when a range is provided', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const providedRange = [0, 4]
    const actual = getRange(timeSeries, providedRange)

    expect(actual).to.deep.equal(providedRange)
  })

  it('gets the range for multiple timeSeries', () => {
    const timeSeries = [
      [new Date(1000), null, 1],
      [new Date(1000), 100, 1],
      [new Date(2000), null, 2],
      [new Date(3000), 200, 3],
    ]

    const actual = getRange(timeSeries)
    const expected = [1, 200]

    expect(actual).to.deep.equal(expected)
  })

  it('returns a null array of two elements when min and max are equal', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 1], [new Date(3000), 1]]
    const actual = getRange(timeSeries)
    const expected = [null, null]

    expect(actual).to.deep.equal(expected)
  })

  describe('when user provides a Kapacitor rule value', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: 20, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: -10, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })

    it('subtracts from positive values if Kapactior operator is "lower than"', () => {
      // TODO: go home
    })
  })

  describe('when user provides a Kapacitor rule rangeValue', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: 20, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: -10, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })
  })
})
<commit_msg>Add tests<commit_after>import getRange from 'shared/parsing/getRangeForDygraph'

describe('getRangeForDygraphSpec', () => {
  it('gets the range for one timeSeries', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const actual = getRange(timeSeries)
    const expected = [1, 3]

    expect(actual).to.deep.equal(expected)
  })

  it('does not get range when a range is provided', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const providedRange = [0, 4]
    const actual = getRange(timeSeries, providedRange)

    expect(actual).to.deep.equal(providedRange)
  })

  it('gets the range for multiple timeSeries', () => {
    const timeSeries = [
      [new Date(1000), null, 1],
      [new Date(1000), 100, 1],
      [new Date(2000), null, 2],
      [new Date(3000), 200, 3],
    ]

    const actual = getRange(timeSeries)
    const expected = [1, 200]

    expect(actual).to.deep.equal(expected)
  })

  it('returns a null array of two elements when min and max are equal', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 1], [new Date(3000), 1]]
    const actual = getRange(timeSeries)
    const expected = [null, null]

    expect(actual).to.deep.equal(expected)
  })

  describe('when user provides a Kapacitor rule value', () => {
    const defaultMax = 20
    const defaultMin = -10
    const positiveMin = 10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]
    const timeSeries1 = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), positiveMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: 20, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: -10, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })

    describe('when Kapacitor operator is "lower than" and value is outiside the range', () => {
      it('subtracts from a positive value', () => {
        const value = 5
        const rangeValue = ""
        const operator = "lower than"
        const kapacitorRuleValues = {value, rangeValue, operator}

        const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)
        expect(min).to.be.lessThan(value)
        expect(max).to.equal(defaultMax)
      })
    })
  })

  describe('when user provides a Kapacitor rule rangeValue', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: 20, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: -10, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })
  })
})
<|endoftext|>