<commit_before>/*global define*/
define([
	"dojo/_base/declare",
	"dojo/Evented",
	"dojo/on",
	"esri/map",
	"esri/graphic",
	"esri/geometry/jsonUtils",
	"esri/layers/FeatureLayer",
	"esri/InfoTemplate",
	"esri/symbols/SimpleMarkerSymbol",
	"esri/symbols/SimpleLineSymbol",
	"esri/Color",
	"esri/renderers/UniqueValueRenderer",
	"elc/elc-ui/main",
	"elc"
], function (declare, Evented, on, esriMap, Graphic, geometryJsonUtils, FeatureLayer, InfoTemplate, SimpleMarkerSymbol, SimpleLineSymbol, Color, UniqueValueRenderer, ElcUI, Elc) {
	var elcUI, routeLocator, pointResultsLayer, lineResultsLayer;

	var wsdotLogoGreen = new Color([0, 123, 95]);
	var eventColor = new Color([255, 100, 100]);

	var routePointOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255,255,255,0.8]), 2);
	var routePointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 12, routePointOutlineSymbol, wsdotLogoGreen);
	var eventPointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 12, eventLineSymbol, eventColor);
	var routeLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, wsdotLogoGreen, 3);
	var eventLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SHORTDOT, eventColor, 3);

	var pointRenderer = new UniqueValueRenderer(routePointSymbol, "IsEvent");
	var lineRenderer = new UniqueValueRenderer(routeLineSymbol, "IsEvent");

	pointRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventPointSymbol,
		value: 1
	});

	pointRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routePointSymbol,
		value: 0
	});

	lineRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventLineSymbol,
		value: 1
	});

	lineRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routeLineSymbol,
		value: 0
	});

	function RouteLocationGraphicSet(routeLocation) {
		var geometry, attributes, graphic;
		/** @member {?esri/Graphic} */
		this.routeFeature = null;
		/** @member {?esri/Graphic} */
		this.eventPointFeature = null;
		/** @member {?esri/Graphic} */
		this.connectorLineFeature = null;

		var spatialReference = { wkid: 3857 };

		if (routeLocation) {
			if (routeLocation.RouteGeometry) {
				if (!routeLocation.RouteGeometry.spatialReference) {
					routeLocation.RouteGeometry.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.RouteGeometry);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 0;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.routeFeature = graphic;
			}
			if (routeLocation.EventPoint) {
				if (!routeLocation.EventPoint.spatialReference) {
					routeLocation.EventPoint.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.EventPoint);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.eventPointFeature = graphic;
			}
			if (routeLocation.RouteGeometry && routeLocation.EventPoint) {
				geometry = geometryJsonUtils.fromJson({
					paths: [
						[
							[this.routeFeature.geometry.x, this.routeFeature.geometry.y],
							[this.eventPointFeature.geometry.x, this.eventPointFeature.geometry.y]
						]
					],
					spatialReference: spatialReference
				});
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.connectorLineFeature = graphic;
			}
		}
	}

	/**
	 * Creates popup content from graphic attributes.
	 * @param {esri/Graphic} graphic
	 * @returns {HTMLDListElement}
	 */
	function graphicToHtml(graphic) {
		var dl = document.createElement("dl"), dt, dd, v;
		var layer = graphic._layer;

		layer.fields.forEach(function (field) {
			if (graphic.attributes.hasOwnProperty(field.name)) {
				v = graphic.attributes[field.name];
				if (v || (v === 0 && !/Code$/i.test(field.name))) {
					dt = document.createElement("dt");
					dt.textContent = field.alias || field.name;
					dd = document.createElement("dd");
					dd.textContent = v;
					dl.appendChild(dt);
					dl.appendChild(dd);
				}
			}
		});
		return dl;
	}

	var ArcGisElcUI = declare(Evented, {
		constructor: function (domNode) {

			var self = this;

			/**
			 * Adds ELC results to feature layers.
			 */
			function addResultsToMap(elcResults) {
				var nonGraphics, graphics;
				if (elcResults && Array.isArray(elcResults)) {
					if (elcResults.length === 0) {
						self.emit("elc-results-not-found", { elcResults: elcResults });
					} else {
						pointResultsLayer.suspend();
						lineResultsLayer.suspend();
						nonGraphics = [];
						graphics = [];
						elcResults.forEach(function (routeLocation) {
							var gSet = new RouteLocationGraphicSet(routeLocation);
							if (gSet.routeFeature) {
								if (gSet.routeFeature.geometry.type === "point") {
									pointResultsLayer.add(gSet.routeFeature);
								} else if (gSet.routeFeature.geometry.type === "polyline") {
									lineResultsLayer.add(gSet.routeFeature);
								} else {
									console.warn("Unexpected geometry type", gSet.routeFeature);
								}
								graphics.push(gSet.routeFeature);
							} else {
								nonGraphics.push(routeLocation);
							}
							if (gSet.eventPointFeature) {
								pointResultsLayer.add(gSet.eventPointFeature);
							}
							if (gSet.connectorLineFeature) {
								lineResultsLayer.add(gSet.connectorLineFeature);
							}
							////if (routeLocation.RouteGeometry) {
							////	graphic = routeLocationToGraphic(routeLocation);
							////	if (graphic) {
							////		if (graphic.geometry.type === "point") {
							////			pointResultsLayer.add(graphic);
							////		} else if (graphic.geometry.type === "polyline") {
							////			lineResultsLayer.add(graphic);
							////		} else {
							////			console.warn("Unexpected geometry type", graphic);
							////		}
							////	}
							////	graphics.push(graphic);
							////} else {
							////	nonGraphics.push(routeLocation);
							////}
						});
						pointResultsLayer.resume();
						lineResultsLayer.resume();
						self.emit("elc-results-found", {
							elcResults: elcResults,
							graphics: graphics
						});
					}
				}
				if (nonGraphics && nonGraphics.length > 0) {
					self.emit("non-geometry-results-returned", {
						elcResults: elcResults,
						nonGeometryResults: nonGraphics
					});
				}
			}

			routeLocator = new Elc.RouteLocator();

			elcUI = new ElcUI(domNode);

			elcUI.root.addEventListener('find-route-location-submit', function (e) {
				var locations = [new Elc.RouteLocation(e.detail)];
				routeLocator.findRouteLocations({
					locations: locations,
					outSR: 3857
				}).then(addResultsToMap, function (error) {
					console.error("find route location error", error);
				});
			});

			elcUI.root.addEventListener('find-nearest-route-location-submit', function (e) {
				// Setup map click event. This should only occur ONCE.
				on.once(self.map, "click", function (mapEvt) {
					var mapPoint = mapEvt.mapPoint;
					routeLocator.findNearestRouteLocations({
						coordinates: [mapPoint.x, mapPoint.y],
						searchRadius: e.detail.radius,
						inSR: mapPoint.spatialReference.wkid,
						outSR: mapPoint.spatialReference.wkid,
						referenceDate: new Date()
					}).then(function (elcResults) {
						if (elcResults.length) {
							elcResults = elcResults.map(function (r) {
								r = r.toJSON();
								delete r.Coordinates;
								delete r.Angle;
								delete r.RouteGeometry;
								delete r.RealignmentDate;
								delete r.ArmCalcReturnCode;
								delete r.ArmCalcReturnMessage;
								//delete r.EventPoint;
								return new Elc.RouteLocation(r);
							});
							routeLocator.findRouteLocations({
								locations: elcResults,
								outSR: 3857
							}).then(addResultsToMap, function (error) {
								console.error("find route location error", error);
							});
						} else {
							addResultsToMap(elcResults).then(addResultsToMap);
						}
					}, function (error) {
						console.error("find nearest route location error", error);
					});
				});
			});

			var infoTemplate = new InfoTemplate("Route Location", graphicToHtml);

			var elcFields = [
					{ name: "Id", type: "esriFieldTypeInteger", alias: "ID" },
					{ name: "Route", type: "esriFieldTypeString" },
					{ name: "Decrease", type: "esriFieldTypeSmallInteger" },
					{ name: "Arm", type: "esriFieldTypeDouble", alias: "ARM" },
					{ name: "Srmp", type: "esriFieldTypeDouble", alias: "SRMP" },
					{ name: "Back", type: "esriFieldTypeSmallInteger" },
					{ name: "ReferenceDate", type: "esriFieldTypeDate", alias: "Reference Date" },
					{ name: "ResponseDate", type: "esriFieldTypeDate", alias: "Response Date" },
					{ name: "RealignmentDate", type: "esriFieldTypeDate", alias: "Realignment Date" },
					{ name: "EndArm", type: "esriFieldTypeDouble", alias: "End ARM" },
					{ name: "EndSrmp", type: "esriFieldTypeDouble", alias: "End SRMP" },
					{ name: "EndBack", type: "esriFieldTypeSmallInteger", alias: "End Back" },
					{ name: "EndReferenceDate", type: "esriFieldTypeDate", alias: "End Reference Date" },
					{ name: "EndResponseDate", type: "esriFieldTypeDate", alias: "End Response Date" },
					{ name: "EndRealignDate", type: "esriFieldTypeDate", alias: "End Realignment Date" },
					{ name: "ArmCalcReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc Return Code" },
					{ name: "ArmCalcEndReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc End Return Code" },
					{ name: "ArmCalcReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc Return Message" },
					{ name: "ArmCalcEndReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc End Return Message" },
					{ name: "LocatingError", type: "esriFieldTypeString", alias: "Locating Error" },
					//{ name: "RouteGeometry", type: "esriFieldTypeGeometry" },
					//{ name: "EventPoint", type: "esriFieldTypeGeometry" },
					{ name: "Distance", type: "esriFieldTypeDouble" },
					{ name: "Angle", type: "esriFieldTypeDouble" },
					{ name: "IsEvent", type: "esriFieldTypeSmallInteger", alias: "Is Event"}
			];

			lineResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPolyline",
					fields: elcFields
				}
			}, {
				id: "Located Segment",
				infoTemplate: infoTemplate
			});
			lineResultsLayer.setRenderer(lineRenderer);

			pointResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPoint",
					fields: elcFields
				}
			}, {
				id: "Located Milepost",
				infoTemplate: infoTemplate
			});

			pointResultsLayer.setRenderer(pointRenderer);

			this._layers = {
				pointResults: pointResultsLayer,
				lineResults: lineResultsLayer
			};

			var clearBtn = document.querySelector(".clear-graphics-button");
			clearBtn.addEventListener("click", function () {
				pointResultsLayer.clear();
				lineResultsLayer.clear();
			});
		},
		setMap: function (map) {
			map.addLayer(this._layers.lineResults);
			map.addLayer(this._layers.pointResults);
			this.map = map;
		}
	});

	return ArcGisElcUI;
});<commit_msg>Deleted commented-out code. :x:<commit_after>/*global define*/
define([
	"dojo/_base/declare",
	"dojo/Evented",
	"dojo/on",
	"esri/map",
	"esri/graphic",
	"esri/geometry/jsonUtils",
	"esri/layers/FeatureLayer",
	"esri/InfoTemplate",
	"esri/symbols/SimpleMarkerSymbol",
	"esri/symbols/SimpleLineSymbol",
	"esri/Color",
	"esri/renderers/UniqueValueRenderer",
	"elc/elc-ui/main",
	"elc"
], function (declare, Evented, on, esriMap, Graphic, geometryJsonUtils, FeatureLayer, InfoTemplate, SimpleMarkerSymbol, SimpleLineSymbol, Color, UniqueValueRenderer, ElcUI, Elc) {
	var elcUI, routeLocator, pointResultsLayer, lineResultsLayer;

	var wsdotLogoGreen = new Color([0, 123, 95]);
	var eventColor = new Color([255, 100, 100]);

	var routePointOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255,255,255,0.8]), 2);
	var routePointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 12, routePointOutlineSymbol, wsdotLogoGreen);
	var eventPointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 12, eventLineSymbol, eventColor);
	var routeLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, wsdotLogoGreen, 3);
	var eventLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SHORTDOT, eventColor, 3);

	var pointRenderer = new UniqueValueRenderer(routePointSymbol, "IsEvent");
	var lineRenderer = new UniqueValueRenderer(routeLineSymbol, "IsEvent");

	pointRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventPointSymbol,
		value: 1
	});

	pointRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routePointSymbol,
		value: 0
	});

	lineRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventLineSymbol,
		value: 1
	});

	lineRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routeLineSymbol,
		value: 0
	});

	function RouteLocationGraphicSet(routeLocation) {
		var geometry, attributes, graphic;
		/** @member {?esri/Graphic} */
		this.routeFeature = null;
		/** @member {?esri/Graphic} */
		this.eventPointFeature = null;
		/** @member {?esri/Graphic} */
		this.connectorLineFeature = null;

		var spatialReference = { wkid: 3857 };

		if (routeLocation) {
			if (routeLocation.RouteGeometry) {
				if (!routeLocation.RouteGeometry.spatialReference) {
					routeLocation.RouteGeometry.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.RouteGeometry);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 0;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.routeFeature = graphic;
			}
			if (routeLocation.EventPoint) {
				if (!routeLocation.EventPoint.spatialReference) {
					routeLocation.EventPoint.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.EventPoint);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.eventPointFeature = graphic;
			}
			if (routeLocation.RouteGeometry && routeLocation.EventPoint) {
				geometry = geometryJsonUtils.fromJson({
					paths: [
						[
							[this.routeFeature.geometry.x, this.routeFeature.geometry.y],
							[this.eventPointFeature.geometry.x, this.eventPointFeature.geometry.y]
						]
					],
					spatialReference: spatialReference
				});
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.connectorLineFeature = graphic;
			}
		}
	}

	/**
	 * Creates popup content from graphic attributes.
	 * @param {esri/Graphic} graphic
	 * @returns {HTMLDListElement}
	 */
	function graphicToHtml(graphic) {
		var dl = document.createElement("dl"), dt, dd, v;
		var layer = graphic._layer;

		layer.fields.forEach(function (field) {
			if (graphic.attributes.hasOwnProperty(field.name)) {
				v = graphic.attributes[field.name];
				if (v || (v === 0 && !/Code$/i.test(field.name))) {
					dt = document.createElement("dt");
					dt.textContent = field.alias || field.name;
					dd = document.createElement("dd");
					dd.textContent = v;
					dl.appendChild(dt);
					dl.appendChild(dd);
				}
			}
		});
		return dl;
	}

	var ArcGisElcUI = declare(Evented, {
		constructor: function (domNode) {

			var self = this;

			/**
			 * Adds ELC results to feature layers.
			 */
			function addResultsToMap(elcResults) {
				var nonGraphics, graphics;
				if (elcResults && Array.isArray(elcResults)) {
					if (elcResults.length === 0) {
						self.emit("elc-results-not-found", { elcResults: elcResults });
					} else {
						pointResultsLayer.suspend();
						lineResultsLayer.suspend();
						nonGraphics = [];
						graphics = [];
						elcResults.forEach(function (routeLocation) {
							var gSet = new RouteLocationGraphicSet(routeLocation);
							if (gSet.routeFeature) {
								if (gSet.routeFeature.geometry.type === "point") {
									pointResultsLayer.add(gSet.routeFeature);
								} else if (gSet.routeFeature.geometry.type === "polyline") {
									lineResultsLayer.add(gSet.routeFeature);
								} else {
									console.warn("Unexpected geometry type", gSet.routeFeature);
								}
								graphics.push(gSet.routeFeature);
							} else {
								nonGraphics.push(routeLocation);
							}
							if (gSet.eventPointFeature) {
								pointResultsLayer.add(gSet.eventPointFeature);
							}
							if (gSet.connectorLineFeature) {
								lineResultsLayer.add(gSet.connectorLineFeature);
							}

						});
						pointResultsLayer.resume();
						lineResultsLayer.resume();
						self.emit("elc-results-found", {
							elcResults: elcResults,
							graphics: graphics
						});
					}
				}
				if (nonGraphics && nonGraphics.length > 0) {
					self.emit("non-geometry-results-returned", {
						elcResults: elcResults,
						nonGeometryResults: nonGraphics
					});
				}
			}

			routeLocator = new Elc.RouteLocator();

			elcUI = new ElcUI(domNode);

			elcUI.root.addEventListener('find-route-location-submit', function (e) {
				var locations = [new Elc.RouteLocation(e.detail)];
				routeLocator.findRouteLocations({
					locations: locations,
					outSR: 3857
				}).then(addResultsToMap, function (error) {
					console.error("find route location error", error);
				});
			});

			elcUI.root.addEventListener('find-nearest-route-location-submit', function (e) {
				// Setup map click event. This should only occur ONCE.
				on.once(self.map, "click", function (mapEvt) {
					var mapPoint = mapEvt.mapPoint;
					routeLocator.findNearestRouteLocations({
						coordinates: [mapPoint.x, mapPoint.y],
						searchRadius: e.detail.radius,
						inSR: mapPoint.spatialReference.wkid,
						outSR: mapPoint.spatialReference.wkid,
						referenceDate: new Date()
					}).then(function (elcResults) {
						if (elcResults.length) {
							elcResults = elcResults.map(function (r) {
								r = r.toJSON();
								delete r.Coordinates;
								delete r.Angle;
								delete r.RouteGeometry;
								delete r.RealignmentDate;
								delete r.ArmCalcReturnCode;
								delete r.ArmCalcReturnMessage;
								//delete r.EventPoint;
								return new Elc.RouteLocation(r);
							});
							routeLocator.findRouteLocations({
								locations: elcResults,
								outSR: 3857
							}).then(addResultsToMap, function (error) {
								console.error("find route location error", error);
							});
						} else {
							addResultsToMap(elcResults).then(addResultsToMap);
						}
					}, function (error) {
						console.error("find nearest route location error", error);
					});
				});
			});

			var infoTemplate = new InfoTemplate("Route Location", graphicToHtml);

			var elcFields = [
					{ name: "Id", type: "esriFieldTypeInteger", alias: "ID" },
					{ name: "Route", type: "esriFieldTypeString" },
					{ name: "Decrease", type: "esriFieldTypeSmallInteger" },
					{ name: "Arm", type: "esriFieldTypeDouble", alias: "ARM" },
					{ name: "Srmp", type: "esriFieldTypeDouble", alias: "SRMP" },
					{ name: "Back", type: "esriFieldTypeSmallInteger" },
					{ name: "ReferenceDate", type: "esriFieldTypeDate", alias: "Reference Date" },
					{ name: "ResponseDate", type: "esriFieldTypeDate", alias: "Response Date" },
					{ name: "RealignmentDate", type: "esriFieldTypeDate", alias: "Realignment Date" },
					{ name: "EndArm", type: "esriFieldTypeDouble", alias: "End ARM" },
					{ name: "EndSrmp", type: "esriFieldTypeDouble", alias: "End SRMP" },
					{ name: "EndBack", type: "esriFieldTypeSmallInteger", alias: "End Back" },
					{ name: "EndReferenceDate", type: "esriFieldTypeDate", alias: "End Reference Date" },
					{ name: "EndResponseDate", type: "esriFieldTypeDate", alias: "End Response Date" },
					{ name: "EndRealignDate", type: "esriFieldTypeDate", alias: "End Realignment Date" },
					{ name: "ArmCalcReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc Return Code" },
					{ name: "ArmCalcEndReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc End Return Code" },
					{ name: "ArmCalcReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc Return Message" },
					{ name: "ArmCalcEndReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc End Return Message" },
					{ name: "LocatingError", type: "esriFieldTypeString", alias: "Locating Error" },
					//{ name: "RouteGeometry", type: "esriFieldTypeGeometry" },
					//{ name: "EventPoint", type: "esriFieldTypeGeometry" },
					{ name: "Distance", type: "esriFieldTypeDouble" },
					{ name: "Angle", type: "esriFieldTypeDouble" },
					{ name: "IsEvent", type: "esriFieldTypeSmallInteger", alias: "Is Event"}
			];

			lineResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPolyline",
					fields: elcFields
				}
			}, {
				id: "Located Segment",
				infoTemplate: infoTemplate
			});
			lineResultsLayer.setRenderer(lineRenderer);

			pointResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPoint",
					fields: elcFields
				}
			}, {
				id: "Located Milepost",
				infoTemplate: infoTemplate
			});

			pointResultsLayer.setRenderer(pointRenderer);

			this._layers = {
				pointResults: pointResultsLayer,
				lineResults: lineResultsLayer
			};

			var clearBtn = document.querySelector(".clear-graphics-button");
			clearBtn.addEventListener("click", function () {
				pointResultsLayer.clear();
				lineResultsLayer.clear();
			});
		},
		setMap: function (map) {
			map.addLayer(this._layers.lineResults);
			map.addLayer(this._layers.pointResults);
			this.map = map;
		}
	});

	return ArcGisElcUI;
});<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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.
 */
({
    /**
     * Verify that aura:beforeLayoutChange is fired before a layout change
     * IE789 excluded: history service was not previously supported in IE
     */
    testEventsPreAndPostLayoutChange:{
	browsers:["-IE7", "-IE8", "-IE9"],
	testLabels : ["UnAdaptableTest"],
	test:[
	      function(cmp){
		  $A.test.setTestTimeout(5000);
		  //Use history service to change layout to STEP 2
		  $A.historyService.set("Step2");
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
        		}, function() {
        		    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
        			    {"layoutName":"Step2", "title": "Step 2"}, true);
        		});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
          		}, function() {
          		    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
          			    {"layoutName":"Step2", "title": "Step 2"}, false);
          		});
	      },function(cmp){
		  //Use layout service to change layout to STEP 1
		  $A.layoutService.layout("Step1");
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
      			}, function() {
      			    	this.assertEvntParams(cmp,{"prevLayoutName": "Step2", "prevTitle":"Step 2"},
      			    		{"layoutName":"Step1", "title": "Step 1"}, true);
      			});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
        		}, function() {
        		    this.assertEvntParams(cmp,{"prevLayoutName": "Step2", "prevTitle":"Step 2"},
  			    		{"layoutName":"Step1", "title": "Step 1"}, false);
        		});
	      },function(cmp){
		  //Use layout service to go back to previous state, STEP 2
		  $A.layoutService.back();
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
    			}, function() {
    			    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
    				    	{"layoutName":"Step2", "title": "Step 2"}, true);
    			});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
      			}, function() {
      			    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
			    		{"layoutName":"Step2", "title": "Step 2"}, false);
      			});
	      }]
    },
    assertEvntParams: function(cmp , prevLayoutParams, currentLayoutParams, preLayoutChange ){
	var evt;
	if(preLayoutChange){
	    $A.test.assertDefined(cmp._beforeLayoutChangeEvt, "aura:beforeLayoutChange event not fired before layout changed");
	    evt = cmp._beforeLayoutChangeEvt;
	    cmp._layoutChanging = false;
	}else{
	    $A.test.assertDefined(cmp._layoutChangeEvt, "aura:layoutChange event not fired after layout changed");
	    evt = cmp._layoutChangeEvt;
	    cmp._layoutChanged = false;
	}
	$A.test.assertEquals(currentLayoutParams.layoutName, evt.getParam("layoutName"));
	$A.test.assertEquals(currentLayoutParams.title, evt.getParam("title"));
	$A.test.assertEquals(prevLayoutParams.prevLayoutName, evt.getParam("prevLayoutName"));
	$A.test.assertEquals(prevLayoutParams.prevTitle, evt.getParam("prevTitle"));
    }

})<commit_msg>disable flapping test<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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.
 */
({
    /**
     * Verify that aura:beforeLayoutChange is fired before a layout change
     * IE789 excluded: history service was not previously supported in IE
     */
    //TODO: W-1635821 - fix this flapper
    _testEventsPreAndPostLayoutChange:{
	browsers:["-IE7", "-IE8", "-IE9"],
	testLabels : ["UnAdaptableTest"],
	test:[
	      function(cmp){
		  $A.test.setTestTimeout(5000);
		  //Use history service to change layout to STEP 2
		  $A.historyService.set("Step2");
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
        		}, function() {
        		    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
        			    {"layoutName":"Step2", "title": "Step 2"}, true);
        		});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
          		}, function() {
          		    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
          			    {"layoutName":"Step2", "title": "Step 2"}, false);
          		});
	      },function(cmp){
		  //Use layout service to change layout to STEP 1
		  $A.layoutService.layout("Step1");
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
      			}, function() {
      			    	this.assertEvntParams(cmp,{"prevLayoutName": "Step2", "prevTitle":"Step 2"},
      			    		{"layoutName":"Step1", "title": "Step 1"}, true);
      			});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
        		}, function() {
        		    this.assertEvntParams(cmp,{"prevLayoutName": "Step2", "prevTitle":"Step 2"},
  			    		{"layoutName":"Step1", "title": "Step 1"}, false);
        		});
	      },function(cmp){
		  //Use layout service to go back to previous state, STEP 2
		  $A.layoutService.back();
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanging;
    			}, function() {
    			    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
    				    	{"layoutName":"Step2", "title": "Step 2"}, true);
    			});
		  $A.test.addWaitFor(true, function() {
			return cmp._layoutChanged;
      			}, function() {
      			    this.assertEvntParams(cmp,{"prevLayoutName": "default", "prevTitle":"Step 0"},
			    		{"layoutName":"Step2", "title": "Step 2"}, false);
      			});
	      }]
    },
    assertEvntParams: function(cmp , prevLayoutParams, currentLayoutParams, preLayoutChange ){
	var evt;
	if(preLayoutChange){
	    $A.test.assertDefined(cmp._beforeLayoutChangeEvt, "aura:beforeLayoutChange event not fired before layout changed");
	    evt = cmp._beforeLayoutChangeEvt;
	    cmp._layoutChanging = false;
	}else{
	    $A.test.assertDefined(cmp._layoutChangeEvt, "aura:layoutChange event not fired after layout changed");
	    evt = cmp._layoutChangeEvt;
	    cmp._layoutChanged = false;
	}
	$A.test.assertEquals(currentLayoutParams.layoutName, evt.getParam("layoutName"));
	$A.test.assertEquals(currentLayoutParams.title, evt.getParam("title"));
	$A.test.assertEquals(prevLayoutParams.prevLayoutName, evt.getParam("prevLayoutName"));
	$A.test.assertEquals(prevLayoutParams.prevTitle, evt.getParam("prevTitle"));
    }

})<|endoftext|>


<commit_before>import * as utils from "base/utils";
import Hook from "models/hook";

/*!
 * VIZABI Color Model (hook)
 */

const defaultPalettes = {
  "_continuous": {
    "_default": "#ffb600",
    "0": "hsl(270, 80%, 55%)",
    "25": "hsl(202.5, 80%, 55%)",
    "50": "hsl(135, 80%, 55%)",
    "75": "hsl(48, 70%, 62%)",
    "100": "hsl(0, 80%, 55%)"
  },
  "_discrete": {
    "_default": "#ffb600",
    "0": "#4cd843",
    "1": "#e83739",
    "2": "#ff7f00",
    "3": "#c027d4",
    "4": "#d66425",
    "5": "#0ab8d8",
    "6": "#bcfa83",
    "7": "#ff8684",
    "8": "#ffb04b",
    "9": "#f599f5",
    "10": "#f4f459",
    "11": "#7fb5ed"
  },
  "_default": {
    "_default": "#ffb600"
  }
};

const ColorModel = Hook.extend({

  /**
   * Default values for this model
   */
  getClassDefaults() {
    const defaults = {
      use: null,
      which: null,
      scaleType: null,
      syncModels: [],
      palette: {},
      paletteLabels: null,
      allow: {
        //this is almost everything, but not "nominal", so no random strings like "name"
        scales: ["linear", "log", "genericLog", "time", "pow", "ordinal"]
      }
    };
    return utils.deepExtend(this._super(), defaults);
  },

  autoconfigureModel() {
    if (!this.which && this.autoconfig) {
      const concept = this.dataSource.getConcept(this.autoconfig);

      if (concept) {
        const obj = {
          which: concept.concept,
          use: concept.use || "indicator",
          scaleType: (concept.scales && concept.scales[0] ? concept.scales[0] : "linear")
        };
        this.set(obj);
      } else {
        const obj = {
          which: "_default",
          use: "constant",
          scaleType: "ordinal"
        };
        this.set(obj);
      }

      utils.printAutoconfigResult(this);
    }
  },

  /**
   * Initializes the color hook
   * @param {Object} values The initial values of this model
   * @param parent A reference to the parent model
   * @param {Object} bind Initial events to bind
   */
  init(name, values, parent, bind) {
    const _this = this;
    this._type = "color";

    this._super(name, values, parent, bind);

    this._syncModelReferences = {};
    this._hasDefaultColor = false;

    this.on("hook_change", () => {
      if (_this._readyOnce || _this._loadCall) return;

      _this._setSyncModels();

      if (_this.palette && Object.keys(_this.palette._data).length !== 0) {
        const defaultPalette = _this.getDefaultPalette();
        const currentPalette = _this.getPalette(true);
        const palette = {};
        //extend partial current palette with default palette and
        //switch current palette elements which equals
        //default palette elments to nonpersistent state
        Object.keys(defaultPalette).forEach(key => {
          if (!currentPalette[key] || defaultPalette[key] == currentPalette[key]) palette[key] = defaultPalette[key];
        });
        _this.set("palette", palette, false, false);
      }
    });
  },

  setInterModelListeners() {
    this._super();
    this._setSyncModels();
  },

  // args: {colorID, shadeID}
  getColorShade(args) {
    const palette = this.getPalette();

    if (!args) return utils.warn("getColorShade() is missing arguments");

    // if colorID is not given or not found in the palette, replace it with default color
    if (!args.colorID || !palette[args.colorID]) args.colorID = "_default";

    // if the resolved colr value is not an array (has only one shade) -- return it
    if (!utils.isArray(palette[args.colorID])) return args.shadeID == "shade" ? d3.rgb(palette[args.colorID]).darker(0.5).toString() : palette[args.colorID];

    const conceptpropsColor = this.getConceptprops().color;
    const shade = args.shadeID && conceptpropsColor && conceptpropsColor.shades && conceptpropsColor.shades[args.shadeID] ? conceptpropsColor.shades[args.shadeID] : 0;

    return palette[args.colorID][shade];

  },

  /**
   * Get the above constants
   */
  isUserSelectable() {
    const conceptpropsColor = this.getConceptprops().color;
    return conceptpropsColor == null || conceptpropsColor.selectable == null || conceptpropsColor.selectable;
  },

  setWhich(newValue) {
    if (this.palette) this.palette._data = {};
    this._super(newValue);
    this._setSyncModels();
  },

  _setSyncModels() {
    const _this = this;
    this.syncModels.forEach(modelName => {
      //fetch the model to sync, it's marker and entities
      const model = _this.getClosestModel(modelName);
      const marker = model.isHook() ? model._parent : model;
      const entities = marker.getClosestModel(marker.space[0]);

      //save the references here locally
      _this._syncModelReferences[modelName] = { model, marker, entities };

      if (_this.isDiscrete()) _this._setSyncModel(model, marker, entities);
    });
  },

  _setSyncModel(model, marker, entities) {
    if (model == marker) {
      const newFilter = {
        dim: this.which,
        show: {}
      };
      marker.setDataSourceForAllSubhooks(this.data);
      entities.set(newFilter, false, false);
    } else {
      if (model.isDiscrete() && model.use !== "constant") model.set({ which: this.which, data: this.data }, false, false);
    }
  },

  getColorlegendMarker() {
    return (this._syncModelReferences["marker_colorlegend"] || {})["marker"];
  },

  getColorlegendEntities() {
    return (this._syncModelReferences["marker_colorlegend"] || {})["entities"];
  },

  /**
   * set color
   */
  setColor(value, pointer, persistent) {
    let newRange;
    const palette = this.getPalette();
    if (this.isDiscrete()) {
      const paletteLength = Object.keys(palette).length;
      const index = this.scale.domain().indexOf(pointer) % paletteLength;
      newRange = this.scale.range();
      newRange[index] = value;
    } else {
      palette[pointer] = value;
      newRange = utils.values(palette);
    }
    this.scale.range(newRange);
    this.palette.set(pointer, value, persistent, persistent);
  },

  /**
   * maps the value to this hook's specifications
   * @param value Original value
   * @returns hooked value
   */
  mapValue(value) {
    //if the property value does not exist, supply the _default
    // otherwise the missing value would be added to the domain
    if (this.scale != null && this.isDiscrete() && this._hasDefaultColor && this.scale.domain().indexOf(value) == -1) value = "_default";
    return this._super(value);
  },

  getDefaultPalette() {
    const conceptpropsColor = this.getConceptprops().color;
    let palette;

    this.discreteDefaultPalette = false;

    if (conceptpropsColor && conceptpropsColor.palette) {
      //specific color palette from hook concept properties
      palette = utils.clone(conceptpropsColor.palette);
    } else if (defaultPalettes[this.which]) {
      //color palette for this.which exists in palette defaults
      palette = utils.clone(defaultPalettes[this.which]);
    } else if (this.use === "constant") {
      //an explicit hex color constant #abc or #adcdef is provided
      if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/.test(this.which)) {
        palette = { "_default": this.which };
      } else {
        palette = utils.clone(defaultPalettes["_default"]);
      }
    } else {
      palette = utils.clone(defaultPalettes[this.isDiscrete() ? "_discrete" : "_continuous"]);
      this.discreteDefaultPalette = true;
    }

    return palette;
  },

  _getPaletteLabels() {
    const conceptpropsColor = this.getConceptprops().color;
    let paletteLabels = null;

    if (conceptpropsColor && conceptpropsColor.paletteLabels) {
      //specific color palette from hook concept properties
      paletteLabels = utils.clone(conceptpropsColor.paletteLabels);
    }
    return paletteLabels;
  },

  getPaletteLabels() {
    return this.paletteLabels.getPlainObject();
  },

  getPalette(includeDefault) {
    //rebuild palette if it's empty
    if (!this.palette || Object.keys(this.palette._data).length === 0) {
      const palette = this.getDefaultPalette();
      this.set("palette", palette, false, false);
      const paletteLabels = this._getPaletteLabels();
      this.set("paletteLabels", paletteLabels, false, false);
    }
    const palette = this.palette.getPlainObject();
    if (this.use === "indicator" && !includeDefault) {
      delete palette["_default"];
    }
    return palette;
  },

  /**
   * Gets the domain for this hook
   * @returns {Array} domain
   */
  buildScale(scaleType = this.scaleType) {
    const _this = this;

    const paletteObject = _this.getPalette();
    let domain = Object.keys(paletteObject);
    let range = utils.values(paletteObject);

    this._hasDefaultColor = domain.indexOf("_default") > -1;

    if (scaleType == "time") {

      const timeMdl = this._space.time;
      const limits = timeMdl.splash ?
        { min: timeMdl.parse(timeMdl.startOrigin), max: timeMdl.parse(timeMdl.endOrigin) }
        :
        { min: timeMdl.start, max: timeMdl.end };

      const singlePoint = (limits.max - limits.min == 0);

      domain = domain.sort((a, b) => a - b);
      range = domain.map(m => singlePoint ? paletteObject[domain[0]] : paletteObject[m]);
      domain = domain.map(m => limits.min.valueOf() + m / 100 * (limits.max.valueOf() - limits.min.valueOf()));

      this.scale = d3.scaleUtc()
        .domain(domain)
        .range(range)
        .interpolate(d3.interpolateCubehelix);

    } else if (!this.isDiscrete()) {

      let limits = this.getLimits(this.which);
      //default domain is based on limits
      limits = [limits.min, limits.max];

      const singlePoint = (limits[1] - limits[0] == 0);

      domain = domain.sort((a, b) => a - b);
      range = domain.map(m => singlePoint ? paletteObject[domain[0]] : paletteObject[m]);
      domain = domain.map(m => limits[0] + m / 100 * (limits[1] - limits[0]));

      if (d3.min(domain) <= 0 && d3.max(domain) >= 0 && scaleType === "log") scaleType = "genericLog";

      if (scaleType === "log" || scaleType === "genericLog") {
        const s = d3.scaleGenericlog()
          .domain(limits)
          .range(limits);
        domain = domain.map(d => s.invert(d));
      }
      this.scale = d3[`scale${utils.capitalize(scaleType)}`]()
        .domain(domain)
        .range(range)
        .interpolate(d3.interpolateCubehelix);

    } else {
      range = range.map(m => utils.isArray(m) ? m[0] : m);

      scaleType = "ordinal";

      if (this.discreteDefaultPalette) domain = domain.concat(this.getUnique(this.which));
      this.scale = d3[`scale${utils.capitalize(scaleType)}`]()
        .domain(domain)
        .range(range);
    }

    this.scaleType = scaleType;
  }

});

export default ColorModel;
<commit_msg>fix color jumping on reload if color scale use default discrete palette #2758 (#2793)<commit_after>import * as utils from "base/utils";
import Hook from "models/hook";

/*!
 * VIZABI Color Model (hook)
 */

const defaultPalettes = {
  "_continuous": {
    "_default": "#ffb600",
    "0": "hsl(270, 80%, 55%)",
    "25": "hsl(202.5, 80%, 55%)",
    "50": "hsl(135, 80%, 55%)",
    "75": "hsl(48, 70%, 62%)",
    "100": "hsl(0, 80%, 55%)"
  },
  "_discrete": {
    "_default": "#ffb600",
    "0": "#4cd843",
    "1": "#e83739",
    "2": "#ff7f00",
    "3": "#c027d4",
    "4": "#d66425",
    "5": "#0ab8d8",
    "6": "#bcfa83",
    "7": "#ff8684",
    "8": "#ffb04b",
    "9": "#f599f5",
    "10": "#f4f459",
    "11": "#7fb5ed"
  },
  "_default": {
    "_default": "#ffb600"
  }
};

const ColorModel = Hook.extend({

  /**
   * Default values for this model
   */
  getClassDefaults() {
    const defaults = {
      use: null,
      which: null,
      scaleType: null,
      syncModels: [],
      palette: {},
      paletteLabels: null,
      allow: {
        //this is almost everything, but not "nominal", so no random strings like "name"
        scales: ["linear", "log", "genericLog", "time", "pow", "ordinal"]
      }
    };
    return utils.deepExtend(this._super(), defaults);
  },

  autoconfigureModel() {
    if (!this.which && this.autoconfig) {
      const concept = this.dataSource.getConcept(this.autoconfig);

      if (concept) {
        const obj = {
          which: concept.concept,
          use: concept.use || "indicator",
          scaleType: (concept.scales && concept.scales[0] ? concept.scales[0] : "linear")
        };
        this.set(obj);
      } else {
        const obj = {
          which: "_default",
          use: "constant",
          scaleType: "ordinal"
        };
        this.set(obj);
      }

      utils.printAutoconfigResult(this);
    }
  },

  /**
   * Initializes the color hook
   * @param {Object} values The initial values of this model
   * @param parent A reference to the parent model
   * @param {Object} bind Initial events to bind
   */
  init(name, values, parent, bind) {
    const _this = this;
    this._type = "color";

    this._super(name, values, parent, bind);

    this._syncModelReferences = {};
    this._hasDefaultColor = false;

    this.on("hook_change", () => {
      if (_this._readyOnce || _this._loadCall) return;

      _this._setSyncModels();

      if (_this.palette && Object.keys(_this.palette._data).length !== 0) {
        const defaultPalette = _this.getDefaultPalette();
        const currentPalette = _this.getPalette(true);
        const palette = {};
        //extend partial current palette with default palette and
        //switch current palette elements which equals
        //default palette elments to nonpersistent state
        Object.keys(defaultPalette).forEach(key => {
          if (!currentPalette[key] || defaultPalette[key] == currentPalette[key]) palette[key] = defaultPalette[key];
        });
        _this.set("palette", palette, false, false);
      }
    });
  },

  setInterModelListeners() {
    this._super();
    this._setSyncModels();
  },

  // args: {colorID, shadeID}
  getColorShade(args) {
    const palette = this.getPalette();

    if (!args) return utils.warn("getColorShade() is missing arguments");

    // if colorID is not given or not found in the palette, replace it with default color
    if (!args.colorID || !palette[args.colorID]) args.colorID = "_default";

    // if the resolved colr value is not an array (has only one shade) -- return it
    if (!utils.isArray(palette[args.colorID])) return args.shadeID == "shade" ? d3.rgb(palette[args.colorID]).darker(0.5).toString() : palette[args.colorID];

    const conceptpropsColor = this.getConceptprops().color;
    const shade = args.shadeID && conceptpropsColor && conceptpropsColor.shades && conceptpropsColor.shades[args.shadeID] ? conceptpropsColor.shades[args.shadeID] : 0;

    return palette[args.colorID][shade];

  },

  /**
   * Get the above constants
   */
  isUserSelectable() {
    const conceptpropsColor = this.getConceptprops().color;
    return conceptpropsColor == null || conceptpropsColor.selectable == null || conceptpropsColor.selectable;
  },

  setWhich(newValue) {
    if (this.palette) this.palette._data = {};
    this._super(newValue);
    this._setSyncModels();
  },

  _setSyncModels() {
    const _this = this;
    this.syncModels.forEach(modelName => {
      //fetch the model to sync, it's marker and entities
      const model = _this.getClosestModel(modelName);
      const marker = model.isHook() ? model._parent : model;
      const entities = marker.getClosestModel(marker.space[0]);

      //save the references here locally
      _this._syncModelReferences[modelName] = { model, marker, entities };

      if (_this.isDiscrete()) _this._setSyncModel(model, marker, entities);
    });
  },

  _setSyncModel(model, marker, entities) {
    if (model == marker) {
      const newFilter = {
        dim: this.which,
        show: {}
      };
      marker.setDataSourceForAllSubhooks(this.data);
      entities.set(newFilter, false, false);
    } else {
      if (model.isDiscrete() && model.use !== "constant") model.set({ which: this.which, data: this.data }, false, false);
    }
  },

  getColorlegendMarker() {
    return (this._syncModelReferences["marker_colorlegend"] || {})["marker"];
  },

  getColorlegendEntities() {
    return (this._syncModelReferences["marker_colorlegend"] || {})["entities"];
  },

  /**
   * set color
   */
  setColor(value, pointer, persistent) {
    let range;
    const palette = this.getPalette();
    if (this.isDiscrete()) {
      range = this.scale.range();
      range[this.scale.domain().indexOf(pointer)] = value;
    } else {
      palette[pointer] = value;
      range = utils.values(palette);
    }
    this.scale.range(range);
    this.palette.set(pointer, value, persistent, persistent);
  },

  /**
   * maps the value to this hook's specifications
   * @param value Original value
   * @returns hooked value
   */
  mapValue(value) {
    //if the property value does not exist, supply the _default
    // otherwise the missing value would be added to the domain
    if (this.scale != null && this.isDiscrete() && this._hasDefaultColor && this.scale.domain().indexOf(value) == -1) value = "_default";
    return this._super(value);
  },

  getDefaultPalette() {
    const conceptpropsColor = this.getConceptprops().color;
    let palette;

    this.discreteDefaultPalette = false;

    if (conceptpropsColor && conceptpropsColor.palette) {
      //specific color palette from hook concept properties
      palette = utils.clone(conceptpropsColor.palette);
    } else if (defaultPalettes[this.which]) {
      //color palette for this.which exists in palette defaults
      palette = utils.clone(defaultPalettes[this.which]);
    } else if (this.use === "constant") {
      //an explicit hex color constant #abc or #adcdef is provided
      if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/.test(this.which)) {
        palette = { "_default": this.which };
      } else {
        palette = utils.clone(defaultPalettes["_default"]);
      }
    } else {
      palette = utils.clone(defaultPalettes[this.isDiscrete() ? "_discrete" : "_continuous"]);
      this.discreteDefaultPalette = true;
    }

    return palette;
  },

  _getPaletteLabels() {
    const conceptpropsColor = this.getConceptprops().color;
    let paletteLabels = null;

    if (conceptpropsColor && conceptpropsColor.paletteLabels) {
      //specific color palette from hook concept properties
      paletteLabels = utils.clone(conceptpropsColor.paletteLabels);
    }
    return paletteLabels;
  },

  getPaletteLabels() {
    return this.paletteLabels.getPlainObject();
  },

  getPalette(includeDefault) {
    //rebuild palette if it's empty
    if (!this.palette || Object.keys(this.palette._data).length === 0) {
      const palette = this.getDefaultPalette();
      this.set("palette", palette, false, false);
      const paletteLabels = this._getPaletteLabels();
      this.set("paletteLabels", paletteLabels, false, false);
    }
    const palette = this.palette.getPlainObject();
    if (this.use === "indicator" && !includeDefault) {
      delete palette["_default"];
    }
    return palette;
  },

  /**
   * Gets the domain for this hook
   * @returns {Array} domain
   */
  buildScale(scaleType = this.scaleType) {
    const _this = this;

    const paletteObject = _this.getPalette();
    let domain = Object.keys(paletteObject);
    let range = utils.values(paletteObject);

    this._hasDefaultColor = domain.indexOf("_default") > -1;

    if (scaleType == "time") {

      const timeMdl = this._space.time;
      const limits = timeMdl.splash ?
        { min: timeMdl.parse(timeMdl.startOrigin), max: timeMdl.parse(timeMdl.endOrigin) }
        :
        { min: timeMdl.start, max: timeMdl.end };

      const singlePoint = (limits.max - limits.min == 0);

      domain = domain.sort((a, b) => a - b);
      range = domain.map(m => singlePoint ? paletteObject[domain[0]] : paletteObject[m]);
      domain = domain.map(m => limits.min.valueOf() + m / 100 * (limits.max.valueOf() - limits.min.valueOf()));

      this.scale = d3.scaleUtc()
        .domain(domain)
        .range(range)
        .interpolate(d3.interpolateCubehelix);

    } else if (!this.isDiscrete()) {

      let limits = this.getLimits(this.which);
      //default domain is based on limits
      limits = [limits.min, limits.max];

      const singlePoint = (limits[1] - limits[0] == 0);

      domain = domain.sort((a, b) => a - b);
      range = domain.map(m => singlePoint ? paletteObject[domain[0]] : paletteObject[m]);
      domain = domain.map(m => limits[0] + m / 100 * (limits[1] - limits[0]));

      if (d3.min(domain) <= 0 && d3.max(domain) >= 0 && scaleType === "log") scaleType = "genericLog";

      if (scaleType === "log" || scaleType === "genericLog") {
        const s = d3.scaleGenericlog()
          .domain(limits)
          .range(limits);
        domain = domain.map(d => s.invert(d));
      }
      this.scale = d3[`scale${utils.capitalize(scaleType)}`]()
        .domain(domain)
        .range(range)
        .interpolate(d3.interpolateCubehelix);

    } else {
      range = range.map(m => utils.isArray(m) ? m[0] : m);

      scaleType = "ordinal";

      if (this.discreteDefaultPalette) {
        const defaultPalette = utils.extend({}, defaultPalettes["_discrete"]);
        delete defaultPalette["_default"];
        const defaultPaletteKeys = Object.keys(defaultPalette);

        domain = [].concat(this.getUnique(this.which));
        range = domain.map((d, i) => paletteObject[d] || defaultPalette[defaultPaletteKeys[i % defaultPaletteKeys.length]]);
        domain.push("_default");
        range.push(paletteObject["_default"]);
      }

      this.scale = d3[`scale${utils.capitalize(scaleType)}`]()
        .domain(domain)
        .range(range);
    }

    this.scaleType = scaleType;
  }

});

export default ColorModel;
<|endoftext|>


<commit_before>var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var Utils = require('../../../../helpers/utils');
var template = require('./analysis-controls.tpl');
var InfoboxFactory = require('../../../../components/infobox/infobox-factory');
var InfoboxModel = require('../../../../components/infobox/infobox-model');
var InfoboxCollection = require('../../../../components/infobox/infobox-collection');
var AnalysesQuotaPresenter = require('./analyses-quota/analyses-quota-presenter');
var AnalysesQuotaOptions = require('./analyses-quota/analyses-quota-options');
var AnalysesQuotaProvider = require('./analyses-quota/analyses-quota-provider');
var AnalysesQuotaEstimation = require('./analyses-quota/analyses-quota-estimation-input');
var AnalysesQuotaEnough = require('./analyses-quota/analyses-quota-enough');
var checkAndBuildOpts = require('../../../../helpers/required-opts');
var AnalysisButtonView = require('./analysis-button-view');
var AnalysisQuotaView = require('./analysis-quota-view');

var REQUIRED_OPTS = [
  'formModel',
  'userActions',
  'userModel',
  'stackLayoutModel',
  'configModel',
  'querySchemaModel',
  'quotaInfo'
];

/**
 * View representing the apply button for a form
 */
module.exports = CoreView.extend({
  className: 'Options-bar Options-bar--right u-flex',

  events: {
    'click .js-save': '_onSaveClicked'
  },

  _buttonView: null,

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this._analysisNode = opts.analysisNode;

    AnalysesQuotaEstimation.init(opts.configModel);
    AnalysesQuotaEnough.init(opts.configModel);

    this._viewModel = new Backbone.Model({
      isNewAnalysis: !opts.analysisNode,
      hasChanges: !opts.analysisNode,
      isDone: true,
      type: this._formModel.get('type')
    });

    this._infoboxModel = new InfoboxModel({
      state: this._quotaInfo.getState(),
      visible: true
    });

    this._initBinds();

    if (!opts.analysisNode) {
      this._fetchQuotaIfNeeded();
    }

    this.render = this.render.bind(this);
    this._onFormModelChanged = this._onFormModelChanged.bind(this);
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();
    var type = this._formModel.get('type');
    var isValid = this._formModel.isValid();

    // If no dataservice, requiresQuota returns false because the model doesn't exist and it's converted to boolean
    var requiresQuota = AnalysesQuotaOptions.requiresQuota(type, this._quotaInfo);
    var view;

    if (this._isAnalysisDone() && !this._hasChanges()) {
      view = this._createButtonView();
    } else {
      // If the analysis doesn't require quota, let's not wait or check the fetching state
      if (!requiresQuota || !isValid) {
        view = this._createButtonView();
      } else {
        view = this._createQuotaView();
      }
    }

    this.addView(view);
    this.$el.append(view.render().el);
    this._setTrackingClass();

    return this;
  },

  _setTrackingClass: function () {
    this.$('.js-save').addClass('track-' + this._formModel.get('type') + '-analysis track-apply');
  },

  _initBinds: function () {
    this.listenTo(this._formModel, 'change', this._onFormModelChanged);

    if (this._analysisNode) {
      this._bindAnalysisNodeStatusChanges();
    }
  },

  _bindAnalysisNodeStatusChanges: function () {
    this.listenTo(this._analysisNode, 'change:status', function () {
      this._viewModel.set('isDone', this._isAnalysisDone());
      var isAnalysisDone = this._isAnalysisDone();
      if (isAnalysisDone) {
        this.render();
      }
    });
  },

  _onFormModelChanged: function () {
    var isAnalysisDone = this._isAnalysisDone();
    var isDone = this._viewModel.get('isNewAnalysis') || isAnalysisDone;
    var canSave = this._formModel.isValid() && isDone;

    this._viewModel.set({
      hasChanges: true,
      type: this._formModel.get('type'),
      isDisabled: !canSave
    });

    this.render();
    this._fetchQuotaIfNeeded();
  },

  _fetchQuotaIfNeeded: function () {
    var type = this._formModel.get('type');
    var isValid = this._formModel.isValid();
    var quota = this._quotaInfo;
    if ((quota.needsCheck() || AnalysesQuotaOptions.requiresQuota(type, quota)) && isValid) {
      this._fetchQuota();
    }
  },

  _fetchQuota: function () {
    this._infoboxModel.set('state', 'fetching');

    this._quotaInfo.fetch({
      success: function () {
        this._buildQuotaViewInfo();
      }.bind(this),
      error: function (error) {
        this._viewModel.set({error: error});
        this._infoboxModel.set('state', 'error');
      }.bind(this)
    });
  },

  _createButtonView: function () {
    var label = this._formModel.get('persisted')
        ? _t('editor.layers.analysis-form.apply-btn')
        : _t('editor.layers.analysis-form.create-btn');

    this._viewModel.set({
      label: label,
      isDisabled: !this._canSave()
    });

    if (this._buttonView === null) {
      this._buttonView = new AnalysisButtonView({
        template: template,
        model: this._viewModel
      });
    }

    return this._buttonView;
  },

  _buildQuotaViewInfo: function () {
    var formModel = this._formModel;
    var userModel = this._userModel;
    var type = formModel.get('type');
    var service = AnalysesQuotaOptions.getServiceName(type);
    var query = this._querySchemaModel.get('query');
    var quotaData = this._quotaInfo;

    this.payload = {};
    this.payload.type = type;

    var errorCallback = function (error) {
      this._viewModel.set('error', error);
      this._infoboxModel.set('state', 'error');
    }.bind(this);

    var checkEnoughQuota = function (data) {
      // Apply transformation to the input
      // for example isolines takes tracts in in order to calculate estimation
      var rows = AnalysesQuotaOptions.transformInput(type, data, formModel);
      this.payload.estimation = rows;
      // with the input, we calculate wether the quota is enough
      return AnalysesQuotaEnough.fetch(service, rows);
    }.bind(this);

    var buildPayload = function (enoughQuota) {
      var payload = this.payload;
      // then we can get the full set of data
      payload.quotaInfo = AnalysesQuotaProvider(type, userModel, quotaData, enoughQuota);
      payload.creditsLeft = payload.quotaInfo.totalQuota - payload.quotaInfo.usedQuota;
      payload.canRunAnalysis = ((enoughQuota && payload.quotaInfo.totalQuota > 0) ||
                               (payload.quotaInfo.hardLimit === false && payload.quotaInfo.totalQuota > 0));

      // finally we are ready to show the information
      this._infoboxModel.set('state', 'ready');
    }.bind(this);

    var dsReady = quotaData.isReady();

    // Only checks api if analysis belongs to a service with quota
    if (service !== null && dsReady) {
      if (formModel.isValid()) {
        // get the estimation for this analysis
        AnalysesQuotaEstimation.fetch(query)
          .then(checkEnoughQuota, errorCallback)
          .then(buildPayload, errorCallback);
      } else {
        // to render the simple button view.
        this.render();
      }
    } else if (!dsReady) {
      errorCallback(_t('editor.layers.analysis-form.quota.quota-dataservice-down'));
    }
  },

  _createQuotaView: function () {
    var states = [
      {
        state: 'fetching',
        createContentView: function () {
          return InfoboxFactory.createLoading({
            body: _t('editor.layers.analysis-form.quota.loading')
          });
        }
      },
      {
        state: 'ready',
        createContentView: function () {
          var payload = this.payload;
          var confirmLabel = _t('editor.layers.analysis-form.confirm-analysis');
          var quotaMessage = '';

          if (payload.creditsLeft > 0) {
            quotaMessage = _t('editor.layers.analysis-form.quota.credits-left-message', {
              smart_count: Utils.formatNumber(payload.creditsLeft)
            });
          } else {
            quotaMessage = _t('editor.layers.analysis-form.quota.no-credits-message');
          }
          payload.quotaInfo.quotaMessage = quotaMessage;

          var dataInfobox = _.extend(AnalysesQuotaPresenter.make(payload, this._configModel, this._userModel), {
            title: _t('editor.layers.analysis-form.quota.title'),
            mainAction: {
              label: confirmLabel,
              type: 'primary',
              disabled: !this._canSave() || !payload.canRunAnalysis
            },
            quota: payload.quotaInfo
          });

          return InfoboxFactory.createQuota(dataInfobox);
        }.bind(this),
        mainAction: function () {
          var payload = this.payload;
          if (this._canSave() && payload.canRunAnalysis) {
            this._saveAnalysis();
          }
        }.bind(this),
        closeAction: function () {
          this._stackLayoutModel.prevStep('layers');
        }.bind(this)
      },
      {
        state: 'error',
        createContentView: function () {
          return InfoboxFactory.createWithAction({
            title: _t('editor.layers.analysis-form.quota.title'),
            type: 'error',
            body: _t('editor.layers.analysis-form.quota.quota-fetch-error', {
              error: this._viewModel.get('error')
            }),
            mainAction: {
              label: _t('editor.layers.analysis-form.quota.cancel')
            }
          });
        }.bind(this),
        mainAction: function () {
          this._stackLayoutModel.prevStep('layers');
        }.bind(this)
      }
    ];

    return new AnalysisQuotaView({
      className: 'Infobox-wrapper',
      infoboxModel: this._infoboxModel,
      infoboxCollection: new InfoboxCollection(states)
    });
  },

  _canSave: function () {
    var isAnalysisDone = this._isAnalysisDone();
    var isDone = this._viewModel.get('isNewAnalysis') || isAnalysisDone;
    var hasChanges = this._viewModel.get('hasChanges');
    return this._formModel.isValid() && isDone && hasChanges;
  },

  _onSaveClicked: function () {
    if (this._canSave()) {
      this._saveAnalysis();
    }
  },

  _hasChanges: function () {
    return this._viewModel.get('hasChanges') === true;
  },

  _isAnalysisDone: function () {
    return this._analysisNode && (this._analysisNode.isDone() || this._analysisNode.hasFailed());
  },

  _saveAnalysis: function () {
    this._infoboxModel.set('state', 'idle');
    var newNode = this._userActions.saveAnalysis(this._formModel);

    if (!this._analysisNode) {
      this._analysisNode = newNode;
      this._bindAnalysisNodeStatusChanges();
    }

    this._viewModel.set({ hasChanges: false, isDone: false });
    this.render();
  }
});
<commit_msg>Make sure all promises are resolved before displaying the quota info.<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var Utils = require('../../../../helpers/utils');
var template = require('./analysis-controls.tpl');
var InfoboxFactory = require('../../../../components/infobox/infobox-factory');
var InfoboxModel = require('../../../../components/infobox/infobox-model');
var InfoboxCollection = require('../../../../components/infobox/infobox-collection');
var AnalysesQuotaPresenter = require('./analyses-quota/analyses-quota-presenter');
var AnalysesQuotaOptions = require('./analyses-quota/analyses-quota-options');
var AnalysesQuotaProvider = require('./analyses-quota/analyses-quota-provider');
var AnalysesQuotaEstimation = require('./analyses-quota/analyses-quota-estimation-input');
var AnalysesQuotaEnough = require('./analyses-quota/analyses-quota-enough');
var checkAndBuildOpts = require('../../../../helpers/required-opts');
var AnalysisButtonView = require('./analysis-button-view');
var AnalysisQuotaView = require('./analysis-quota-view');

var REQUIRED_OPTS = [
  'formModel',
  'userActions',
  'userModel',
  'stackLayoutModel',
  'configModel',
  'querySchemaModel',
  'quotaInfo'
];

/**
 * View representing the apply button for a form
 */
module.exports = CoreView.extend({
  className: 'Options-bar Options-bar--right u-flex',

  events: {
    'click .js-save': '_onSaveClicked'
  },

  _buttonView: null,

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this._analysisNode = opts.analysisNode;

    AnalysesQuotaEstimation.init(opts.configModel);
    AnalysesQuotaEnough.init(opts.configModel);

    this._viewModel = new Backbone.Model({
      isNewAnalysis: !opts.analysisNode,
      hasChanges: !opts.analysisNode,
      isDone: true,
      type: this._formModel.get('type')
    });

    this._infoboxModel = new InfoboxModel({
      state: this._quotaInfo.getState(),
      visible: true
    });

    this._initBinds();

    if (!opts.analysisNode) {
      this._fetchQuotaIfNeeded();
    }

    this.render = this.render.bind(this);
    this._onFormModelChanged = this._onFormModelChanged.bind(this);
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();
    var type = this._formModel.get('type');
    var isValid = this._formModel.isValid();

    // If no dataservice, requiresQuota returns false because the model doesn't exist and it's converted to boolean
    var requiresQuota = AnalysesQuotaOptions.requiresQuota(type, this._quotaInfo);
    var view;

    if (this._isAnalysisDone() && !this._hasChanges()) {
      view = this._createButtonView();
    } else {
      // If the analysis doesn't require quota, let's not wait or check the fetching state
      if (!requiresQuota || !isValid) {
        view = this._createButtonView();
      } else {
        view = this._createQuotaView();
      }
    }

    this.addView(view);
    this.$el.append(view.render().el);
    this._setTrackingClass();

    return this;
  },

  _setTrackingClass: function () {
    this.$('.js-save').addClass('track-' + this._formModel.get('type') + '-analysis track-apply');
  },

  _initBinds: function () {
    this.listenTo(this._formModel, 'change', this._onFormModelChanged);

    if (this._analysisNode) {
      this._bindAnalysisNodeStatusChanges();
    }
  },

  _bindAnalysisNodeStatusChanges: function () {
    this.listenTo(this._analysisNode, 'change:status', function () {
      this._viewModel.set('isDone', this._isAnalysisDone());
      var isAnalysisDone = this._isAnalysisDone();
      if (isAnalysisDone) {
        this.render();
      }
    });
  },

  _onFormModelChanged: function () {
    var isAnalysisDone = this._isAnalysisDone();
    var isDone = this._viewModel.get('isNewAnalysis') || isAnalysisDone;
    var canSave = this._formModel.isValid() && isDone;

    this._viewModel.set({
      hasChanges: true,
      type: this._formModel.get('type'),
      isDisabled: !canSave
    });

    this.render();
    this._fetchQuotaIfNeeded();
  },

  _fetchQuotaIfNeeded: function () {
    var type = this._formModel.get('type');
    var isValid = this._formModel.isValid();
    var quota = this._quotaInfo;
    if ((quota.needsCheck() || AnalysesQuotaOptions.requiresQuota(type, quota)) && isValid) {
      this._fetchQuota();
    }
  },

  _fetchQuota: function () {
    this._infoboxModel.set('state', 'fetching');

    this._quotaInfo.fetch({
      success: function () {
        this._buildQuotaViewInfo();
      }.bind(this),
      error: function (error) {
        this._viewModel.set({error: error});
        this._infoboxModel.set('state', 'error');
      }.bind(this)
    });
  },

  _createButtonView: function () {
    var label = this._formModel.get('persisted')
        ? _t('editor.layers.analysis-form.apply-btn')
        : _t('editor.layers.analysis-form.create-btn');

    this._viewModel.set({
      label: label,
      isDisabled: !this._canSave()
    });

    if (this._buttonView === null) {
      this._buttonView = new AnalysisButtonView({
        template: template,
        model: this._viewModel
      });
    }

    return this._buttonView;
  },

  _buildQuotaViewInfo: function () {
    var formModel = this._formModel;
    var userModel = this._userModel;
    var type = formModel.get('type');
    var service = AnalysesQuotaOptions.getServiceName(type);
    var query = this._querySchemaModel.get('query');
    var quotaData = this._quotaInfo;

    this.payload = {};
    this.payload.type = type;

    var errorCallback = function (error) {
      this._viewModel.set('error', error);
      this._infoboxModel.set('state', 'error');
    }.bind(this);

    var checkEnoughQuota = function (data) {
      // Apply transformation to the input
      // for example isolines takes tracts in in order to calculate estimation
      var rows = AnalysesQuotaOptions.transformInput(type, data, formModel);
      this.payload.estimation = rows;
      // with the input, we calculate wether the quota is enough
      this.enoughQuotaPromise = AnalysesQuotaEnough.fetch(service, rows);
      return this.enoughQuotaPromise;
    }.bind(this);

    var buildPayload = function (enoughQuota) {
      var payload = this.payload;
      // then we can get the full set of data
      payload.quotaInfo = AnalysesQuotaProvider(type, userModel, quotaData, enoughQuota);
      payload.creditsLeft = payload.quotaInfo.totalQuota - payload.quotaInfo.usedQuota;
      payload.canRunAnalysis = ((enoughQuota && payload.quotaInfo.totalQuota > 0) ||
                               (payload.quotaInfo.hardLimit === false && payload.quotaInfo.totalQuota > 0));

      // we need to check the request's state because it could be another render while permforming the current one
      // so, to make sure we have all data returned by the apis, we don't raise the green flag until both stats are resolved.
      var estimationState = this.estimationPromise.state();
      var enoughQuotaState = this.enoughQuotaPromise.state();

      if (estimationState === 'resolved' && enoughQuotaState === 'resolved') {
        // finally we are ready to show the information
        this._infoboxModel.set('state', 'ready');
      }
    }.bind(this);

    var dsReady = quotaData.isReady();

    // Only checks api if analysis belongs to a service with quota
    if (service !== null && dsReady) {
      if (formModel.isValid()) {
        // get the estimation for this analysis
        this.estimationPromise = AnalysesQuotaEstimation.fetch(query);

        this.estimationPromise
          .then(checkEnoughQuota, errorCallback)
          .then(buildPayload, errorCallback);
      } else {
        // to render the simple button view.
        this.render();
      }
    } else if (!dsReady) {
      errorCallback(_t('editor.layers.analysis-form.quota.quota-dataservice-down'));
    }
  },

  _createQuotaView: function () {
    var states = [
      {
        state: 'fetching',
        createContentView: function () {
          return InfoboxFactory.createLoading({
            body: _t('editor.layers.analysis-form.quota.loading')
          });
        }
      },
      {
        state: 'ready',
        createContentView: function () {
          var payload = this.payload;
          var confirmLabel = _t('editor.layers.analysis-form.confirm-analysis');
          var quotaMessage = '';

          if (payload.creditsLeft > 0) {
            quotaMessage = _t('editor.layers.analysis-form.quota.credits-left-message', {
              smart_count: Utils.formatNumber(payload.creditsLeft)
            });
          } else {
            quotaMessage = _t('editor.layers.analysis-form.quota.no-credits-message');
          }
          payload.quotaInfo.quotaMessage = quotaMessage;

          var dataInfobox = _.extend(AnalysesQuotaPresenter.make(payload, this._configModel, this._userModel), {
            title: _t('editor.layers.analysis-form.quota.title'),
            mainAction: {
              label: confirmLabel,
              type: 'primary',
              disabled: !this._canSave() || !payload.canRunAnalysis
            },
            quota: payload.quotaInfo
          });

          return InfoboxFactory.createQuota(dataInfobox);
        }.bind(this),
        mainAction: function () {
          var payload = this.payload;
          if (this._canSave() && payload.canRunAnalysis) {
            this._saveAnalysis();
          }
        }.bind(this),
        closeAction: function () {
          this._stackLayoutModel.prevStep('layers');
        }.bind(this)
      },
      {
        state: 'error',
        createContentView: function () {
          return InfoboxFactory.createWithAction({
            title: _t('editor.layers.analysis-form.quota.title'),
            type: 'error',
            body: _t('editor.layers.analysis-form.quota.quota-fetch-error', {
              error: this._viewModel.get('error')
            }),
            mainAction: {
              label: _t('editor.layers.analysis-form.quota.cancel')
            }
          });
        }.bind(this),
        mainAction: function () {
          this._stackLayoutModel.prevStep('layers');
        }.bind(this)
      }
    ];

    return new AnalysisQuotaView({
      className: 'Infobox-wrapper',
      infoboxModel: this._infoboxModel,
      infoboxCollection: new InfoboxCollection(states)
    });
  },

  _canSave: function () {
    var isAnalysisDone = this._isAnalysisDone();
    var isDone = this._viewModel.get('isNewAnalysis') || isAnalysisDone;
    var hasChanges = this._viewModel.get('hasChanges');
    return this._formModel.isValid() && isDone && hasChanges;
  },

  _onSaveClicked: function () {
    if (this._canSave()) {
      this._saveAnalysis();
    }
  },

  _hasChanges: function () {
    return this._viewModel.get('hasChanges') === true;
  },

  _isAnalysisDone: function () {
    return this._analysisNode && (this._analysisNode.isDone() || this._analysisNode.hasFailed());
  },

  _saveAnalysis: function () {
    this._infoboxModel.set('state', 'idle');
    var newNode = this._userActions.saveAnalysis(this._formModel);

    if (!this._analysisNode) {
      this._analysisNode = newNode;
      this._bindAnalysisNodeStatusChanges();
    }

    this._viewModel.set({ hasChanges: false, isDone: false });
    this.render();
  }
});
<|endoftext|>


<commit_before>var h = require('virtual-dom/h')
  , inherits = require('util').inherits
  , BaseElement = require('./base-element')

module.exports = Wallet

function Wallet(target) {
  BaseElement.call(this, target)
}

inherits(Wallet, BaseElement)

Wallet.prototype.render = function(info, accounts) {
  var self = this
  accounts = accounts.sort(function(a, b) {
    return a.balance < b.balance
      ? 1
      : a.balance > b.balance
      ? -1
      : 0
  }).map(address)
  return [
    h('#receive.js-tab-address-book-content.tabby', [
      h('.wrapper', [
        h('.tab-text', [
          h('h3.text-primary', 'Receive Paycoins')
        ])
      , h('.js-my-addresses', [
          h('table.table.addresses.table-striped', [accounts])
        ])
      ])
    ])
  ]
}

function address(item) {
  var name = item.label || '[no label]'
  var balance = (+item.balance || 0).toFixed(6)
  return h('tr', [
    h('td', name)
  , h('td', item.address)
  , h('td', balance)
  ])
}
<commit_msg>Add active class to wallet tr on click<commit_after>var h = require('virtual-dom/h')
  , inherits = require('util').inherits
  , BaseElement = require('./base-element')
  , dom = require('../dom')

module.exports = Wallet

function Wallet(target) {
  BaseElement.call(this, target)
}

inherits(Wallet, BaseElement)

Wallet.prototype.render = function(info, accounts) {
  var self = this
  accounts = accounts.sort(function(a, b) {
    return a.balance < b.balance
      ? 1
      : a.balance > b.balance
      ? -1
      : 0
  }).map(address)
  return [
    h('#receive.js-tab-address-book-content.tabby', [
      h('.wrapper', [
        h('.tab-text', [
          h('h3.text-primary', 'Receive Paycoins')
        ])
      , h('.js-my-addresses', [
          h('table.table.addresses.table-striped', [accounts])
        ])
      ])
    ])
  ]
}

function address(item) {
  var name = item.label || '[no label]'
  var balance = (+item.balance || 0).toFixed(6)
  return h('tr', {
    onclick: function() {
      var sibs = dom.siblings(this)
      for (var i=0, len=sibs.length; i<len; i++) {
        sibs[i].classList.remove('active')
      }
      this.classList.add('active')
    }
  }, [
    h('td', name)
  , h('td', item.address)
  , h('td', balance)
  ])
}
<|endoftext|>