<commit_before>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/renderer/MeshData',
	'goo/animation/SkeletonPose',
	'goo/loaders/JsonUtils',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil',
	'goo/util/ArrayUtil'
], function(
	ConfigHandler,
	MeshData,
	SkeletonPose,
	JsonUtils,
	PromiseUtil,
	_,
	ArrayUtil
) {
	function MeshDataHandler() {
		ConfigHandler.apply(this, arguments);
		this._objects = {};
	}

	MeshDataHandler.prototype = Object.create(ConfigHandler.prototype);
	ConfigHandler._registerClass('mesh', MeshDataHandler);

	MeshDataHandler.prototype.update = function(ref, meshConfig) {
		var that = this;
		if (!this._objects[ref]) {
			if (meshConfig.binaryData) {
				return this.getConfig(meshConfig.binaryData).then(function(bindata) {
					if (!bindata) {
						throw new Error("Binary mesh data was empty");
					}
					return that._createMeshData(meshConfig, bindata).then(function(meshData) {
						return that._objects[ref] = meshData;
					});
				});
			} else {
				return this._createMeshData(meshConfig, null).then(function(meshData) {
					return that._objects[ref] = meshData;
				});
			}
		} else {
			return PromiseUtil.createDummyPromise(this._objects[ref]);
		}
	};

	MeshDataHandler.prototype.remove = function(ref) {};

	MeshDataHandler.prototype._createMeshData = function(meshConfig, bindata) {
		var compression;
		if (meshConfig.compression && meshConfig.compression.compressed) {
			compression = {
				compressedVertsRange: meshConfig.compression.compressedVertsRange || (1 << 14) - 1,
				compressedColorsRange: meshConfig.compression.compressedColorsRange || (1 << 8) - 1,
				compressedUnitVectorRange: meshConfig.compression.compressedUnitVectorRange || (1 << 10) - 1
			};
		}

		var meshData = this._createMeshDataObject(meshConfig);
		this._fillMeshData(meshData, meshConfig, bindata, compression);
		if (meshConfig.pose) {
			meshConfig.poseRef = meshConfig.pose;
		}
		/*
		 if meshConfig.poseRef
		 skelRef = meshConfig.poseRef
		 @getConfig(skelRef).then (skelConfig)=>
		 @updateObject(skelRef, skelConfig).then (skeleton)=>
		 meshData.currentPose = skeleton
		 return meshData
		 else
		 */

		return PromiseUtil.createDummyPromise(meshData);
	};

	MeshDataHandler.prototype._createMeshDataObject = function(config) {
		var attributeMap, indexCount, meshData, type, vertexCount, weightsPerVert;//, _i, _len, _ref, _ref1, _ref2;
		var data = config.data || config;
		if (config.type === 'SkinnedMesh') {
			weightsPerVert = 4;
			type = MeshData.SKINMESH;
		} else {
			weightsPerVert = 0;
			type = MeshData.MESH;
		}
		vertexCount = data.vertexCount;
		if (vertexCount === 0) {
			return null;
		}
		if(data.indexLengths != null) {
			indexCount = data.indexLengths[0];
		} else if (data.indices != null) {
			indexCount = data.indices.length;

		} else {
			indexCount = 0;
		}

		attributeMap = {};
		if (data.vertices && data.vertices.length > 0) {
			attributeMap.POSITION = MeshData.createAttribute(3, 'Float');
		}
		if (data.normals && data.normals.length > 0) {
			attributeMap.NORMAL = MeshData.createAttribute(3, 'Float');
		}
		if (data.tangents && data.tangents.length > 0) {
			attributeMap.TANGENT = MeshData.createAttribute(4, 'Float');
		}
		if (data.colors && data.colors.length > 0) {
			attributeMap.COLOR = MeshData.createAttribute(4, 'Float');
		}
		if (weightsPerVert > 0 && data.weights) {
			attributeMap.WEIGHTS = MeshData.createAttribute(4, 'Float');
		}
		if (weightsPerVert > 0 && data.joints) {
			attributeMap.JOINTIDS = MeshData.createAttribute(4, 'Short');
		}
		if (data.textureCoords && data.textureCoords.length > 0) {
			for (var texIdx = 0; texIdx < data.textureCoords.length; texIdx++) {
				attributeMap['TEXCOORD' + texIdx] = MeshData.createAttribute(2, 'Float');
			}
		}
		meshData = new MeshData(attributeMap, vertexCount, indexCount);
		meshData.type = type;

		return meshData;
	};

	MeshDataHandler.prototype._fillMeshData = function(meshData, config, bindata, compression) {
		var that = this;

		var data = config.data || config;

		var weightsPerVert;
		if (meshData.type === MeshData.SKINMESH) {
			weightsPerVert = 4;
		} else {
			weightsPerVert = 0;
		}

		var _fillAttributeBuffer = function(attr, data) {
			var opts;
			if (data != null ? data.length : void 0) {
				if (compression && typeof data === 'string') {
					opts = that._getCompressionOptions(attr, config, compression);
					JsonUtils.fillAttributeBufferFromCompressedString(data, meshData, attr, opts.scale, opts.offset);
				} else if (bindata) {
					meshData.getAttributeBuffer(attr).set(ArrayUtil.getTypedArray(bindata, data));
				} else {
					JsonUtils.fillAttributeBuffer(data, meshData, attr);
				}
			}
		};

		_fillAttributeBuffer(MeshData.POSITION, data.vertices);
		_fillAttributeBuffer(MeshData.NORMAL, data.normals);
		_fillAttributeBuffer(MeshData.TANGENT, data.tangents);
		_fillAttributeBuffer(MeshData.COLOR, data.colors);

		if (meshData.type === MeshData.SKINMESH) {
			_fillAttributeBuffer(MeshData.WEIGHTS, data.weights);
		}
		if (data.textureCoords != null && data.textureCoords.length > 0) {
			var textureUnits = data.textureCoords;
			for (var texIdx = 0; texIdx < textureUnits.length; texIdx++) {
				var texObj = textureUnits[texIdx];
				var attr = 'TEXCOORD' + texIdx;
				_fillAttributeBuffer(attr, texObj.UVs || texObj);
			}
		}
		if (weightsPerVert > 0 && data.joints != null && data.joints.length > 0) {
			var buffer = meshData.getAttributeBuffer(MeshData.JOINTIDS);
			var jointData = this._getIntBuffer(data.joints, 32767, bindata, compression);
			if (meshData.type === MeshData.SKINMESH) {
				var localJointMap = [];
				var localIndex = 0;
				for (var idx = 0; idx < jointData.length; idx++) {
					var jointIndex = jointData[idx];
					if (localJointMap[jointIndex] === void 0) {
						localJointMap[jointIndex] = localIndex++;
					}
					buffer.set([localJointMap[jointIndex]], idx);
				}
				var localMap = [];
				for (var jointIndex = 0; jointIndex < localJointMap.length; jointIndex++) {
					var localIndex = localJointMap[jointIndex];
					if (localIndex !== null) {
						localMap[localIndex] = jointIndex;
					}
				}
				meshData.paletteMap = localMap;
				meshData.weightsPerVertex = weightsPerVert;
			} else {
				for (var jointIdx = 0; 0 < jointData.capacity(); jointIdx++) {
					buffer.putCast(jointIdx, jointData.get(jointIdx));
				}
			}
		}

		if (data.indices) {
			meshData.getIndexBuffer().set(this._getIntBuffer(data.indices, data.vertexCount, bindata, compression));
		}
		if (data.indexModes) {
			meshData.indexModes = data.indexModes.slice(0);
		}
		if (data.indexLengths) {
			meshData.indexLengths = data.indexLengths.slice(0);
		}
		if (data.boundingBox) {
			meshData.boundingBox = data.boundingBox;
		}
		return meshData;
	};

	MeshDataHandler.prototype._getIntBuffer = function(data, len, bindata, compression) {
		if (!data) {
			return null;
		} else if (compression) {
			return JsonUtils.getIntBufferFromCompressedString(data, len);
		} else if (bindata) {
			return ArrayUtil.getTypedArray(bindata, data);
		} else {
			return JsonUtils.getIntBuffer(data, len);
		}
	};

	MeshDataHandler.prototype._getCompressionOptions = function(attr, config, compression) {
		var offset, offsetObj, options, scale;
		var data = config.data || config;
		if (attr === MeshData.POSITION) {
			offsetObj = data.vertexOffsets;
			options = {
				offset: [offsetObj.xOffset, offsetObj.yOffset, offsetObj.zOffset],
				scale: [data.vertexScale, data.vertexScale, data.vertexScale]
			};
		} else if (attr === MeshData.WEIGHTS) {
			offset = 0;
			scale = 1 / compression.compressedVertsRange;
			options = {
				offset: [offset],
				scale: [scale]
			};
		} else if (attr === MeshData.NORMAL) {
			offset = 1 - (compression.compressedUnitVectorRange + 1 >> 1);
			scale = 1 / -offset;
			options = {
				offset: [offset, offset, offset],
				scale: [scale, scale, scale]
			};
		} else if (attr === MeshData.TANGENT) {
			offset = 1 - (compression.compressedUnitVectorRange + 1 >> 1);
			scale = 1 / -offset;
			options = {
				offset: [offset, offset, offset, offset],
				scale: [scale, scale, scale, scale]
			};
		} else if (attr === MeshData.COLOR) {
			offset = 0;
			scale = 1 / (compression.compressedColorsRange + 1);
			options = {
				offset: [offset, offset, offset, offset],
				scale: [scale, scale, scale, scale]
			};
		} else if (attr.substr(0, 8) === 'TEXCOORD') {
			var texIdx = parseInt(attr.substr(8));
			var texObj = data.textureCoords[texIdx];
			options = {
				offset: texObj.UVOffsets,
				scale: texObj.UVScales
			};
		}

		return options;
	};

	return MeshDataHandler;

});
<commit_msg>Removed legacy stuff<commit_after>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/renderer/MeshData',
	'goo/animation/SkeletonPose',
	'goo/loaders/JsonUtils',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil',
	'goo/util/ArrayUtil'
], function(
	ConfigHandler,
	MeshData,
	SkeletonPose,
	JsonUtils,
	PromiseUtil,
	_,
	ArrayUtil
) {
	function MeshDataHandler() {
		ConfigHandler.apply(this, arguments);
		this._objects = {};
	}

	MeshDataHandler.prototype = Object.create(ConfigHandler.prototype);
	ConfigHandler._registerClass('mesh', MeshDataHandler);

	MeshDataHandler.prototype.update = function(ref, meshConfig) {
		var that = this;
		if (!this._objects[ref]) {
			if (meshConfig.binaryData) {
				return this.getConfig(meshConfig.binaryData).then(function(bindata) {
					if (!bindata) {
						throw new Error("Binary mesh data was empty");
					}
					return that._createMeshData(meshConfig, bindata).then(function(meshData) {
						return that._objects[ref] = meshData;
					});
				});
			} else {
				return this._createMeshData(meshConfig, null).then(function(meshData) {
					return that._objects[ref] = meshData;
				});
			}
		} else {
			return PromiseUtil.createDummyPromise(this._objects[ref]);
		}
	};

	MeshDataHandler.prototype.remove = function(ref) {};

	MeshDataHandler.prototype._createMeshData = function(meshConfig, bindata) {
		var compression;
		if (meshConfig.compression && meshConfig.compression.compressed) {
			compression = {
				compressedVertsRange: meshConfig.compression.compressedVertsRange || (1 << 14) - 1,
				compressedColorsRange: meshConfig.compression.compressedColorsRange || (1 << 8) - 1,
				compressedUnitVectorRange: meshConfig.compression.compressedUnitVectorRange || (1 << 10) - 1
			};
		}

		var meshData = this._createMeshDataObject(meshConfig);
		this._fillMeshData(meshData, meshConfig, bindata, compression);

		return PromiseUtil.createDummyPromise(meshData);
	};

	MeshDataHandler.prototype._createMeshDataObject = function(config) {
		var attributeMap, indexCount, meshData, type, vertexCount, weightsPerVert;//, _i, _len, _ref, _ref1, _ref2;
		var data = config.data || config;
		if (config.type === 'SkinnedMesh') {
			weightsPerVert = 4;
			type = MeshData.SKINMESH;
		} else {
			weightsPerVert = 0;
			type = MeshData.MESH;
		}
		vertexCount = data.vertexCount;
		if (vertexCount === 0) {
			return null;
		}
		if(data.indexLengths != null) {
			indexCount = data.indexLengths[0];
		} else if (data.indices != null) {
			indexCount = data.indices.length;

		} else {
			indexCount = 0;
		}

		attributeMap = {};
		if (data.vertices && data.vertices.length > 0) {
			attributeMap.POSITION = MeshData.createAttribute(3, 'Float');
		}
		if (data.normals && data.normals.length > 0) {
			attributeMap.NORMAL = MeshData.createAttribute(3, 'Float');
		}
		if (data.tangents && data.tangents.length > 0) {
			attributeMap.TANGENT = MeshData.createAttribute(4, 'Float');
		}
		if (data.colors && data.colors.length > 0) {
			attributeMap.COLOR = MeshData.createAttribute(4, 'Float');
		}
		if (weightsPerVert > 0 && data.weights) {
			attributeMap.WEIGHTS = MeshData.createAttribute(4, 'Float');
		}
		if (weightsPerVert > 0 && data.joints) {
			attributeMap.JOINTIDS = MeshData.createAttribute(4, 'Short');
		}
		if (data.textureCoords && data.textureCoords.length > 0) {
			for (var texIdx = 0; texIdx < data.textureCoords.length; texIdx++) {
				attributeMap['TEXCOORD' + texIdx] = MeshData.createAttribute(2, 'Float');
			}
		}
		meshData = new MeshData(attributeMap, vertexCount, indexCount);
		meshData.type = type;

		return meshData;
	};

	MeshDataHandler.prototype._fillMeshData = function(meshData, config, bindata, compression) {
		var that = this;

		var data = config.data || config;

		var weightsPerVert;
		if (meshData.type === MeshData.SKINMESH) {
			weightsPerVert = 4;
		} else {
			weightsPerVert = 0;
		}

		var _fillAttributeBuffer = function(attr, data) {
			var opts;
			if (data != null ? data.length : void 0) {
				if (compression && typeof data === 'string') {
					opts = that._getCompressionOptions(attr, config, compression);
					JsonUtils.fillAttributeBufferFromCompressedString(data, meshData, attr, opts.scale, opts.offset);
				} else if (bindata) {
					meshData.getAttributeBuffer(attr).set(ArrayUtil.getTypedArray(bindata, data));
				} else {
					JsonUtils.fillAttributeBuffer(data, meshData, attr);
				}
			}
		};

		_fillAttributeBuffer(MeshData.POSITION, data.vertices);
		_fillAttributeBuffer(MeshData.NORMAL, data.normals);
		_fillAttributeBuffer(MeshData.TANGENT, data.tangents);
		_fillAttributeBuffer(MeshData.COLOR, data.colors);

		if (meshData.type === MeshData.SKINMESH) {
			_fillAttributeBuffer(MeshData.WEIGHTS, data.weights);
		}
		if (data.textureCoords != null && data.textureCoords.length > 0) {
			var textureUnits = data.textureCoords;
			for (var texIdx = 0; texIdx < textureUnits.length; texIdx++) {
				var texObj = textureUnits[texIdx];
				var attr = 'TEXCOORD' + texIdx;
				_fillAttributeBuffer(attr, texObj.UVs || texObj);
			}
		}
		if (weightsPerVert > 0 && data.joints != null && data.joints.length > 0) {
			var buffer = meshData.getAttributeBuffer(MeshData.JOINTIDS);
			var jointData = this._getIntBuffer(data.joints, 32767, bindata, compression);
			if (meshData.type === MeshData.SKINMESH) {
				var localJointMap = [];
				var localIndex = 0;
				for (var idx = 0; idx < jointData.length; idx++) {
					var jointIndex = jointData[idx];
					if (localJointMap[jointIndex] === void 0) {
						localJointMap[jointIndex] = localIndex++;
					}
					buffer.set([localJointMap[jointIndex]], idx);
				}
				var localMap = [];
				for (var jointIndex = 0; jointIndex < localJointMap.length; jointIndex++) {
					var localIndex = localJointMap[jointIndex];
					if (localIndex !== null) {
						localMap[localIndex] = jointIndex;
					}
				}
				meshData.paletteMap = localMap;
				meshData.weightsPerVertex = weightsPerVert;
			} else {
				for (var jointIdx = 0; 0 < jointData.capacity(); jointIdx++) {
					buffer.putCast(jointIdx, jointData.get(jointIdx));
				}
			}
		}

		if (data.indices) {
			meshData.getIndexBuffer().set(this._getIntBuffer(data.indices, data.vertexCount, bindata, compression));
		}
		if (data.indexModes) {
			meshData.indexModes = data.indexModes.slice(0);
		}
		if (data.indexLengths) {
			meshData.indexLengths = data.indexLengths.slice(0);
		}
		if (data.boundingBox) {
			meshData.boundingBox = data.boundingBox;
		}
		return meshData;
	};

	MeshDataHandler.prototype._getIntBuffer = function(data, len, bindata, compression) {
		if (!data) {
			return null;
		} else if (compression) {
			return JsonUtils.getIntBufferFromCompressedString(data, len);
		} else if (bindata) {
			return ArrayUtil.getTypedArray(bindata, data);
		} else {
			return JsonUtils.getIntBuffer(data, len);
		}
	};

	MeshDataHandler.prototype._getCompressionOptions = function(attr, config, compression) {
		var offset, offsetObj, options, scale;
		var data = config.data || config;
		if (attr === MeshData.POSITION) {
			offsetObj = data.vertexOffsets;
			options = {
				offset: [offsetObj.xOffset, offsetObj.yOffset, offsetObj.zOffset],
				scale: [data.vertexScale, data.vertexScale, data.vertexScale]
			};
		} else if (attr === MeshData.WEIGHTS) {
			offset = 0;
			scale = 1 / compression.compressedVertsRange;
			options = {
				offset: [offset],
				scale: [scale]
			};
		} else if (attr === MeshData.NORMAL) {
			offset = 1 - (compression.compressedUnitVectorRange + 1 >> 1);
			scale = 1 / -offset;
			options = {
				offset: [offset, offset, offset],
				scale: [scale, scale, scale]
			};
		} else if (attr === MeshData.TANGENT) {
			offset = 1 - (compression.compressedUnitVectorRange + 1 >> 1);
			scale = 1 / -offset;
			options = {
				offset: [offset, offset, offset, offset],
				scale: [scale, scale, scale, scale]
			};
		} else if (attr === MeshData.COLOR) {
			offset = 0;
			scale = 1 / (compression.compressedColorsRange + 1);
			options = {
				offset: [offset, offset, offset, offset],
				scale: [scale, scale, scale, scale]
			};
		} else if (attr.substr(0, 8) === 'TEXCOORD') {
			var texIdx = parseInt(attr.substr(8));
			var texObj = data.textureCoords[texIdx];
			options = {
				offset: texObj.UVOffsets,
				scale: texObj.UVScales
			};
		}

		return options;
	};

	return MeshDataHandler;

});
<|endoftext|>


<commit_before>import AdvanceKeyCombo from 'input/keyboard/combo/AdvanceKeyCombo.js';

export default function ProcessKeyCombo (event, combo) {

    let matched = false;

    if (combo.matched)
    {
        return true;
    }

    if (event.keyCode === combo.current)
    {
        //  Key was correct
        console.log('Combo Key correct', combo.current);

        if (combo.index > 0 && combo.maxKeyDelay > 0)
        {
            //  We have to check to see if the delay between
            //  the new key and the old one was too long (if enabled)

            let duration = event.timeStamp - combo.timeLastMatched;

            //  Check if they pressed it in time or not
            if (combo.index > 0 && (duration >= combo.maxKeyDelay))
            {
                matched = AdvanceKeyCombo(event, combo);
            }
        }
        else
        {
            //  We don't check the time for the first key pressed, so just advance it
            matched = AdvanceKeyCombo(event, combo);
        }
    }
    else
    {
        //  Wrong key was pressed

        console.log('Combo Key wrong, combo reset', combo.current);

        if (combo.resetOnWrongKey)
        {
            combo.index = 0;
            combo.current = combo.keyCodes[0];
        }
    }

    if (matched)
    {
        combo.timeLastMatched = event.timeStamp;
        combo.matched = true;
        combo.timeMatched = event.timeStamp;

        console.log('Combo Matched', matched);
    }

    return matched;

}
<commit_msg>Fixed one bug to do with maxKeyDelay but still not right, committing to fix later.<commit_after>import AdvanceKeyCombo from 'input/keyboard/combo/AdvanceKeyCombo.js';

export default function ProcessKeyCombo (event, combo) {

    let matched = false;

    if (combo.matched)
    {
        return true;
    }

    if (event.keyCode === combo.current)
    {
        //  Key was correct
        console.log('Combo Key correct', combo.current);

        if (combo.index > 0 && combo.maxKeyDelay > 0)
        {
            //  We have to check to see if the delay between
            //  the new key and the old one was too long (if enabled)

            let timeLimit = combo.timeLastMatched + combo.maxKeyDelay;

            //  Check if they pressed it in time or not
            if (event.timeStamp <= timeLimit)
            {
                matched = AdvanceKeyCombo(event, combo);
            }
        }
        else
        {
            //  We don't check the time for the first key pressed, so just advance it
            matched = AdvanceKeyCombo(event, combo);
        }
    }
    else
    {
        //  Wrong key was pressed

        console.log('Combo Key wrong, combo reset', combo.current);

        if (combo.resetOnWrongKey)
        {
            combo.index = 0;
            combo.current = combo.keyCodes[0];
        }
    }

    if (matched)
    {
        combo.timeLastMatched = event.timeStamp;
        combo.matched = true;
        combo.timeMatched = event.timeStamp;

        console.log('Combo Matched', matched);
    }

    return matched;

}
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import {getUrlParams} from 'utils/params';
import layerKeys from 'constants/LayerConstants';
import mapStore from 'stores/MapStore';
import mapActions from 'actions/MapActions';
import appUtils from 'utils/AppUtils';
import text from 'js/languages';
import moment from 'moment';
import {defaultColorTheme} from '../../config';
import layerActions from '../../actions/LayerActions';

export default class ReportSubscribeButtons extends Component {
  constructor(props) {
    super(props);

    this.descriptionOptions = {
      print: '',
      subscribe: 'Subscribe to receive alerts for the selected area',
    };

    this.state = {
      descriptionText: ''
    };
  }

  static contextTypes = {
    language: PropTypes.string.isRequired,
    settings: PropTypes.object.isRequired,
    map: PropTypes.object.isRequired
  };

  printReport = () => {
    const { map, settings, language } = this.context;
    const selectedFeature = map.infoWindow && map.infoWindow.getSelectedFeature();
    const {
      canopyDensity,
      activeSlopeClass,
      activeLayers,
      dynamicLayers,
      lossFromSelectIndex,
      lossToSelectIndex,
      gladStartDate,
      gladEndDate,
      terraIStartDate,
      terraIEndDate,
      viirsStartDate,
      viirsEndDate,
      modisStartDate,
      modisEndDate,
      activeFilters,
      activeVersions,
      selectedFeatureTitles
    } = mapStore.getState();

    if (selectedFeature) {

      const visibleLayers = [...activeLayers];

      if (activeLayers.indexOf(layerKeys.VIIRS_ACTIVE_FIRES) > -1) {
        const index = visibleLayers.indexOf(layerKeys.VIIRS_ACTIVE_FIRES);
        visibleLayers.splice(index, 1);
        const layer24HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES);
        const layer48HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_48HR);
        const layer72HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_72HR);
        const layer7D = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_7D);
        const layer1YR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_1YR);
        const viirsLayers = [layer24HR, layer48HR, layer72HR, layer7D, layer1YR];
        viirsLayers.forEach(layer => {
          if (layer.visible === true) {
            visibleLayers.push(layer.id);
          }
        });
      }

      if (activeLayers.indexOf(layerKeys.MODIS_ACTIVE_FIRES) > -1) {
        const index = visibleLayers.indexOf(layerKeys.MODIS_ACTIVE_FIRES);
        visibleLayers.splice(index, 1);
        const layer24HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES);
        const layer48HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_48HR);
        const layer72HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_72HR);
        const layer7D = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_7D);
        const layer1YR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_1YR);
        const modisLayers = [layer24HR, layer48HR, layer72HR, layer7D, layer1YR];
        modisLayers.forEach(layer => {
          if (layer.visible === true) {
            visibleLayers.push(layer.id);
          }
        });
      }

      const params = getUrlParams(location.href);
      const payload = {
        lang: language,
        activeLayers: visibleLayers,
        dynamicLayers,
        tcLossFrom: lossFromSelectIndex,
        tcLossTo: lossToSelectIndex,
        gladFrom: gladStartDate,
        gladTo: gladEndDate,
        terraIFrom: moment(terraIStartDate).format('YYYY-MM-DD'),
        terraITo: moment(terraIEndDate).format('YYYY-MM-DD'),
        viirsStartDate: moment(viirsStartDate).format('YYYY-MM-DD HH:mm:ss'),
        viirsEndDate: moment(viirsEndDate).format('YYYY-MM-DD HH:mm:ss'),
        modisStartDate: moment(modisStartDate).format('YYYY-MM-DD HH:mm:ss'),
        modisEndDate: moment(modisEndDate).format('YYYY-MM-DD HH:mm:ss'),
        activeSlopeClass,
        selectedFeature,
        canopyDensity,
        settings,
        activeFilters: [],
        activeVersions: [],
        selectedFeatureTitles: selectedFeatureTitles
      };

      if (params.appid) {
        payload.appid = params.appid;
      }

      if (selectedFeature._layer && selectedFeature._layer.id && selectedFeature._layer.id !== layerKeys.USER_FEATURES) {
        let layerString = '';

        payload.OBJECTID = selectedFeature && selectedFeature.attributes ? selectedFeature.attributes[selectedFeature._layer.objectIdField] : null;
        payload.OBJECTID_Field = selectedFeature._layer.objectIdField;

        layerString = selectedFeature._layer.url;
        layerString += '--' + selectedFeature._layer.id;

        payload.layerId = layerString;
      }

      if (Object.keys(activeFilters).length) {
        Object.keys(activeFilters).forEach((key) => {
          payload.activeFilters.push(`${key}|${activeFilters[key]}`);
        });
      }

      if (Object.keys(activeVersions).length) {
        Object.keys(activeVersions).forEach((key) => {
          payload.activeVersions.push(`${key}|${activeVersions[key]}`);
        });
      }

      appUtils.generateReport(payload);
    }

  };

  toggleSubscribe = () => {
    mapActions.toggleSubscribeModal({ visible: true });
  }

  updateDescriptionText = (evt) => {
    const { id } = evt.target;
    this.setState({
      descriptionText: this.descriptionOptions[id],
    });
  }

  clearDescriptionText = () => {
    this.setState({
      descriptionText: '',
    });
  }

  render () {
    const { language } = this.context;
    const { descriptionText } = this.state;
    const {
      isLoggedIn
    } = mapStore.getState();
    const { customColorTheme } = this.context.settings;

    return (

          <button
            style={{border: `1px solid ${customColorTheme && customColorTheme !== '' ? customColorTheme : defaultColorTheme}`}}
            className='report-sub-button pointer'
            id='print'
            onClick={this.printReport}
            onMouseEnter={this.updateDescriptionText}
            onMouseLeave={this.clearDescriptionText}
          >
            {text[language].PRINT_REPORT}

          </button>
          {!isLoggedIn ? null :
            <button
              className='report-sub-button pointer left-border-separator'
              id='subscribe'
              onClick={this.toggleSubscribe}
              onMouseEnter={this.updateDescriptionText}
              onMouseLeave={this.clearDescriptionText}
            >
              {text[language].SUBSCRIBE}

            </button>
          }

            {descriptionText}

    );
  }

}
<commit_msg>Fix border color on subscribe button; Add additional if check for updating description text to prevent flickering<commit_after>import React, { PropTypes, Component } from 'react';
import {getUrlParams} from 'utils/params';
import layerKeys from 'constants/LayerConstants';
import mapStore from 'stores/MapStore';
import mapActions from 'actions/MapActions';
import appUtils from 'utils/AppUtils';
import text from 'js/languages';
import moment from 'moment';
import {defaultColorTheme} from '../../config';
import layerActions from '../../actions/LayerActions';

export default class ReportSubscribeButtons extends Component {
  constructor(props) {
    super(props);

    this.descriptionOptions = {
      print: '',
      subscribe: 'Subscribe to receive alerts for the selected area',
    };

    this.state = {
      descriptionText: ''
    };
  }

  static contextTypes = {
    language: PropTypes.string.isRequired,
    settings: PropTypes.object.isRequired,
    map: PropTypes.object.isRequired
  };

  printReport = () => {
    const { map, settings, language } = this.context;
    const selectedFeature = map.infoWindow && map.infoWindow.getSelectedFeature();
    const {
      canopyDensity,
      activeSlopeClass,
      activeLayers,
      dynamicLayers,
      lossFromSelectIndex,
      lossToSelectIndex,
      gladStartDate,
      gladEndDate,
      terraIStartDate,
      terraIEndDate,
      viirsStartDate,
      viirsEndDate,
      modisStartDate,
      modisEndDate,
      activeFilters,
      activeVersions,
      selectedFeatureTitles
    } = mapStore.getState();

    if (selectedFeature) {

      const visibleLayers = [...activeLayers];

      if (activeLayers.indexOf(layerKeys.VIIRS_ACTIVE_FIRES) > -1) {
        const index = visibleLayers.indexOf(layerKeys.VIIRS_ACTIVE_FIRES);
        visibleLayers.splice(index, 1);
        const layer24HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES);
        const layer48HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_48HR);
        const layer72HR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_72HR);
        const layer7D = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_7D);
        const layer1YR = brApp.map.getLayer(layerKeys.VIIRS_ACTIVE_FIRES_1YR);
        const viirsLayers = [layer24HR, layer48HR, layer72HR, layer7D, layer1YR];
        viirsLayers.forEach(layer => {
          if (layer.visible === true) {
            visibleLayers.push(layer.id);
          }
        });
      }

      if (activeLayers.indexOf(layerKeys.MODIS_ACTIVE_FIRES) > -1) {
        const index = visibleLayers.indexOf(layerKeys.MODIS_ACTIVE_FIRES);
        visibleLayers.splice(index, 1);
        const layer24HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES);
        const layer48HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_48HR);
        const layer72HR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_72HR);
        const layer7D = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_7D);
        const layer1YR = brApp.map.getLayer(layerKeys.MODIS_ACTIVE_FIRES_1YR);
        const modisLayers = [layer24HR, layer48HR, layer72HR, layer7D, layer1YR];
        modisLayers.forEach(layer => {
          if (layer.visible === true) {
            visibleLayers.push(layer.id);
          }
        });
      }

      const params = getUrlParams(location.href);
      const payload = {
        lang: language,
        activeLayers: visibleLayers,
        dynamicLayers,
        tcLossFrom: lossFromSelectIndex,
        tcLossTo: lossToSelectIndex,
        gladFrom: gladStartDate,
        gladTo: gladEndDate,
        terraIFrom: moment(terraIStartDate).format('YYYY-MM-DD'),
        terraITo: moment(terraIEndDate).format('YYYY-MM-DD'),
        viirsStartDate: moment(viirsStartDate).format('YYYY-MM-DD HH:mm:ss'),
        viirsEndDate: moment(viirsEndDate).format('YYYY-MM-DD HH:mm:ss'),
        modisStartDate: moment(modisStartDate).format('YYYY-MM-DD HH:mm:ss'),
        modisEndDate: moment(modisEndDate).format('YYYY-MM-DD HH:mm:ss'),
        activeSlopeClass,
        selectedFeature,
        canopyDensity,
        settings,
        activeFilters: [],
        activeVersions: [],
        selectedFeatureTitles: selectedFeatureTitles
      };

      if (params.appid) {
        payload.appid = params.appid;
      }

      if (selectedFeature._layer && selectedFeature._layer.id && selectedFeature._layer.id !== layerKeys.USER_FEATURES) {
        let layerString = '';

        payload.OBJECTID = selectedFeature && selectedFeature.attributes ? selectedFeature.attributes[selectedFeature._layer.objectIdField] : null;
        payload.OBJECTID_Field = selectedFeature._layer.objectIdField;

        layerString = selectedFeature._layer.url;
        layerString += '--' + selectedFeature._layer.id;

        payload.layerId = layerString;
      }

      if (Object.keys(activeFilters).length) {
        Object.keys(activeFilters).forEach((key) => {
          payload.activeFilters.push(`${key}|${activeFilters[key]}`);
        });
      }

      if (Object.keys(activeVersions).length) {
        Object.keys(activeVersions).forEach((key) => {
          payload.activeVersions.push(`${key}|${activeVersions[key]}`);
        });
      }

      appUtils.generateReport(payload);
    }

  };

  toggleSubscribe = () => {
    mapActions.toggleSubscribeModal({ visible: true });
  }

  updateDescriptionText = (evt) => {
    const { id } = evt.target;
    if (this.state.descriptionText === '') {
      this.setState({
        descriptionText: this.descriptionOptions[id]
      });
    }
  }

  clearDescriptionText = () => {
    if (this.state.descriptionText !== '') {
      this.setState({
        descriptionText: ''
      });
    }
  }

  render () {
    const { language } = this.context;
    const { descriptionText } = this.state;
    const {
      isLoggedIn
    } = mapStore.getState();
    const { customColorTheme } = this.context.settings;

    return (

          <button
            style={{border: `1px solid ${customColorTheme && customColorTheme !== '' ? customColorTheme : defaultColorTheme}`}}
            className='report-sub-button pointer'
            id='print'
            onClick={this.printReport}
            onMouseEnter={this.updateDescriptionText}
            onMouseLeave={this.clearDescriptionText}
          >
            {text[language].PRINT_REPORT}

          </button>
          {!isLoggedIn ? null :
            <button
              style={{border: `1px solid ${customColorTheme && customColorTheme !== '' ? customColorTheme : defaultColorTheme}`}}
              className='report-sub-button pointer left-border-separator'
              id='subscribe'
              onClick={this.toggleSubscribe}
              onMouseEnter={this.updateDescriptionText}
              onMouseLeave={this.clearDescriptionText}
            >
              {text[language].SUBSCRIBE}

            </button>
          }

            {descriptionText}

    );
  }

}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from '../../prop_types';
import i18n from '../../i18n/i18n';
import moment from 'moment';
import $ from 'jquery';
import { uuid } from '../../utils';
import { autobind, mixin } from '../../utils/decorators';

import InputBase from './input_base';
import InputMasked from './input_masked';
import { Label } from '../label';
import { Button } from '../button';

import { CssClassMixin } from '../../mixins';

@mixin(CssClassMixin)
export default class InputDatepicker extends InputBase {
  static propTypes = {
    mask: PropTypes.string,
  };

  static defaultProps = {
    themeClassKey: 'input.datepicker',
    mask: null,
    format: null,
    maskType: 'date',
  };

  state = {
    ...this.state,
    inputMaskedKey: uuid.v4(),
  };

  componentDidMount() {
    super.componentDidMount();
    this.setPickadatePlugin();
  }

  render() {
    return (

        <InputMasked
          {...this.props}
          value={this.getFormattedDateValue()}
          className={this.className()}
          onChange={this.handleChange}
          onIncomplete={this.handleMaskIncomplete}
          key={this.state.inputMaskedKey}
          ref="input"
        />

        <Button
          disabled={this.props.disabled || this.props.readOnly}
          icon={{ type: 'calendar' }}
          className="input-datepicker__button prefix"
          onClick={this.handleCalendarClick}
          type="button"
          ref="button"
        />

    );
  }

  getDateFormat() {
    // TODO: remover deprecation warning na major version
    if (this.props.format && console && console.warn) {
      // eslint-disable-next-line no-console
      console.warn(
          'Prop "format" of component "InputDatepicker" is deprecated. Use "dateFormat" instead.'
      );
    }
    return (this.props.dateFormat || i18n.t('date.formats.date'));
  }

  getFormattedDateValue() {
    const value = this.state.value;
    return !value ? value : this.tryGetISOFormatValue(this.state.value);
  }

  tryGetISOFormatValue(value) {
    const date = moment.utc(value, moment.ISO_8601);
    return date.isValid() ? this.formatDate(date) : value;
  }

  formatDate(date) {
    return date.format(this.getDateFormat());
  }

  labelIsActive() {
    const inputValue = this.state.value;

    return (inputValue != null && String(inputValue).length > 0);
  }

  @autobind
  setPickadatePlugin() {
    const $inputNode = $(ReactDOM.findDOMNode(this.refs.input)).pickadate({
      editable: true,
      selectMonths: true,
      selectYears: true,
      format: this.getDateFormat().toLowerCase(),
      onSet: this.handlePickadateSet,
    });

    $inputNode.pickadate('on', 'close', this.props.onChange);
  }

  @autobind
  handleCalendarClick(event) {
    const $inputNode = $(ReactDOM.findDOMNode(this.refs.input));
    const picker = $inputNode.pickadate('picker');

    event.preventDefault();
    event.stopPropagation();

    requestAnimationFrame(() => { picker.get('open') ? picker.close() : picker.open(); });
  }

  @autobind
  handlePickadateSet(pickadateObject) {
    const selectedDate = moment(pickadateObject.select).format();
    this.props.onChange(null, this.getFormattedDateValue(), this);

    this.setState({
      value: selectedDate,
      inputMaskedKey: uuid.v4(),
    }, this.setPickadatePlugin);
  }

  @autobind
  handleMaskIncomplete() {
    this.setState({ value: null });
  }

  _getValue() {
    return this.getFormattedDateValue();
  }
}
<commit_msg>Ajustes component InputDatepicker<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from '../../prop_types';
import i18n from '../../i18n/i18n';
import moment from 'moment';
import $ from 'jquery';
import { uuid } from '../../utils';
import { autobind, mixin } from '../../utils/decorators';

import InputBase from './input_base';
import InputMasked from './input_masked';
import { Label } from '../label';
import { Button } from '../button';

import { CssClassMixin } from '../../mixins';

@mixin(CssClassMixin)
export default class InputDatepicker extends InputBase {
  static propTypes = {
    mask: PropTypes.string,
  };

  static defaultProps = {
    themeClassKey: 'input.datepicker',
    mask: null,
    format: null,
    maskType: 'date',
  };

  state = {
    ...this.state,
    inputMaskedKey: uuid.v4(),
  };

  componentDidMount() {
    super.componentDidMount();
    this.setPickadatePlugin();
  }

  render() {
    return (

        <InputMasked
          {...this.props}
          value={this.getFormattedDateValue()}
          className={this.className()}
          onChange={this.handleChange}
          onIncomplete={this.handleMaskIncomplete}
          key={this.state.inputMaskedKey}
          ref="input"
        />

        <Button
          disabled={this.props.disabled || this.props.readOnly}
          icon={{ type: 'calendar' }}
          className="input-datepicker__button prefix"
          onClick={this.handleCalendarClick}
          type="button"
          ref="button"
        />

    );
  }

  getDateFormat() {
    // TODO: remover deprecation warning na major version
    if (this.props.format && console && console.warn) {
      // eslint-disable-next-line no-console
      console.warn(
          'Prop "format" of component "InputDatepicker" is deprecated. Use "dateFormat" instead.'
      );
    }
    return (this.props.dateFormat || i18n.t('date.formats.date'));
  }

  getFormattedDateValue() {
    const value = this.state.value;
    return !value ? value : this.tryGetISOFormatValue(this.state.value);
  }

  tryGetISOFormatValue(value) {
    const date = moment.utc(value, moment.ISO_8601);
    return date.isValid() ? this.formatDate(date) : value;
  }

  formatDate(date) {
    return date.format(this.getDateFormat());
  }

  labelIsActive() {
    const inputValue = this.state.value;

    return (inputValue != null && String(inputValue).length > 0);
  }

  @autobind
  setPickadatePlugin() {
    const $inputNode = $(ReactDOM.findDOMNode(this.refs.input)).pickadate({
      editable: true,
      selectMonths: true,
      selectYears: true,
      format: this.getDateFormat().toLowerCase(),
      onSet: this.handlePickadateSet,
    });

    $inputNode.pickadate('on', 'close', this.props.onChange);
  }

  @autobind
  handleCalendarClick(event) {
    const $inputNode = $(ReactDOM.findDOMNode(this.refs.input));
    const picker = $inputNode.pickadate('picker');

    event.preventDefault();
    event.stopPropagation();

    requestAnimationFrame(() => { picker.get('open') ? picker.close() : picker.open(); });
  }

  @autobind
  handlePickadateSet(pickadateObject) {
    const selectedDate = moment(pickadateObject.select).format();
    this.props.onChange(null, this.getFormattedDateValue(), this);

    this.setState({
      value: selectedDate,
      inputMaskedKey: uuid.v4(),
    }, this.setPickadatePlugin);
  }

  @autobind
  handleMaskIncomplete() {
    this.setState({ value: null });
  }

  @autobind
  handleReset() {
    if (this.mounted) {
      const value = null;
      const inputMaskedKey = uuid.v4();
      this.setState({ value, inputMaskedKey }, this.setPickadatePlugin);
    }
  }

  _getValue() {
    return this.getFormattedDateValue();
  }
}
<|endoftext|>


<commit_before>import { Trans } from "@lingui/macro";
import { i18nMark } from "@lingui/react";
import { Confirm } from "reactjs-components";
import mixin from "reactjs-mixin";
import PropTypes from "prop-types";
import React from "react";
import { StoreMixin } from "mesosphere-shared-reactjs";

import MetronomeStore from "../../stores/MetronomeStore";
import ModalHeading from "../modals/ModalHeading";

const METHODS_TO_BIND = ["handleButtonConfirm"];

class JobStopRunModal extends mixin(StoreMixin) {
  constructor() {
    super(...arguments);

    this.store_listeners = [
      {
        name: "metronome",
        events: ["jobStopRunSuccess", "jobStopRunError"],
        suppressUpdate: true
      }
    ];

    this.state = {
      pendingRequest: false
    };

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  handleButtonConfirm() {
    const { selectedItems, jobID } = this.props;
    // TODO DCOS-8763 introduce support for multiple job run IDs
    if (selectedItems.length === 1) {
      MetronomeStore.stopJobRun(jobID, selectedItems[0]);
    }

    this.setState({ pendingRequest: true });
  }

  onMetronomeStoreJobStopRunSuccess() {
    this.setState({ pendingRequest: false });
    this.props.onClose();
    this.props.onSuccess();
  }

  onMetronomeStoreJobStopRunError() {
    this.props.onClose();
  }

  getContentHeader(selectedItems, selectedItemsLength) {
    let headerContent = ` ${selectedItemsLength} Job Runs`;
    if (selectedItemsLength === 1) {
      headerContent = "this";
    }

    return (
      <ModalHeading key="confirmHeader">
        {`Are you sure you want to stop ${headerContent}?`}
      </ModalHeading>
    );
  }

  getConfirmTextBody(selectedItems, selectedItemsLength) {
    const bodyText =
      selectedItemsLength === 1
        ? i18nMark("You are about to stop the job run with id") +
          ` ${selectedItems[0]}.`
        : i18nMark("You are about to stop the selected job runs.");

    return <Trans render="span" id={bodyText} />;
  }

  getModalContents() {
    const { selectedItems } = this.props;
    const selectedItemsLength = selectedItems.length;

    return (

        {this.getConfirmTextBody(selectedItems, selectedItemsLength)}

    );
  }

  render() {
    const { onClose, open, selectedItems } = this.props;
    let rightButtonText = "Stop Job Run";
    const selectedItemsLength = selectedItems.length;

    if (selectedItems.length > 1) {
      rightButtonText = "Stop Job Runs";
    }

    return (
      <Confirm
        closeByBackdropClick={true}
        disabled={this.state.pendingRequest}
        header={this.getContentHeader(selectedItems, selectedItemsLength)}
        open={open}
        onClose={onClose}
        leftButtonText="Cancel"
        leftButtonCallback={onClose}
        leftButtonClassName="button button-primary-link"
        rightButtonText={rightButtonText}
        rightButtonClassName="button button-danger"
        rightButtonCallback={this.handleButtonConfirm}
        showHeader={true}
      >
        {this.getModalContents()}
      </Confirm>
    );
  }
}

JobStopRunModal.defaultProps = {
  onSuccess() {}
};

JobStopRunModal.propTypes = {
  jobID: PropTypes.string.isRequired,
  onClose: PropTypes.func.isRequired,
  onSuccess: PropTypes.func,
  open: PropTypes.bool.isRequired,
  selectedItems: PropTypes.array.isRequired
};

export default JobStopRunModal;
<commit_msg>feat(JobStopRunModal): Translate bare strings<commit_after>import { Trans, t } from "@lingui/macro";
import { i18nMark, withI18n } from "@lingui/react";
import { Confirm } from "reactjs-components";
import mixin from "reactjs-mixin";
import PropTypes from "prop-types";
import React from "react";
import { StoreMixin } from "mesosphere-shared-reactjs";

import MetronomeStore from "../../stores/MetronomeStore";
import ModalHeading from "../modals/ModalHeading";

const METHODS_TO_BIND = ["handleButtonConfirm"];

class JobStopRunModal extends mixin(StoreMixin) {
  constructor() {
    super(...arguments);

    this.store_listeners = [
      {
        name: "metronome",
        events: ["jobStopRunSuccess", "jobStopRunError"],
        suppressUpdate: true
      }
    ];

    this.state = {
      pendingRequest: false
    };

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  handleButtonConfirm() {
    const { selectedItems, jobID } = this.props;
    // TODO DCOS-8763 introduce support for multiple job run IDs
    if (selectedItems.length === 1) {
      MetronomeStore.stopJobRun(jobID, selectedItems[0]);
    }

    this.setState({ pendingRequest: true });
  }

  onMetronomeStoreJobStopRunSuccess() {
    this.setState({ pendingRequest: false });
    this.props.onClose();
    this.props.onSuccess();
  }

  onMetronomeStoreJobStopRunError() {
    this.props.onClose();
  }

  getContentHeader(selectedItems, selectedItemsLength) {
    // L10NTODO: Pluralize
    const headerContent =
      selectedItemsLength === 1 ? (
        <Trans render="span">Are you sure you want to stop this?</Trans>
      ) : (
        <Trans render="span">
          Are you sure you want to stop {selectedItemsLength} Job Runs?
        </Trans>
      );

    return <ModalHeading key="confirmHeader">{headerContent}</ModalHeading>;
  }

  getConfirmTextBody(selectedItems, selectedItemsLength) {
    const bodyText =
      selectedItemsLength === 1
        ? i18nMark("You are about to stop the job run with id") +
          ` ${selectedItems[0]}.`
        : i18nMark("You are about to stop the selected job runs.");

    return <Trans render="span" id={bodyText} />;
  }

  getModalContents() {
    const { selectedItems } = this.props;
    const selectedItemsLength = selectedItems.length;

    return (

        {this.getConfirmTextBody(selectedItems, selectedItemsLength)}

    );
  }

  render() {
    const { onClose, open, selectedItems, i18n } = this.props;
    // L10NTODO: Pluralize
    const rightButtonText =
      selectedItems.length > 1 ? "Stop Job Runs" : "Stop Job Run";

    const selectedItemsLength = selectedItems.length;

    return (
      <Confirm
        closeByBackdropClick={true}
        disabled={this.state.pendingRequest}
        header={this.getContentHeader(selectedItems, selectedItemsLength)}
        open={open}
        onClose={onClose}
        leftButtonText={i18n._(t`Cancel`)}
        leftButtonCallback={onClose}
        leftButtonClassName="button button-primary-link"
        rightButtonText={i18n._(rightButtonText)}
        rightButtonClassName="button button-danger"
        rightButtonCallback={this.handleButtonConfirm}
        showHeader={true}
      >
        {this.getModalContents()}
      </Confirm>
    );
  }
}

JobStopRunModal.defaultProps = {
  onSuccess() {}
};

JobStopRunModal.propTypes = {
  jobID: PropTypes.string.isRequired,
  onClose: PropTypes.func.isRequired,
  onSuccess: PropTypes.func,
  open: PropTypes.bool.isRequired,
  selectedItems: PropTypes.array.isRequired
};

export default withI18n()(JobStopRunModal);
<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.controllers').controller('newVersionIsAvailable', function($scope, $modalInstance, go, newVersion){

  $scope.openDownloadLink = function(){
    var link = '';
    if (navigator && navigator.app) {
      link = 'https://play.google.com/store/apps/details?id=org.byteball.wallet'
    }
    else {
      link = 'https://github.com/byteball/byteball/releases/tag/v' + newVersion.version;
    }
    go.openExternalLink(link);
    $modalInstance.close('closed result');
  };

  $scope.later = function(){
    $modalInstance.close('closed result');
  };
});
<commit_msg>show version, testnet link<commit_after>'use strict';

angular.module('copayApp.controllers').controller('newVersionIsAvailable', function($scope, $modalInstance, go, newVersion){

  $scope.version = newVersion.version;

  $scope.openDownloadLink = function(){
    var link = '';
    if (navigator && navigator.app) {
      link = 'https://play.google.com/store/apps/details?id=org.byteball.wallet';
	  if (newVersion.version.match('t$'))
		  link += '.testnet';
    }
    else {
      link = 'https://github.com/byteball/byteball/releases/tag/v' + newVersion.version;
    }
    go.openExternalLink(link);
    $modalInstance.close('closed result');
  };

  $scope.later = function(){
    $modalInstance.close('closed result');
  };
});
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { CloudinaryImage } from 'react-cloudinary';
import _ from 'lodash';

import Breadcrumb from 'components/Breadcrumb';

import { getCategoryBreadcrumbPath } from 'commons/utils/storeUtil';
import productUtil from 'commons/utils/productUtil';
import numberUtil from 'commons/utils/numberUtil';
import i18n from 'commons/utils/i18n';

import ResponsiveImage from 'components/snippet/ResponsiveImage';

export default React.createClass({
  propTypes: {
    activeImage: PropTypes.object,
    addCartProduct: PropTypes.func,
    addFavoriteBrand: PropTypes.func,
    addWish: PropTypes.func,
    attributes: PropTypes.array,
    buyNow: PropTypes.func,
    images: PropTypes.array.isRequired,
    isLikeBrand: PropTypes.bool,
    product: PropTypes.object.isRequired,
    selectedVariant: PropTypes.object,
    setActiveImage: PropTypes.func,
    toggleWish: PropTypes.func,
    variantAttributes: PropTypes.object,
    wishId: PropTypes.number,
  },
  contextTypes: {
    activeLocale: PropTypes.string,
    activeCurrency: PropTypes.string,
    currencySign: PropTypes.object,
  },
  componentDidMount() {
    // 2016. 04. 14. [heekyu] timing issue
    setTimeout(() => this.adjustScroll(this.props), 100);
  },
  componentWillReceiveProps(nextProps) {
    this.adjustScroll(nextProps);
  },
  setActiveImage(image) {
    if (image.url) {
      $('.main-image-box img').attr('src', image.url);
    }
    this.props.setActiveImage(image);
  },
  handleMouseEnterThumbnail(image) {
    this.setActiveImage(image);
  },
  handleMouseEnterMainImage() {
    // TODO make this to external state
    $('.enlarge-image-box').css('display', 'block');
  },
  handleMouseMoveMainImage(e) {
    // TODO make this to external state
    const width = 50; // TODO
    const height = width / 10 * 13;
    const offset = $(e.target).offset();
    const x = e.pageX - offset.left;
    const y = e.pageY - offset.top;
    const left = Math.max(x - width, 0);
    const top = Math.max(y - height, 0);
    const zoomElem = $('.enlarge-image-box img');
    zoomElem.css('margin-left', `-${left}px`);
    zoomElem.css('margin-top', `-${top}px`);
  },
  handleMouseLeaveMainImage() {
    // TODO make this to external state
    $('.enlarge-image-box').css('display', 'none');
  },
  adjustScroll(props) {
    const { images, activeImage } = props;
    let activeIndex = -1;
    images.forEach((image, index) => {
      if (activeImage && image.url === activeImage.url) {
        activeIndex = index;
      }
    });
    const thumbnailContainer = $('.thumbnail-container');
    if (activeIndex < 0 || thumbnailContainer.length < 1) {
      return;
    }
    const getScrollTop = (index) => $('.thumbnail-container span').eq(index).offset().top - $('.thumbnail-container span').eq(0).offset().top; // eslint-disable-line
    const currentTop = thumbnailContainer.scrollTop();
    const currentBottom = currentTop + 570;
    const activeTop = getScrollTop(activeIndex);
    if (activeTop < currentTop) {
      thumbnailContainer.stop().animate({ scrollTop: activeTop }, '500');
    } else if (activeTop > currentBottom - 50) {
      thumbnailContainer.stop().animate({ scrollTop: getScrollTop(activeIndex - 5) }, '500');
    }
  },
  render() {
    const { product, images, activeImage, variantAttributes, attributes, selectedVariant } = this.props
    const { addCartProduct, buyNow, toggleWish, addFavoriteBrand, isLikeBrand, wishId } = this.props;
    if (!product || !variantAttributes) {
      return (

);
    }
    const brand = product.brand;
    const { activeLocale, activeCurrency, currencySign } = this.context;
    const renderPreload = (image) => ;
    const renderThumbnail = (image) => {
      let className = '';
      if (activeImage && image.url === activeImage.url) {
        className = 'image-active';
      }
      //
{renderImage(image)}

      return (
         this.handleMouseEnterThumbnail(image)}>
          <ResponsiveImage image={image} width={120} />

      );
    };
    const thumbnailUp = () => {
      if (!activeImage) {
        return;
      }
      for (let i = 1; i < images.length; i++) {
        if (images[i].url === activeImage.url) {
          this.setActiveImage(images[i - 1]);
          break;
        }
      }
    };
    const thumbnailDown = () => {
      if (!activeImage) {
        return;
      }
      for (let i = 0; i < images.length - 1; i++) {
        if (images[i].url === activeImage.url) {
          this.setActiveImage(images[i + 1]);
          break;
        }
      }
    };

    const renderPath = (categoryPath, index) => {
      const crumbPath = [{ link: '/', name: 'Home' }];
      for (let i = 0; i < categoryPath.ko.length; i++) {
        crumbPath.push({ link: 'products', name: categoryPath.ko[i] });
      }
      crumbPath.push({ name: productUtil.getName(product) });
      return (<Breadcrumb key={`breadcrumb-${index}`} path={crumbPath} />);
    };

    let isColorSelected = false;
    let isSizeSelected = false;
    const renderColor = (attrObj, fnOnSelect) => {
      const keys = Object.keys(attrObj);
      const render = (key) => {
        const obj = attrObj[key];
        let className = 'img-variant-color';
        if (!obj.enable) {
          className += ' disable-item';
        }
        if (obj.selected) {
          className += ' active';
          isColorSelected = true;
        }
        if (obj.img.publicId) {
          return (<CloudinaryImage
            className={className}
            key={key}
            onClick={() => fnOnSelect(key)}
            publicId={obj.img.publicId}
            options={{ width: 120, crop: 'scale' }}
          />);
        }
        return ( fnOnSelect(key)} src={obj.img.url} />);
      };
      return (
        <div key={`product-color-${product.id}`} className="color-line">

{i18n.get('pcItemDetail.color')}

            {keys.map(render)}

      );
    };
    const renderSize = (attrObj, fnOnSelect) => {
      const keys = Object.keys(attrObj);
      const render = (key) => {
        const obj = attrObj[key];
        let className = '';
        if (!obj.enable) {
          className += ' disable-item';
        }
        if (obj.selected) {
          className += ' active';
          isSizeSelected = true;
        }
        return (

 fnOnSelect(key)} className={`attribute-item-text${className}`}>{key}

        );
      };
      return (
        <div key={`product-size-${product.id}`} className="size-line">

{i18n.get('pcItemDetail.size')}

            {keys.map(render)}

      );
    };

    const renderAllAttributes = () => {
      const res = [];
      attributes.forEach((attr) => {
        if (attr.attrName === 'Color') {
          res.push(renderColor(variantAttributes[attr.key], attr.select));
        } else if (attr.attrName === 'Size') {
          res.push(renderSize(variantAttributes[attr.key], attr.select));
        }
      });

      return res;
    };

    let path;
    if (product.categories && product.categories.length) {
      const genLinkUrl = (category) => `/categories/${category.id}`;
      path = getCategoryBreadcrumbPath(product.categories[product.categories.length - 1], genLinkUrl);
      // 2016. 06. 21. [heekyu] 4th category is not display
      if (path.length > 4) {
        path = path.slice(0, 4);
      }
      if (path && path.length) {
        path.unshift({ link: '/', name: i18n.getObj('word.home') });
        const productBreadcrumbName = {};
        Object.keys(product.name).forEach((locale) => {
          productBreadcrumbName[locale] = product.name[locale] || product.id;
        });
        path.push({ name: productBreadcrumbName });
      }
    }
    if (!path) {
      path = [
        { link: '/', name: i18n.getObj('word.home') },
        { link: '/products', name: { en: 'Product List', ko: '상품목록' } },
        { name: productUtil.getAllNames(product) },
      ];
    }
    let priceKRW = 0;
    let price = 0;
    if (selectedVariant) {
      price = selectedVariant[activeCurrency];
      priceKRW = selectedVariant.KRW;
    } else {
      price = product[activeCurrency];
      priceKRW = product.KRW;
    }

    const brandLink = () => (brand.pathname ? `/${brand.pathname}` : `/brands/${brand.id}`);

    const renderBrand = () => {
      if (brand) {
        const renderFavoriteButton = () => {
          if (isLikeBrand) {
            return (
              <Link to={brandLink()} className="favorite-brand">♥ {i18n.get('pcItemDetail.favoriteBrands')}</Link>
            );
          }
          return (
             addFavoriteBrand(product.brand.id)}>{i18n.get('pcItemDetail.addToFavoriteBrands')}
          );
        };
        return (

{i18n.get('pcItemDetail.brand')}

              <Link to={`/brands/${brand.id}`}>{_.get(brand, `name.${activeLocale}`)}</Link>
              {renderFavoriteButton()}

        );
      }
      return '';
    };

    const renderPrice = () => {
      let approximately = null;
      if (activeCurrency !== 'KRW') {
        approximately = `Approximately ${numberUtil.formatPrice(price, activeCurrency, currencySign)}`;
      }
      return (

            {numberUtil.format(priceKRW)} KRW

{approximately}

      );
    };

    let animation = null;
    const isAttributesSelected = () => {
      const doAnimation = (elem, borderSize) => {
        let opacity = 10;
        if (animation) {
          clearInterval(animation);
        }
        animation = setInterval(() => {
          if (opacity === 0) {
            clearInterval(animation);
            animation = null;
            elem.css('border', '');
            return;
          }
          elem.css('border', `${borderSize}px solid rgba(240, 80, 0, ${opacity / 10}`);
          opacity -= 1;
        }, 200);
      };
      if (!isColorSelected) {
        doAnimation($('.color-line img'), 2);
        return false;
      }
      if (!isSizeSelected) {
        doAnimation($('.size-line .attribute-item-text'), 2);
        return false;
      }
      // TODO is there attributes other than color, size ?
      return true;
    };
    const onBuyNow = () => {
      if (!isAttributesSelected()) {
        return;
      }
      buyNow(selectedVariant.id, this.refs.quantity.value);
    };

    const onAddCart = () => {
      if (!isAttributesSelected()) {
        return;
      }
      addCartProduct(selectedVariant.id, this.refs.quantity.value);
    };
    const renderWarning = () => {
      if (!isColorSelected) {
        return (
{i18n.get('pcItemDetail.selectColor')}
);
      }
      if (!isSizeSelected) {
        return (
{i18n.get('pcItemDetail.selectSize')}
);
      }
      return null;
    };

    return (

          {images.map(renderPreload)}

        <Breadcrumb key="breadcrumb-default" path={path} />
        {/* TODO product.data && product.data.categoryPath ? product.data.categoryPath.map(renderPath) : [] */}

                {images.map(renderThumbnail)}

                {i18n.get('pcItemDetail.warnLinkshopsImage1')}

                {i18n.get('pcItemDetail.warnLinkshopsImage2')}

              {productUtil.getName(product)}
              <div className={`wish-button ${wishId ? 'active' : ''}`} onClick={toggleWish}>

{i18n.get('pcCart.price')}

              {renderPrice()}

{i18n.get('pcItemDetail.skuNumber')}

{product.id}

{i18n.get('pcItemDetail.building')}

{_.get(product, 'brand.data.location.building.name.ko')}

            {renderBrand()}
            {renderAllAttributes()}
            {/*

Quantity:

<input type="number" defaultValue="1" min="1" ref="quantity" />

            {renderBrand()}
             */}

{i18n.get('pcItemDetail.quantity')}

                  <input className="input-number-nospin" min="1" defaultValue="1" ref="quantity" type="number" />

 $('.input-number-nospin').val(+this.refs.quantity.value + 1)}>

 $('.input-number-nospin').val(Math.max(+this.refs.quantity.value - 1, 1))}>

            {renderWarning()}

              <button className="product-buy-now-button"
                onClick={onBuyNow}
              >
                {i18n.get('pcItemDetail.orderNow')}
              </button>
              <button className="product-add-to-cart-button"
                onClick={onAddCart}
              >
                {i18n.get('pcItemDetail.cart')}
              </button>

    );
  },
});
<commit_msg>product detail - show warning when click button<commit_after>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { CloudinaryImage } from 'react-cloudinary';
import _ from 'lodash';

import Breadcrumb from 'components/Breadcrumb';

import { getCategoryBreadcrumbPath } from 'commons/utils/storeUtil';
import productUtil from 'commons/utils/productUtil';
import numberUtil from 'commons/utils/numberUtil';
import i18n from 'commons/utils/i18n';

import ResponsiveImage from 'components/snippet/ResponsiveImage';

export default React.createClass({
  propTypes: {
    activeImage: PropTypes.object,
    addCartProduct: PropTypes.func,
    addFavoriteBrand: PropTypes.func,
    addWish: PropTypes.func,
    attributes: PropTypes.array,
    buyNow: PropTypes.func,
    images: PropTypes.array.isRequired,
    isLikeBrand: PropTypes.bool,
    product: PropTypes.object.isRequired,
    selectedVariant: PropTypes.object,
    setActiveImage: PropTypes.func,
    toggleWish: PropTypes.func,
    variantAttributes: PropTypes.object,
    wishId: PropTypes.number,
  },
  contextTypes: {
    activeLocale: PropTypes.string,
    activeCurrency: PropTypes.string,
    currencySign: PropTypes.object,
  },
  getInitialState() {
    return {};
  },
  componentDidMount() {
    // 2016. 04. 14. [heekyu] timing issue
    setTimeout(() => this.adjustScroll(this.props), 100);
  },
  componentWillReceiveProps(nextProps) {
    this.adjustScroll(nextProps);
  },
  setActiveImage(image) {
    if (image.url) {
      $('.main-image-box img').attr('src', image.url);
    }
    this.props.setActiveImage(image);
  },
  handleMouseEnterThumbnail(image) {
    this.setActiveImage(image);
  },
  handleMouseEnterMainImage() {
    // TODO make this to external state
    $('.enlarge-image-box').css('display', 'block');
  },
  handleMouseMoveMainImage(e) {
    // TODO make this to external state
    const width = 50; // TODO
    const height = width / 10 * 13;
    const offset = $(e.target).offset();
    const x = e.pageX - offset.left;
    const y = e.pageY - offset.top;
    const left = Math.max(x - width, 0);
    const top = Math.max(y - height, 0);
    const zoomElem = $('.enlarge-image-box img');
    zoomElem.css('margin-left', `-${left}px`);
    zoomElem.css('margin-top', `-${top}px`);
  },
  handleMouseLeaveMainImage() {
    // TODO make this to external state
    $('.enlarge-image-box').css('display', 'none');
  },
  adjustScroll(props) {
    const { images, activeImage } = props;
    let activeIndex = -1;
    images.forEach((image, index) => {
      if (activeImage && image.url === activeImage.url) {
        activeIndex = index;
      }
    });
    const thumbnailContainer = $('.thumbnail-container');
    if (activeIndex < 0 || thumbnailContainer.length < 1) {
      return;
    }
    const getScrollTop = (index) => $('.thumbnail-container span').eq(index).offset().top - $('.thumbnail-container span').eq(0).offset().top; // eslint-disable-line
    const currentTop = thumbnailContainer.scrollTop();
    const currentBottom = currentTop + 570;
    const activeTop = getScrollTop(activeIndex);
    if (activeTop < currentTop) {
      thumbnailContainer.stop().animate({ scrollTop: activeTop }, '500');
    } else if (activeTop > currentBottom - 50) {
      thumbnailContainer.stop().animate({ scrollTop: getScrollTop(activeIndex - 5) }, '500');
    }
  },
  render() {
    const { product, images, activeImage, variantAttributes, attributes, selectedVariant } = this.props
    const { addCartProduct, buyNow, toggleWish, addFavoriteBrand, isLikeBrand, wishId } = this.props;
    if (!product || !variantAttributes) {
      return (

);
    }
    const brand = product.brand;
    const { activeLocale, activeCurrency, currencySign } = this.context;
    const renderPreload = (image) => ;
    const renderThumbnail = (image) => {
      let className = '';
      if (activeImage && image.url === activeImage.url) {
        className = 'image-active';
      }
      //
{renderImage(image)}

      return (
         this.handleMouseEnterThumbnail(image)}>
          <ResponsiveImage image={image} width={120} />

      );
    };
    const thumbnailUp = () => {
      if (!activeImage) {
        return;
      }
      for (let i = 1; i < images.length; i++) {
        if (images[i].url === activeImage.url) {
          this.setActiveImage(images[i - 1]);
          break;
        }
      }
    };
    const thumbnailDown = () => {
      if (!activeImage) {
        return;
      }
      for (let i = 0; i < images.length - 1; i++) {
        if (images[i].url === activeImage.url) {
          this.setActiveImage(images[i + 1]);
          break;
        }
      }
    };

    const renderPath = (categoryPath, index) => {
      const crumbPath = [{ link: '/', name: 'Home' }];
      for (let i = 0; i < categoryPath.ko.length; i++) {
        crumbPath.push({ link: 'products', name: categoryPath.ko[i] });
      }
      crumbPath.push({ name: productUtil.getName(product) });
      return (<Breadcrumb key={`breadcrumb-${index}`} path={crumbPath} />);
    };

    let isColorSelected = false;
    let isSizeSelected = false;
    const renderColor = (attrObj, fnOnSelect) => {
      const keys = Object.keys(attrObj);
      const render = (key) => {
        const obj = attrObj[key];
        let className = 'img-variant-color';
        if (!obj.enable) {
          className += ' disable-item';
        }
        if (obj.selected) {
          className += ' active';
          isColorSelected = true;
        }
        if (obj.img.publicId) {
          return (<CloudinaryImage
            className={className}
            key={key}
            onClick={() => fnOnSelect(key)}
            publicId={obj.img.publicId}
            options={{ width: 120, crop: 'scale' }}
          />);
        }
        return ( fnOnSelect(key)} src={obj.img.url} />);
      };
      return (
        <div key={`product-color-${product.id}`} className="color-line">

{i18n.get('pcItemDetail.color')}

            {keys.map(render)}

      );
    };
    const renderSize = (attrObj, fnOnSelect) => {
      const keys = Object.keys(attrObj);
      const render = (key) => {
        const obj = attrObj[key];
        let className = '';
        if (!obj.enable) {
          className += ' disable-item';
        }
        if (obj.selected) {
          className += ' active';
          isSizeSelected = true;
        }
        return (

 fnOnSelect(key)} className={`attribute-item-text${className}`}>{key}

        );
      };
      return (
        <div key={`product-size-${product.id}`} className="size-line">

{i18n.get('pcItemDetail.size')}

            {keys.map(render)}

      );
    };

    const renderAllAttributes = () => {
      const res = [];
      attributes.forEach((attr) => {
        if (attr.attrName === 'Color') {
          res.push(renderColor(variantAttributes[attr.key], attr.select));
        } else if (attr.attrName === 'Size') {
          res.push(renderSize(variantAttributes[attr.key], attr.select));
        }
      });

      return res;
    };

    let path;
    if (product.categories && product.categories.length) {
      const genLinkUrl = (category) => `/categories/${category.id}`;
      path = getCategoryBreadcrumbPath(product.categories[product.categories.length - 1], genLinkUrl);
      // 2016. 06. 21. [heekyu] 4th category is not display
      if (path.length > 4) {
        path = path.slice(0, 4);
      }
      if (path && path.length) {
        path.unshift({ link: '/', name: i18n.getObj('word.home') });
        const productBreadcrumbName = {};
        Object.keys(product.name).forEach((locale) => {
          productBreadcrumbName[locale] = product.name[locale] || product.id;
        });
        path.push({ name: productBreadcrumbName });
      }
    }
    if (!path) {
      path = [
        { link: '/', name: i18n.getObj('word.home') },
        { link: '/products', name: { en: 'Product List', ko: '상품목록' } },
        { name: productUtil.getAllNames(product) },
      ];
    }
    let priceKRW = 0;
    let price = 0;
    if (selectedVariant) {
      price = selectedVariant[activeCurrency];
      priceKRW = selectedVariant.KRW;
    } else {
      price = product[activeCurrency];
      priceKRW = product.KRW;
    }

    const brandLink = () => (brand.pathname ? `/${brand.pathname}` : `/brands/${brand.id}`);

    const renderBrand = () => {
      if (brand) {
        const renderFavoriteButton = () => {
          if (isLikeBrand) {
            return (
              <Link to={brandLink()} className="favorite-brand">♥ {i18n.get('pcItemDetail.favoriteBrands')}</Link>
            );
          }
          return (
             addFavoriteBrand(product.brand.id)}>{i18n.get('pcItemDetail.addToFavoriteBrands')}
          );
        };
        return (

{i18n.get('pcItemDetail.brand')}

              <Link to={`/brands/${brand.id}`}>{_.get(brand, `name.${activeLocale}`)}</Link>
              {renderFavoriteButton()}

        );
      }
      return '';
    };

    const renderPrice = () => {
      let approximately = null;
      if (activeCurrency !== 'KRW') {
        approximately = `Approximately ${numberUtil.formatPrice(price, activeCurrency, currencySign)}`;
      }
      return (

            {numberUtil.format(priceKRW)} KRW

{approximately}

      );
    };

    let animation = null;
    const isAttributesSelected = () => {
      const doAnimation = (elem, borderSize) => {
        let opacity = 10;
        if (animation) {
          clearInterval(animation);
        }
        animation = setInterval(() => {
          if (opacity === 0) {
            clearInterval(animation);
            animation = null;
            elem.css('border', '');
            return;
          }
          elem.css('border', `${borderSize}px solid rgba(240, 80, 0, ${opacity / 10}`);
          opacity -= 1;
        }, 200);
        if (!this.state.isShowWarning) {
          this.setState({ isShowWarning: true });
        }
      };
      if (!isColorSelected) {
        doAnimation($('.color-line img'), 2);
        return false;
      }
      if (!isSizeSelected) {
        doAnimation($('.size-line .attribute-item-text'), 2);
        return false;
      }
      // TODO is there attributes other than color, size ?
      return true;
    };
    const onBuyNow = () => {
      if (!isAttributesSelected()) {
        return;
      }
      buyNow(selectedVariant.id, this.refs.quantity.value);
    };

    const onAddCart = () => {
      if (!isAttributesSelected()) {
        return;
      }
      addCartProduct(selectedVariant.id, this.refs.quantity.value);
    };
    const renderWarning = () => {
      if (!this.state.isShowWarning) {
        return null;
      }
      if (!isColorSelected) {
        return (
{i18n.get('pcItemDetail.selectColor')}
);
      }
      if (!isSizeSelected) {
        return (
{i18n.get('pcItemDetail.selectSize')}
);
      }
      return null;
    };

    return (

          {images.map(renderPreload)}

        <Breadcrumb key="breadcrumb-default" path={path} />
        {/* TODO product.data && product.data.categoryPath ? product.data.categoryPath.map(renderPath) : [] */}

                {images.map(renderThumbnail)}

                {i18n.get('pcItemDetail.warnLinkshopsImage1')}

                {i18n.get('pcItemDetail.warnLinkshopsImage2')}

              {productUtil.getName(product)}
              <div className={`wish-button ${wishId ? 'active' : ''}`} onClick={toggleWish}>

{i18n.get('pcCart.price')}

              {renderPrice()}

{i18n.get('pcItemDetail.skuNumber')}

{product.id}

{i18n.get('pcItemDetail.building')}

{_.get(product, 'brand.data.location.building.name.ko')}

            {renderBrand()}
            {renderAllAttributes()}
            {/*

Quantity:

<input type="number" defaultValue="1" min="1" ref="quantity" />

            {renderBrand()}
             */}

{i18n.get('pcItemDetail.quantity')}

                  <input className="input-number-nospin" min="1" defaultValue="1" ref="quantity" type="number" />

 $('.input-number-nospin').val(+this.refs.quantity.value + 1)}>

 $('.input-number-nospin').val(Math.max(+this.refs.quantity.value - 1, 1))}>

            {renderWarning()}

              <button className="product-buy-now-button"
                onClick={onBuyNow}
              >
                {i18n.get('pcItemDetail.orderNow')}
              </button>
              <button className="product-add-to-cart-button"
                onClick={onAddCart}
              >
                {i18n.get('pcItemDetail.cart')}
              </button>

    );
  },
});
<|endoftext|>


<commit_before>// Copyright 2016-2018, University of Colorado Boulder

/**
 * main view for the 'Energy Systems' screen of the Energy Forms And Changes simulation
 *
 * @author  John Blanco
 * @author  Martin Veillette (Berea College)
 * @author  Jesse Greenberg
 * @author  Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var BeakerHeaterNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BeakerHeaterNode' );
  var BeltNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BeltNode' );
  var BikerNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BikerNode' );
  var Checkbox = require( 'SUN/Checkbox' );
  var EFACA11yStrings = require( 'ENERGY_FORMS_AND_CHANGES/EFACA11yStrings' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var EnergyChunk = require( 'ENERGY_FORMS_AND_CHANGES/common/model/EnergyChunk' );
  var EnergyChunkLegend = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EnergyChunkLegend' );
  var EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var EnergySystemElementSelector = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EnergySystemElementSelector' );
  var EnergyType = require( 'ENERGY_FORMS_AND_CHANGES/common/model/EnergyType' );
  var FanNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/FanNode' );
  var FaucetAndWaterNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/FaucetAndWaterNode' );
  var GeneratorNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/GeneratorNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LayoutBox = require( 'SCENERY/nodes/LayoutBox' );
  var LightBulbNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/LightBulbNode' );
  var ModelViewTransform2 = require( 'PHETCOMMON/view/ModelViewTransform2' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var ResetAllButton = require( 'SCENERY_PHET/buttons/ResetAllButton' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var SolarPanelNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/SolarPanelNode' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var SunNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/SunNode' );
  var TeaKettleNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/TeaKettleNode' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Vector2 = require( 'DOT/Vector2' );

  // strings
  var energySymbolsString = require( 'string!ENERGY_FORMS_AND_CHANGES/energySymbols' );

  // constants
  var EDGE_INSET = 10;
  var SELECTOR_SPACING = 81;
  var BOTTOM_CONTROL_PANEL_HEIGHT = 49; // manually coordinated to match similar panel on 1st screen

  /**
   * @param {EnergySystemsModel} model
   * @constructor
   */
  function EnergySystemsScreenView( model ) {

    var self = this;

    ScreenView.call( this );

    // a11y - the screen summary to be read by assistive technology
    this.addChild( new Node( {
      tagName: 'div',
      innerContent: EFACA11yStrings.energySystemsScreenInteractionHint.value,
      descriptionContent: EFACA11yStrings.energySystemsScreenSummaryDescription.value
    } ) );

    // a11y - a description of the current configuration of the energy system to be read by assistive technology
    var energySystemConfigDescription = new Node( {
      tagName: 'h3',
      innerContent: EFACA11yStrings.energySystem.value,
      descriptionContent: EFACA11yStrings.energySystemHelpText.value
    } );
    this.addChild( energySystemConfigDescription );

    // update the a11y description as the selected element changes
    Property.multilink(
      [
        model.energySourcesCarousel.targetIndexProperty,
        model.energyConvertersCarousel.targetIndexProperty,
        model.energyUsersCarousel.targetIndexProperty
      ],
      function() {
        var energySource = model.energySourcesCarousel.getSelectedElement();
        var energyConverter = model.energyConvertersCarousel.getSelectedElement();
        var energyUser = model.energyUsersCarousel.getSelectedElement();
        assert && assert( energySource.a11yName, 'the selected element has no accessibility name specified' );
        energySystemConfigDescription.descriptionContent = StringUtils.fillIn(
          EFACA11yStrings.energySystemHelpText.value,
          {
            producer: energySource.a11yName,
            converter: energyConverter.a11yName,
            user: energyUser.a11yName
          }
        );
      }
    );

    // convenience variable
    var layoutBounds = this.layoutBounds;

    // Create the model-view transform.  The primary units used in the model are meters, so significant zoom is used.
    // The multipliers for the 2nd parameter can be used to adjust where the point (0, 0) in the model, which is on the
    // middle of the screen above the counter as located in the view. Final arg is zoom factor from original Java sim -
    // smaller zooms out, larger zooms in.
    var mvtOriginX = Math.round( layoutBounds.width * 0.5 );
    var mvtOriginY = Math.round( layoutBounds.height * 0.475 );
    var modelViewTransform = ModelViewTransform2.createSinglePointScaleInvertedYMapping(
      Vector2.ZERO, new Vector2( mvtOriginX, mvtOriginY ), 2200
    );

    // layer node for back-most layer
    var backLayer = new Node();
    this.addChild( backLayer );

    // create the checkbox that controls the visibility of the energy chunks
    // The EnergyChunk that is created in here is not going to be used in the simulation, it is only needed in the
    // EnergyChunkNode that is displayed in the show/hide energy chunks toggle.
    var showEnergyChunksCheckbox = new Checkbox(
      new LayoutBox( {
        children: [
          new Text( energySymbolsString, { font: new PhetFont( 20 ) } ),
          new EnergyChunkNode(
            new EnergyChunk( EnergyType.THERMAL, Vector2.ZERO, Vector2.ZERO, new Property( true ) ),
            modelViewTransform
          )
        ],
        orientation: 'horizontal',
        spacing: 5
      } ),
      model.energyChunksVisibleProperty
    );

    // add the checkbox that controls the visibility of the energy chunks to a panel and then add it to the scene graph
    var showEnergyChunksPanel = new Panel( showEnergyChunksCheckbox, {
      fill: EFACConstants.CONTROL_PANEL_BACKGROUND_COLOR,
      stroke: EFACConstants.CONTROL_PANEL_OUTLINE_STROKE,
      lineWidth: EFACConstants.CONTROL_PANEL_OUTLINE_LINE_WIDTH,
      right: layoutBounds.maxX - EDGE_INSET,
      top: EDGE_INSET
    } );
    self.addChild( showEnergyChunksPanel );

    // add the energy chunk legend
    var energyChunkLegend = new EnergyChunkLegend( modelViewTransform,
      {
        right: layoutBounds.maxX - EDGE_INSET,
        top: showEnergyChunksPanel.bottom + 10
      } );
    this.addChild( energyChunkLegend );

    // only show the energy chunk legend when energy chunks are visible
    model.energyChunksVisibleProperty.linkAttribute( energyChunkLegend, 'visible' );

    // create a background rectangle at the bottom of the screen where the play/pause controls will reside
    var bottomPanel = new Rectangle(
      0,
      0,
      layoutBounds.width * 2, // wide enough that users are unlikely to see the edge
      layoutBounds.height, // tall enough that users are unlikely to see the bottom
      {
        centerX: layoutBounds.centerX,
        top: layoutBounds.maxY - BOTTOM_CONTROL_PANEL_HEIGHT,
        fill: EFACConstants.CLOCK_CONTROL_BACKGROUND_COLOR
      }
    );
    backLayer.addChild( bottomPanel );

    // add the reset all button
    var resetAllButton = new ResetAllButton( {
      listener: function() {
        model.reset();
      },
      radius: EFACConstants.RESET_ALL_BUTTON_RADIUS,
      right: layoutBounds.maxX - EDGE_INSET,
      centerY: ( bottomPanel.top + layoutBounds.maxY ) / 2
    } );
    self.addChild( resetAllButton );

    // create the energy user nodes
    this.beakerHeaterNode = new BeakerHeaterNode(
      model.beakerHeater,
      model.energyChunksVisibleProperty,
      modelViewTransform
    );
    var incandescentBulbNode = new LightBulbNode(
      model.incandescentBulb,
      model.energyChunksVisibleProperty,
      modelViewTransform,
      { bulbType: 'incandescent' }
    );
    var fluorescentBulbNode = new LightBulbNode(
      model.fluorescentBulb,
      model.energyChunksVisibleProperty,
      modelViewTransform,
      { bulbType: 'fluorescent' }
    );
    var fanNode = new FanNode(
      model.fan,
      model.energyChunksVisibleProperty,
      modelViewTransform
    );
    this.addChild( this.beakerHeaterNode );
    this.addChild( incandescentBulbNode );
    this.addChild( fluorescentBulbNode );
    this.addChild( fanNode );

    // create the energy converter nodes
    var generatorNode = new GeneratorNode( model.generator, modelViewTransform );
    var beltNode = new BeltNode( model.belt, modelViewTransform );
    var solarPanelNode = new SolarPanelNode( model.solarPanel, modelViewTransform );
    this.addChild( generatorNode );
    this.addChild( beltNode );
    this.addChild( solarPanelNode );

    // create the energy source nodes
    this.faucetNode = new FaucetAndWaterNode( model.faucet, model.energyChunksVisibleProperty, modelViewTransform );
    var sunNode = new SunNode( model.sun, model.energyChunksVisibleProperty, modelViewTransform );
    this.teaKettleNode = new TeaKettleNode( model.teaKettle, model.energyChunksVisibleProperty, modelViewTransform );
    var bikerNode = new BikerNode( model.biker, model.energyChunksVisibleProperty, modelViewTransform );
    this.addChild( sunNode );
    this.addChild( this.faucetNode );
    this.addChild( bikerNode );
    this.addChild( this.teaKettleNode );

    // add the energy system element selectors, which are sets of radio buttons
    var energySourceSelector = new EnergySystemElementSelector( model.energySourcesCarousel, {
      left: EDGE_INSET,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    var energyConverterSelector = new EnergySystemElementSelector( model.energyConvertersCarousel, {
      left: energySourceSelector.right + SELECTOR_SPACING,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    var energyUserSelector = new EnergySystemElementSelector( model.energyUsersCarousel, {
      left: energyConverterSelector.right + SELECTOR_SPACING,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    this.addChild( energySourceSelector );
    this.addChild( energyConverterSelector );
    this.addChild( energyUserSelector );
  }

  energyFormsAndChanges.register( 'EnergySystemsScreenView', EnergySystemsScreenView );

  return inherit( ScreenView, EnergySystemsScreenView, {

    /**
     * step this view element, called by the framework
     * @param dt - time step, in seconds
     * @public
     */
    step: function( dt ) {
      this.teaKettleNode.steamNode.step( dt );
      this.beakerHeaterNode.step( dt );
      this.faucetNode.step( dt );
    }
  } );
} );

<commit_msg>Fine tune panel position below converter, see https://github.com/phetsims/energy-forms-and-changes/issues/79<commit_after>// Copyright 2016-2018, University of Colorado Boulder

/**
 * main view for the 'Energy Systems' screen of the Energy Forms And Changes simulation
 *
 * @author  John Blanco
 * @author  Martin Veillette (Berea College)
 * @author  Jesse Greenberg
 * @author  Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var BeakerHeaterNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BeakerHeaterNode' );
  var BeltNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BeltNode' );
  var BikerNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/BikerNode' );
  var Checkbox = require( 'SUN/Checkbox' );
  var EFACA11yStrings = require( 'ENERGY_FORMS_AND_CHANGES/EFACA11yStrings' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var EnergyChunk = require( 'ENERGY_FORMS_AND_CHANGES/common/model/EnergyChunk' );
  var EnergyChunkLegend = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EnergyChunkLegend' );
  var EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var EnergySystemElementSelector = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EnergySystemElementSelector' );
  var EnergyType = require( 'ENERGY_FORMS_AND_CHANGES/common/model/EnergyType' );
  var FanNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/FanNode' );
  var FaucetAndWaterNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/FaucetAndWaterNode' );
  var GeneratorNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/GeneratorNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LayoutBox = require( 'SCENERY/nodes/LayoutBox' );
  var LightBulbNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/LightBulbNode' );
  var ModelViewTransform2 = require( 'PHETCOMMON/view/ModelViewTransform2' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var ResetAllButton = require( 'SCENERY_PHET/buttons/ResetAllButton' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var SolarPanelNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/SolarPanelNode' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var SunNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/SunNode' );
  var TeaKettleNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/TeaKettleNode' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Vector2 = require( 'DOT/Vector2' );

  // strings
  var energySymbolsString = require( 'string!ENERGY_FORMS_AND_CHANGES/energySymbols' );

  // constants
  var EDGE_INSET = 10;
  var SELECTOR_SPACING = 82;
  var BOTTOM_CONTROL_PANEL_HEIGHT = 49; // manually coordinated to match similar panel on 1st screen

  /**
   * @param {EnergySystemsModel} model
   * @constructor
   */
  function EnergySystemsScreenView( model ) {

    var self = this;

    ScreenView.call( this );

    // a11y - the screen summary to be read by assistive technology
    this.addChild( new Node( {
      tagName: 'div',
      innerContent: EFACA11yStrings.energySystemsScreenInteractionHint.value,
      descriptionContent: EFACA11yStrings.energySystemsScreenSummaryDescription.value
    } ) );

    // a11y - a description of the current configuration of the energy system to be read by assistive technology
    var energySystemConfigDescription = new Node( {
      tagName: 'h3',
      innerContent: EFACA11yStrings.energySystem.value,
      descriptionContent: EFACA11yStrings.energySystemHelpText.value
    } );
    this.addChild( energySystemConfigDescription );

    // update the a11y description as the selected element changes
    Property.multilink(
      [
        model.energySourcesCarousel.targetIndexProperty,
        model.energyConvertersCarousel.targetIndexProperty,
        model.energyUsersCarousel.targetIndexProperty
      ],
      function() {
        var energySource = model.energySourcesCarousel.getSelectedElement();
        var energyConverter = model.energyConvertersCarousel.getSelectedElement();
        var energyUser = model.energyUsersCarousel.getSelectedElement();
        assert && assert( energySource.a11yName, 'the selected element has no accessibility name specified' );
        energySystemConfigDescription.descriptionContent = StringUtils.fillIn(
          EFACA11yStrings.energySystemHelpText.value,
          {
            producer: energySource.a11yName,
            converter: energyConverter.a11yName,
            user: energyUser.a11yName
          }
        );
      }
    );

    // convenience variable
    var layoutBounds = this.layoutBounds;

    // Create the model-view transform.  The primary units used in the model are meters, so significant zoom is used.
    // The multipliers for the 2nd parameter can be used to adjust where the point (0, 0) in the model, which is on the
    // middle of the screen above the counter as located in the view. Final arg is zoom factor from original Java sim -
    // smaller zooms out, larger zooms in.
    var mvtOriginX = Math.round( layoutBounds.width * 0.5 );
    var mvtOriginY = Math.round( layoutBounds.height * 0.475 );
    var modelViewTransform = ModelViewTransform2.createSinglePointScaleInvertedYMapping(
      Vector2.ZERO, new Vector2( mvtOriginX, mvtOriginY ), 2200
    );

    // layer node for back-most layer
    var backLayer = new Node();
    this.addChild( backLayer );

    // create the checkbox that controls the visibility of the energy chunks
    // The EnergyChunk that is created in here is not going to be used in the simulation, it is only needed in the
    // EnergyChunkNode that is displayed in the show/hide energy chunks toggle.
    var showEnergyChunksCheckbox = new Checkbox(
      new LayoutBox( {
        children: [
          new Text( energySymbolsString, { font: new PhetFont( 20 ) } ),
          new EnergyChunkNode(
            new EnergyChunk( EnergyType.THERMAL, Vector2.ZERO, Vector2.ZERO, new Property( true ) ),
            modelViewTransform
          )
        ],
        orientation: 'horizontal',
        spacing: 5
      } ),
      model.energyChunksVisibleProperty
    );

    // add the checkbox that controls the visibility of the energy chunks to a panel and then add it to the scene graph
    var showEnergyChunksPanel = new Panel( showEnergyChunksCheckbox, {
      fill: EFACConstants.CONTROL_PANEL_BACKGROUND_COLOR,
      stroke: EFACConstants.CONTROL_PANEL_OUTLINE_STROKE,
      lineWidth: EFACConstants.CONTROL_PANEL_OUTLINE_LINE_WIDTH,
      right: layoutBounds.maxX - EDGE_INSET,
      top: EDGE_INSET
    } );
    self.addChild( showEnergyChunksPanel );

    // add the energy chunk legend
    var energyChunkLegend = new EnergyChunkLegend( modelViewTransform,
      {
        right: layoutBounds.maxX - EDGE_INSET,
        top: showEnergyChunksPanel.bottom + 10
      } );
    this.addChild( energyChunkLegend );

    // only show the energy chunk legend when energy chunks are visible
    model.energyChunksVisibleProperty.linkAttribute( energyChunkLegend, 'visible' );

    // create a background rectangle at the bottom of the screen where the play/pause controls will reside
    var bottomPanel = new Rectangle(
      0,
      0,
      layoutBounds.width * 2, // wide enough that users are unlikely to see the edge
      layoutBounds.height, // tall enough that users are unlikely to see the bottom
      {
        centerX: layoutBounds.centerX,
        top: layoutBounds.maxY - BOTTOM_CONTROL_PANEL_HEIGHT,
        fill: EFACConstants.CLOCK_CONTROL_BACKGROUND_COLOR
      }
    );
    backLayer.addChild( bottomPanel );

    // add the reset all button
    var resetAllButton = new ResetAllButton( {
      listener: function() {
        model.reset();
      },
      radius: EFACConstants.RESET_ALL_BUTTON_RADIUS,
      right: layoutBounds.maxX - EDGE_INSET,
      centerY: ( bottomPanel.top + layoutBounds.maxY ) / 2
    } );
    self.addChild( resetAllButton );

    // create the energy user nodes
    this.beakerHeaterNode = new BeakerHeaterNode(
      model.beakerHeater,
      model.energyChunksVisibleProperty,
      modelViewTransform
    );
    var incandescentBulbNode = new LightBulbNode(
      model.incandescentBulb,
      model.energyChunksVisibleProperty,
      modelViewTransform,
      { bulbType: 'incandescent' }
    );
    var fluorescentBulbNode = new LightBulbNode(
      model.fluorescentBulb,
      model.energyChunksVisibleProperty,
      modelViewTransform,
      { bulbType: 'fluorescent' }
    );
    var fanNode = new FanNode(
      model.fan,
      model.energyChunksVisibleProperty,
      modelViewTransform
    );
    this.addChild( this.beakerHeaterNode );
    this.addChild( incandescentBulbNode );
    this.addChild( fluorescentBulbNode );
    this.addChild( fanNode );

    // create the energy converter nodes
    var generatorNode = new GeneratorNode( model.generator, modelViewTransform );
    var beltNode = new BeltNode( model.belt, modelViewTransform );
    var solarPanelNode = new SolarPanelNode( model.solarPanel, modelViewTransform );
    this.addChild( generatorNode );
    this.addChild( beltNode );
    this.addChild( solarPanelNode );

    // create the energy source nodes
    this.faucetNode = new FaucetAndWaterNode( model.faucet, model.energyChunksVisibleProperty, modelViewTransform );
    var sunNode = new SunNode( model.sun, model.energyChunksVisibleProperty, modelViewTransform );
    this.teaKettleNode = new TeaKettleNode( model.teaKettle, model.energyChunksVisibleProperty, modelViewTransform );
    var bikerNode = new BikerNode( model.biker, model.energyChunksVisibleProperty, modelViewTransform );
    this.addChild( sunNode );
    this.addChild( this.faucetNode );
    this.addChild( bikerNode );
    this.addChild( this.teaKettleNode );

    // add the energy system element selectors, which are sets of radio buttons
    var energySourceSelector = new EnergySystemElementSelector( model.energySourcesCarousel, {
      left: EDGE_INSET,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    var energyConverterSelector = new EnergySystemElementSelector( model.energyConvertersCarousel, {
      left: energySourceSelector.right + SELECTOR_SPACING,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    var energyUserSelector = new EnergySystemElementSelector( model.energyUsersCarousel, {
      left: energyConverterSelector.right + SELECTOR_SPACING,
      bottom: bottomPanel.top - EDGE_INSET
    } );
    this.addChild( energySourceSelector );
    this.addChild( energyConverterSelector );
    this.addChild( energyUserSelector );
  }

  energyFormsAndChanges.register( 'EnergySystemsScreenView', EnergySystemsScreenView );

  return inherit( ScreenView, EnergySystemsScreenView, {

    /**
     * step this view element, called by the framework
     * @param dt - time step, in seconds
     * @public
     */
    step: function( dt ) {
      this.teaKettleNode.steamNode.step( dt );
      this.beakerHeaterNode.step( dt );
      this.faucetNode.step( dt );
    }
  } );
} );

<|endoftext|>