<commit_before>define(
	["Ti/_", "Ti/_/Evented", "Ti/_/lang", "Ti/_/ready", "Ti/_/style", "Ti/_/dom"],
	function(_, Evented, lang, ready, style, dom) {

	var global = window,
		body = document.body,
		on = require.on,
		modules = "2DMatrix,ActivityIndicator,AlertDialog,Animation,Button,EmailDialog,ImageView,Label,OptionDialog,Picker,PickerColumn,PickerRow,ProgressBar,ScrollableView,ScrollView,Slider,Switch,Tab,TabGroup,TableView,TableViewRow,TableViewSection,TextArea,TextField,View,WebView,Window",
		creators = {},
		setStyle = style.set,
		handheld = navigator.userAgent.toLowerCase().match(/(iphone|android)/),
		iphone = handheld && handheld[0] === "iphone",
		targetHeight = {},
		hidingAddressBar,
		hideAddressBar = finishAddressBar = function() {
			Ti.UI._recalculateLayout();
			hidingAddressBar = 0;
		},
		unitize = dom.unitize,
		showStats = false;

	on(body, "touchmove", function(e) {
		e.preventDefault();
	});

	require.each(modules.split(','), function(name) {
		creators['create' + name] = function(args) {
			return new (require("Ti/UI/" + name))(args);
		};
	});

	if (!navigator.standalone && handheld) {
		hideAddressBar = function() {
			if (!hidingAddressBar) {
				hidingAddressBar = 1;
				var isPortrait = require("Ti/Gesture").isPortrait | 0,
					h = targetHeight[isPortrait],
					timer;

				if (!h) {
					if (iphone) {
						h = global.innerHeight + 60;
						if (global.screen.availHeight - h > 50) {
							h += 50;
						}
					} else {
						h = global.outerHeight / (global.devicePixelRatio || 0);
					}
					targetHeight[isPortrait] = h;
				}

				setStyle(body, "height", h + "px");

				if (iphone) {
					global.scrollTo(0, 0);
					finishAddressBar();
				} else {
					timer = setInterval(function() {
						global.scrollTo(0, -1);
						if (global.innerHeight + 1 >= h) {
							clearTimeout(timer);
							finishAddressBar();
						}
					}, 50);
				}
			}
		}
		ready(hideAddressBar);
		on(global, "orientationchange", hideAddressBar);
		on(global, "touchstart", hideAddressBar);
	}

	ready(10, function() {
		var splashScreen = document.getElementById("splash"),
			container = (Ti.UI._container = Ti.UI.createView({
				left: 0,
				top: 0
			})),
			node = container.domNode,
			coefficients = container._layoutCoefficients;
		coefficients.width.x1 = 1;
		coefficients.height.x1 = 1;

		node.id = "TiUIContainer";
		setStyle(node, "overflow", "hidden");
		body.appendChild(node);
		container.addEventListener("postlayout", function(){
			setTimeout(function(){
				setStyle(splashScreen,{
					position: "absolute",
					width: unitize(container._measuredWidth),
					height: unitize(container._measuredHeight),
					left: "0px",
					top: "0px",
					right: "",
					bottom: ""
				});
			},10);
		});
		hideAddressBar();
	});

	function updateOrientation() {
		Ti.UI._recalculateLayout();
		require("Ti/Gesture")._updateOrientation();
	}
	on(global, "resize", updateOrientation);
	on(global, "orientationchange", updateOrientation);

	return lang.setObject("Ti.UI", Evented, creators, {

		_addWindow: function(win, set) {
			this._container.add(win.modal ? win._modalParentContainer : win);
			set && this._setWindow(win);
			return win;
		},

		_setWindow: function(win) {
			this.constants.currentWindow = win;
		},

		_removeWindow: function(win) {
			this._container.remove(win.modal ? win._modalParentContainer : win);
			return win;
		},

		_layoutSemaphore: 0,

		_nodesToLayout: [],

		_startLayout: function() {
			this._layoutSemaphore++;
		},

		_finishLayout: function() {
			this._layoutSemaphore--;
			if (this._layoutSemaphore === 0) {
				this._triggerLayout(true);
			}
		},

		_elementLayoutCount: 0,

		_layoutCount: 0,

		_triggerLayout: function(node, force) {
			var self = this;
			if (~self._nodesToLayout.indexOf(node)) {
				return;
			}
			self._nodesToLayout.push(node);
			function startLayout() {

				self._elementLayoutCount = 0;
				self._layoutCount++;
				var startTime = (new Date()).getTime(),
					nodes = self._nodesToLayout,
					layoutNode,
					node,
					parent,
					previousParent,
					children,
					child,
					recursionStack,
					rootNodesToLayout = [],
					layoutRootNode = false,
					breakAfterChildrenCalculations,
					container = self._container;

				// Determine which nodes need to be re-layed out
				for (var i in nodes) {
					layoutNode = nodes[i];

					// Mark all of the children for update that need to be updated
					recursionStack = [layoutNode];
					while (recursionStack.length > 0) {
						node = recursionStack.pop();
						node._markedForLayout = true;
						children = node.children;
						for (var j in children) {
							child = children[j];
							if (node.layout !== "composite" || child._needsMeasuring || node._layout._isDependentOnParent(child)) {
								recursionStack.push(child);
							}
						}
					}

					// Go up and mark any other nodes that need to be marked
					parent = layoutNode;
					if (layoutNode === container) {
						layoutRootNode = true;
					} else {
						while(1) {

							parent._markedForLayout = true;
							previousParent = parent;
							parent = parent._parent;

							// Check if this parent is the stopping point
							breakAfterChildrenCalculations = false;
							if (parent === container) {
								layoutRootNode = true;
								break;
							} else if(!parent._hasSizeDimensions() && !parent._needsMeasuring) {
								!parent._markedForLayout && !~rootNodesToLayout.indexOf(parent) && rootNodesToLayout.push(parent);
								breakAfterChildrenCalculations = true;
							}

							// Recurse through the children of the parent
							recursionStack = [parent];
							while (recursionStack.length > 0) {
								node = recursionStack.pop();
								children = node.children;
								for (var j in children) {
									child = children[j];
									if (child !== previousParent && (node.layout !== "composite" || child._needsMeasuring || node._layout._isDependentOnParent(child))) {
										child._markedForLayout = true;
										recursionStack.push(child);
									}
								}
							}

							if (breakAfterChildrenCalculations) {
								break;
							}
						}
					}
				}

				// Layout all nodes that need it
				if (layoutRootNode) {
					var container = self._container;
					setStyle(self._container.domNode, {
						width: container.width + "px",
						height: container.height + "px",
					});
					container._layout._doLayout(container, container.width, container.height, false, false);
				}
				for (var i in rootNodesToLayout) {
					node = rootNodesToLayout[i];
					node._layout._doLayout(node, node._measuredWidth, node._measuredHeight, node._parent._layout._getWidth(node) === Ti.UI.SIZE, node._parent._layout._getHeight(node) === Ti.UI.SIZE);
				}

				showStats && console.debug("Layout " + self._layoutCount + ": " + self._elementLayoutCount +
					" elements (out of " + document.getElementById("TiUIContainer").getElementsByTagName("*").length + " total) laid out in " + ((new Date().getTime() - startTime)) + "ms");

				self._layoutInProgress = false;
				self._layoutTimer = null;
				self._nodesToLayout = [];

				self.fireEvent("postlayout");
			}
			if (force) {
				clearTimeout(self._layoutTimer);
				self._layoutInProgress = true;
				startLayout();
			} else if (self._nodesToLayout.length === 1) {
				self._layoutInProgress = true;
				self._layoutTimer = setTimeout(function(){ startLayout(); }, 10);
			}
		},

		_recalculateLayout: function() {
			var container = this._container;
			if (container) {
				container.width = global.innerWidth;
				container.height = global.innerHeight;
			}
		},

		properties: {
			backgroundColor: {
				set: function(value) {
					return this._container.backgroundColor = value;
				}
			},
			backgroundImage: {
				set: function(value) {
					return setStyle(body, "backgroundImage", value ? style.url(value) : "");
				}
			},
			currentTab: void 0
		},

		convertUnits: function(convertFromValue, convertToUnits) {
			var intermediary = dom.computeSize(convertFromValue, 0, false);
			switch(convertToUnits) {
				case Ti.UI.UNIT_MM:
					intermediary *= 10;
				case Ti.UI.UNIT_CM:
					return intermediary / ( 0.0393700787 * _.dpi * 10);
				case Ti.UI.UNIT_IN:
					return intermediary / _.dpi;
				case Ti.UI.UNIT_DIP:
					return intermediary * 96 / _.dpi;
				case Ti.UI.UNIT_PX:
					return intermediary;
				default: return 0;
			}
		},

		constants: {
			currentWindow: void 0,
			UNKNOWN: 0,
			FACE_DOWN: 1,
			FACE_UP: 2,
			PORTRAIT: 3,
			UPSIDE_PORTRAIT: 4,
			LANDSCAPE_LEFT: 5,
			LANDSCAPE_RIGHT: 6,
			INPUT_BORDERSTYLE_NONE: 0, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_LINE: 1, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_BEZEL: 2, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_ROUNDED: 3, // DO NOT CHANGE! Values are referenced directly in code
			KEYBOARD_DEFAULT: 2,
			KEYBOARD_EMAIL: 3,
			KEYBOARD_NUMBER_PAD: 6,
			KEYBOARD_PHONE_PAD: 7,
			KEYBOARD_URL: 8,
			NOTIFICATION_DURATION_LONG: 1,
			NOTIFICATION_DURATION_SHORT: 2,
			PICKER_TYPE_DATE: 2,
			PICKER_TYPE_DATE_AND_TIME: 3,
			PICKER_TYPE_PLAIN: 4,
			PICKER_TYPE_TIME: 5,
			RETURNKEY_DEFAULT: 0, // return
			RETURNKEY_DONE: 1, // Done
			RETURNKEY_EMERGENCY_CALL: 2, // Emergency Call
			RETURNKEY_GO: 3, // Go
			RETURNKEY_GOOGLE: 4, // Search
			RETURNKEY_JOIN: 5, // Join
			RETURNKEY_NEXT: 6, // Next
			RETURNKEY_ROUTE: 7, // Route
			RETURNKEY_SEARCH: 8, // Search
			RETURNKEY_SEND: 9, // Send
			RETURNKEY_YAHOO: 10, // Search
			TEXT_ALIGNMENT_CENTER: "center",
			TEXT_ALIGNMENT_RIGHT: "right",
			TEXT_ALIGNMENT_LEFT: "left",
			TEXT_AUTOCAPITALIZATION_ALL: 3,
			TEXT_AUTOCAPITALIZATION_NONE: 0,
			TEXT_AUTOCAPITALIZATION_SENTENCES: 2,
			TEXT_AUTOCAPITALIZATION_WORDS: 1,
			TEXT_VERTICAL_ALIGNMENT_BOTTOM: "bottom",
			TEXT_VERTICAL_ALIGNMENT_CENTER: "center",
			TEXT_VERTICAL_ALIGNMENT_TOP: "top",
			ANIMATION_CURVE_EASE_IN: 1,
			ANIMATION_CURVE_EASE_IN_OUT: 2,
			ANIMATION_CURVE_EASE_OUT: 3,
			ANIMATION_CURVE_LINEAR: 4,
			SIZE: "auto",
			FILL: "fill",
			INHERIT: "inherit",
			UNIT_PX: "px",
			UNIT_MM: "mm",
			UNIT_CM: "cm",
			UNIT_IN: "in",
			UNIT_DIP: "dp", // We don't have DIPs, so we treat them as pixels

			// Hidden constants
			_LAYOUT_COMPOSITE: "composite",
			_LAYOUT_VERTICAL: "vertical",
			_LAYOUT_HORIZONTAL: "horizontal",
			_LAYOUT_CONSTRAINING_VERTICAL: "constrainingVertical",
			_LAYOUT_CONSTRAINING_HORIZONTAL: "constrainingHorizontal",
		}

	});

});<commit_msg>[TIMOB-8275] Bug fix.<commit_after>define(
	["Ti/_", "Ti/_/Evented", "Ti/_/lang", "Ti/_/ready", "Ti/_/style", "Ti/_/dom"],
	function(_, Evented, lang, ready, style, dom) {

	var global = window,
		body = document.body,
		on = require.on,
		modules = "2DMatrix,ActivityIndicator,AlertDialog,Animation,Button,EmailDialog,ImageView,Label,OptionDialog,Picker,PickerColumn,PickerRow,ProgressBar,ScrollableView,ScrollView,Slider,Switch,Tab,TabGroup,TableView,TableViewRow,TableViewSection,TextArea,TextField,View,WebView,Window",
		creators = {},
		setStyle = style.set,
		handheld = navigator.userAgent.toLowerCase().match(/(iphone|android)/),
		iphone = handheld && handheld[0] === "iphone",
		targetHeight = {},
		hidingAddressBar,
		hideAddressBar = finishAddressBar = function() {
			Ti.UI._recalculateLayout();
			hidingAddressBar = 0;
		},
		unitize = dom.unitize,
		showStats = false;

	on(body, "touchmove", function(e) {
		e.preventDefault();
	});

	require.each(modules.split(','), function(name) {
		creators['create' + name] = function(args) {
			return new (require("Ti/UI/" + name))(args);
		};
	});

	if (!navigator.standalone && handheld) {
		hideAddressBar = function() {
			if (!hidingAddressBar) {
				hidingAddressBar = 1;
				var isPortrait = require("Ti/Gesture").isPortrait | 0,
					h = targetHeight[isPortrait],
					timer;

				if (!h) {
					if (iphone) {
						h = global.innerHeight + 60;
						if (global.screen.availHeight - h > 50) {
							h += 50;
						}
					} else {
						h = global.outerHeight / (global.devicePixelRatio || 0);
					}
					targetHeight[isPortrait] = h;
				}

				setStyle(body, "height", h + "px");

				if (iphone) {
					global.scrollTo(0, 0);
					finishAddressBar();
				} else {
					timer = setInterval(function() {
						global.scrollTo(0, -1);
						if (global.innerHeight + 1 >= h) {
							clearTimeout(timer);
							finishAddressBar();
						}
					}, 50);
				}
			}
		}
		ready(hideAddressBar);
		on(global, "orientationchange", hideAddressBar);
		on(global, "touchstart", hideAddressBar);
	}

	ready(10, function() {
		var splashScreen = document.getElementById("splash"),
			container = (Ti.UI._container = Ti.UI.createView({
				left: 0,
				top: 0
			})),
			node = container.domNode,
			coefficients = container._layoutCoefficients;
		coefficients.width.x1 = 1;
		coefficients.height.x1 = 1;

		node.id = "TiUIContainer";
		setStyle(node, "overflow", "hidden");
		body.appendChild(node);
		container.addEventListener("postlayout", function(){
			setTimeout(function(){
				setStyle(splashScreen,{
					position: "absolute",
					width: unitize(container._measuredWidth),
					height: unitize(container._measuredHeight),
					left: "0px",
					top: "0px",
					right: "",
					bottom: ""
				});
			},10);
		});
		hideAddressBar();
	});

	function updateOrientation() {
		Ti.UI._recalculateLayout();
		require("Ti/Gesture")._updateOrientation();
	}
	on(global, "resize", updateOrientation);
	on(global, "orientationchange", updateOrientation);

	return lang.setObject("Ti.UI", Evented, creators, {

		_addWindow: function(win, set) {
			this._container.add(win.modal ? win._modalParentContainer : win);
			set && this._setWindow(win);
			return win;
		},

		_setWindow: function(win) {
			this.constants.currentWindow = win;
		},

		_removeWindow: function(win) {
			this._container.remove(win.modal ? win._modalParentContainer : win);
			return win;
		},

		_layoutSemaphore: 0,

		_nodesToLayout: [],

		_startLayout: function() {
			this._layoutSemaphore++;
		},

		_finishLayout: function() {
			this._layoutSemaphore--;
			if (this._layoutSemaphore === 0) {
				this._triggerLayout(true);
			}
		},

		_elementLayoutCount: 0,

		_layoutCount: 0,

		_triggerLayout: function(node, force) {
			var self = this;
			if (~self._nodesToLayout.indexOf(node)) {
				return;
			}
			self._nodesToLayout.push(node);
			function startLayout() {

				self._elementLayoutCount = 0;
				self._layoutCount++;
				var startTime = (new Date()).getTime(),
					nodes = self._nodesToLayout,
					layoutNode,
					node,
					parent,
					previousParent,
					children,
					child,
					recursionStack,
					rootNodesToLayout = [],
					layoutRootNode = false,
					breakAfterChildrenCalculations,
					container = self._container;

				// Determine which nodes need to be re-layed out
				for (var i in nodes) {
					layoutNode = nodes[i];

					// Mark all of the children for update that need to be updated
					recursionStack = [layoutNode];
					while (recursionStack.length > 0) {
						node = recursionStack.pop();
						node._markedForLayout = true;
						children = node.children;
						for (var j in children) {
							child = children[j];
							if (node.layout !== "composite" || child._needsMeasuring || node._layout._isDependentOnParent(child)) {
								recursionStack.push(child);
							}
						}
					}

					// Go up and mark any other nodes that need to be marked
					parent = layoutNode;
					if (layoutNode === container) {
						layoutRootNode = true;
					} else {
						while(1) {

							parent._markedForLayout = true;
							previousParent = parent;
							parent = parent._parent;

							// Check if this parent is the stopping point
							breakAfterChildrenCalculations = false;
							if (!parent || parent === container) {
								layoutRootNode = true;
								break;
							} else if(!parent._hasSizeDimensions() && !parent._needsMeasuring) {
								!parent._markedForLayout && !~rootNodesToLayout.indexOf(parent) && rootNodesToLayout.push(parent);
								breakAfterChildrenCalculations = true;
							}

							// Recurse through the children of the parent
							recursionStack = [parent];
							while (recursionStack.length > 0) {
								node = recursionStack.pop();
								children = node.children;
								for (var j in children) {
									child = children[j];
									if (child !== previousParent && (node.layout !== "composite" || child._needsMeasuring || node._layout._isDependentOnParent(child))) {
										child._markedForLayout = true;
										recursionStack.push(child);
									}
								}
							}

							if (breakAfterChildrenCalculations) {
								break;
							}
						}
					}
				}

				// Layout all nodes that need it
				if (layoutRootNode) {
					var container = self._container;
					setStyle(self._container.domNode, {
						width: container.width + "px",
						height: container.height + "px",
					});
					container._layout._doLayout(container, container.width, container.height, false, false);
				}
				for (var i in rootNodesToLayout) {
					node = rootNodesToLayout[i];
					node._layout._doLayout(node, node._measuredWidth, node._measuredHeight, node._parent._layout._getWidth(node) === Ti.UI.SIZE, node._parent._layout._getHeight(node) === Ti.UI.SIZE);
				}

				showStats && console.debug("Layout " + self._layoutCount + ": " + self._elementLayoutCount +
					" elements (out of " + document.getElementById("TiUIContainer").getElementsByTagName("*").length + " total) laid out in " + ((new Date().getTime() - startTime)) + "ms");

				self._layoutInProgress = false;
				self._layoutTimer = null;
				self._nodesToLayout = [];

				self.fireEvent("postlayout");
			}
			if (force) {
				clearTimeout(self._layoutTimer);
				self._layoutInProgress = true;
				startLayout();
			} else if (self._nodesToLayout.length === 1) {
				self._layoutInProgress = true;
				self._layoutTimer = setTimeout(function(){ startLayout(); }, 10);
			}
		},

		_recalculateLayout: function() {
			var container = this._container;
			if (container) {
				container.width = global.innerWidth;
				container.height = global.innerHeight;
			}
		},

		properties: {
			backgroundColor: {
				set: function(value) {
					return this._container.backgroundColor = value;
				}
			},
			backgroundImage: {
				set: function(value) {
					return setStyle(body, "backgroundImage", value ? style.url(value) : "");
				}
			},
			currentTab: void 0
		},

		convertUnits: function(convertFromValue, convertToUnits) {
			var intermediary = dom.computeSize(convertFromValue, 0, false);
			switch(convertToUnits) {
				case Ti.UI.UNIT_MM:
					intermediary *= 10;
				case Ti.UI.UNIT_CM:
					return intermediary / ( 0.0393700787 * _.dpi * 10);
				case Ti.UI.UNIT_IN:
					return intermediary / _.dpi;
				case Ti.UI.UNIT_DIP:
					return intermediary * 96 / _.dpi;
				case Ti.UI.UNIT_PX:
					return intermediary;
				default: return 0;
			}
		},

		constants: {
			currentWindow: void 0,
			UNKNOWN: 0,
			FACE_DOWN: 1,
			FACE_UP: 2,
			PORTRAIT: 3,
			UPSIDE_PORTRAIT: 4,
			LANDSCAPE_LEFT: 5,
			LANDSCAPE_RIGHT: 6,
			INPUT_BORDERSTYLE_NONE: 0, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_LINE: 1, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_BEZEL: 2, // DO NOT CHANGE! Values are referenced directly in code
			INPUT_BORDERSTYLE_ROUNDED: 3, // DO NOT CHANGE! Values are referenced directly in code
			KEYBOARD_DEFAULT: 2,
			KEYBOARD_EMAIL: 3,
			KEYBOARD_NUMBER_PAD: 6,
			KEYBOARD_PHONE_PAD: 7,
			KEYBOARD_URL: 8,
			NOTIFICATION_DURATION_LONG: 1,
			NOTIFICATION_DURATION_SHORT: 2,
			PICKER_TYPE_DATE: 2,
			PICKER_TYPE_DATE_AND_TIME: 3,
			PICKER_TYPE_PLAIN: 4,
			PICKER_TYPE_TIME: 5,
			RETURNKEY_DEFAULT: 0, // return
			RETURNKEY_DONE: 1, // Done
			RETURNKEY_EMERGENCY_CALL: 2, // Emergency Call
			RETURNKEY_GO: 3, // Go
			RETURNKEY_GOOGLE: 4, // Search
			RETURNKEY_JOIN: 5, // Join
			RETURNKEY_NEXT: 6, // Next
			RETURNKEY_ROUTE: 7, // Route
			RETURNKEY_SEARCH: 8, // Search
			RETURNKEY_SEND: 9, // Send
			RETURNKEY_YAHOO: 10, // Search
			TEXT_ALIGNMENT_CENTER: "center",
			TEXT_ALIGNMENT_RIGHT: "right",
			TEXT_ALIGNMENT_LEFT: "left",
			TEXT_AUTOCAPITALIZATION_ALL: 3,
			TEXT_AUTOCAPITALIZATION_NONE: 0,
			TEXT_AUTOCAPITALIZATION_SENTENCES: 2,
			TEXT_AUTOCAPITALIZATION_WORDS: 1,
			TEXT_VERTICAL_ALIGNMENT_BOTTOM: "bottom",
			TEXT_VERTICAL_ALIGNMENT_CENTER: "center",
			TEXT_VERTICAL_ALIGNMENT_TOP: "top",
			ANIMATION_CURVE_EASE_IN: 1,
			ANIMATION_CURVE_EASE_IN_OUT: 2,
			ANIMATION_CURVE_EASE_OUT: 3,
			ANIMATION_CURVE_LINEAR: 4,
			SIZE: "auto",
			FILL: "fill",
			INHERIT: "inherit",
			UNIT_PX: "px",
			UNIT_MM: "mm",
			UNIT_CM: "cm",
			UNIT_IN: "in",
			UNIT_DIP: "dp", // We don't have DIPs, so we treat them as pixels

			// Hidden constants
			_LAYOUT_COMPOSITE: "composite",
			_LAYOUT_VERTICAL: "vertical",
			_LAYOUT_HORIZONTAL: "horizontal",
			_LAYOUT_CONSTRAINING_VERTICAL: "constrainingVertical",
			_LAYOUT_CONSTRAINING_HORIZONTAL: "constrainingHorizontal",
		}

	});

});<|endoftext|>


<commit_before>// This file belongs to Whendle, a clock application for the Palm Pre
// http://github.com/dylanmei/Whendle

//
// Copyright (C) 2009-2010 Dylan Meissner (dylanmei@gmail.com)
//
// Permission is hereby granted, free of charge, to any person otaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

Whendle.Migrator = Class.create({
	initialize: function(version, database) {
		this.version = version;
		this.database = database;
		this.jobs = [];
	},

	go: function(on_complete, on_error) {
		if (this.jobs.length == 0) {
			this._on_job_done(on_complete);
		}
		else {
			var jobs = this.jobs.clone();
			for (var i = 0; i < jobs.length; i++) {
				jobs[i](this.database);
			}
		}
	},

	queue_job: function(statement, parameters, on_complete, on_error, note) {
		var self = this;
		var job = function(db) {
			db.scalar(statement, parameters,
				self._on_job_done.bind(self, on_complete, note),
				on_error
			);
		}

		this.jobs.push(job);
	},

	_on_job_done: function(on_complete, note) {

		if (this.jobs.length != 0) {
			this.jobs.pop();
		}

		if (this.jobs.length == 0) {
			on_complete(this.version);
		}
	}
});

// When run the first time
Whendle.Migrator_0_1 = Class.create(Whendle.Migrator, {
	// as of Nov 2009
	DEFAULT_TIMEZONE: '{"name": "America/Los_Angeles", "zones": ["Zone America/Los_Angeles -7:52:58 -\tLMT\t1883 Nov 18 12:07:02", "Zone\tAmerica/Los_Angeles\t-8:00\tUS\tP%sT\t1946", "Zone\tAmerica/Los_Angeles\t-8:00\tCA\tP%sT\t1967", "Zone\tAmerica/Los_Angeles\t-8:00\tUS\tP%sT"], "rules": ["Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS", "Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW", "Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP", "Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS", "Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS", "Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD", "Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD", "Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD", "Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD", "Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS", "Rule\tCA\t1948\tonly\t-\tMar\t14\t2:00\t1:00\tD", "Rule\tCA\t1949\tonly\t-\tJan\t 1\t2:00\t0\tS", "Rule\tCA\t1950\t1966\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tCA\t1950\t1961\t-\tSep\tlastSun\t2:00\t0\tS", "Rule\tCA\t1962\t1966\t-\tOct\tlastSun\t2:00\t0\tS"]}',

	initialize: function($super, database) {
		$super('0.1', database);
	},

	go: function($super, on_complete, on_error) {

		this.queue_job('create table \'whendle\' (id integer primary key autoincrement not null, version text not null)', [],
			on_complete, on_error, 'create whendle table');
		this.queue_job('insert into \'whendle\' (version) values (\'0.1\')', [],
			on_complete, on_error, 'insert version 0.1');
		this.queue_job('create table \'places\' (id integer primary key autoincrement not null, name text not null, admin text, country text, latitude real, longitude real, timezone text, woeid integer, type integer)', [],
			on_complete, on_error, 'create places table');
		this.queue_job('insert into \'places\' (name,admin,country,latitude,longitude,timezone,woeid,type,sort) values (?,?,?,?,?,?,?,?,?)',
			['Sunnyvale', 'California', 'United States', 37.371609, -122.038254, this.DEFAULT_TIMEZONE, 2502265, 7, 0],
			on_complete, on_error, 'insert default place');
		$super(on_complete, on_error);
	}
});

Whendle.Migrator_0_2 = Class.create(Whendle.Migrator, {
	initialize: function($super, database) {
		$super('0.2', database);
	},

	go: function($super, on_complete, on_error) {
		this.queue_job('update \'whendle\' set version=?', ['0.2'],
			on_complete, on_error, 'update whendle table');
		this.queue_job('alter table \'places\' add \'sort\' integer default 0 not null', [],
			on_complete, on_error, 'alter places table');
		$super(on_complete, on_error);
	}
});
<commit_msg>sort column is not included in 0.1 schema migrator<commit_after>// This file belongs to Whendle, a clock application for the Palm Pre
// http://github.com/dylanmei/Whendle

//
// Copyright (C) 2009-2010 Dylan Meissner (dylanmei@gmail.com)
//
// Permission is hereby granted, free of charge, to any person otaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

Whendle.Migrator = Class.create({
	initialize: function(version, database) {
		this.version = version;
		this.database = database;
		this.jobs = [];
	},

	go: function(on_complete, on_error) {
		if (this.jobs.length == 0) {
			this._on_job_done(on_complete);
		}
		else {
			var jobs = this.jobs.clone();
			for (var i = 0; i < jobs.length; i++) {
				jobs[i](this.database);
			}
		}
	},

	queue_job: function(statement, parameters, on_complete, on_error, note) {
		var self = this;
		var job = function(db) {
			db.scalar(statement, parameters,
				self._on_job_done.bind(self, on_complete, note),
				on_error
			);
		}

		this.jobs.push(job);
	},

	_on_job_done: function(on_complete, note) {
		//$.trace(this.version, note);
		if (this.jobs.length != 0) {
			this.jobs.pop();
		}

		if (this.jobs.length == 0) {
			on_complete(this.version);
		}
	}
});

// When run the first time
Whendle.Migrator_0_1 = Class.create(Whendle.Migrator, {
	// as of Nov 2009
	DEFAULT_TIMEZONE: '{"name": "America/Los_Angeles", "zones": ["Zone America/Los_Angeles -7:52:58 -\tLMT\t1883 Nov 18 12:07:02", "Zone\tAmerica/Los_Angeles\t-8:00\tUS\tP%sT\t1946", "Zone\tAmerica/Los_Angeles\t-8:00\tCA\tP%sT\t1967", "Zone\tAmerica/Los_Angeles\t-8:00\tUS\tP%sT"], "rules": ["Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS", "Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW", "Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP", "Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS", "Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS", "Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD", "Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD", "Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD", "Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD", "Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS", "Rule\tCA\t1948\tonly\t-\tMar\t14\t2:00\t1:00\tD", "Rule\tCA\t1949\tonly\t-\tJan\t 1\t2:00\t0\tS", "Rule\tCA\t1950\t1966\t-\tApr\tlastSun\t2:00\t1:00\tD", "Rule\tCA\t1950\t1961\t-\tSep\tlastSun\t2:00\t0\tS", "Rule\tCA\t1962\t1966\t-\tOct\tlastSun\t2:00\t0\tS"]}',

	initialize: function($super, database) {
		$super('0.1', database);
	},

	go: function($super, on_complete, on_error) {
		this.queue_job('create table \'whendle\' (id integer primary key autoincrement not null, version text not null)', [],
			on_complete, on_error, 'create whendle table');
		this.queue_job('insert into \'whendle\' (version) values (\'0.1\')', [],
			on_complete, on_error, 'insert version 0.1');
		this.queue_job('create table \'places\' (id integer primary key autoincrement not null, name text not null, admin text, country text, latitude real, longitude real, timezone text, woeid integer, type integer)', [],
			on_complete, on_error, 'create places table');
		this.queue_job('insert into \'places\' (name,admin,country,latitude,longitude,timezone,woeid,type) values (?,?,?,?,?,?,?,?)',
			['Sunnyvale', 'California', 'United States', 37.371609, -122.038254, this.DEFAULT_TIMEZONE, 2502265, 7],
			on_complete, on_error, 'insert default place');
		$super(on_complete, on_error);
	}
});

Whendle.Migrator_0_2 = Class.create(Whendle.Migrator, {
	initialize: function($super, database) {
		$super('0.2', database);
	},

	go: function($super, on_complete, on_error) {
		this.queue_job('update \'whendle\' set version=?', ['0.2'],
			on_complete, on_error, 'update whendle table');
		this.queue_job('alter table \'places\' add \'sort\' integer default 0 not null', [],
			on_complete, on_error, 'alter places table');
		$super(on_complete, on_error);
	}
});
<|endoftext|>


<commit_before>var Bricoleur = require('./_brico')
, map = require('./_map')
, rtr = require('./_rtr')
, usr = require('./_usr')
, http = require('http')
, fs = require('fs')
, ws = require('ws').Server

var port = 80 // set port

var _usr = new usr() // setup user
var _map = new map({dir:'./_wilds', watch:true, css: './_wilds/_css.styl', js:['./__clnt.js']}) // map _wilds modules

_usr.buildUsers(function (user) { // fix this
   Object[user.host] = new Bricoleur(user) // not too sure about this prob a temp hack
   _map.out.pipe(Object[user.host].in)
})

_map.survey(function (err) {
 console.log('map survey complete')
})

var _rtr = new rtr() // do routing
var server = http.createServer(_rtr.handleReqs) // handle http requests
server.listen(port)

var wss = new ws({server:server})
wss.on('connection', _rtr.handleSoc)
<commit_msg>call autoUpdate fn in __srvr<commit_after>var Bricoleur = require('./_brico')
, map = require('./_map')
, rtr = require('./_rtr')
, usr = require('./_usr')
, http = require('http')
, fs = require('fs')
, ws = require('ws').Server

var port = 80 // set port

var _usr = new usr() // setup user
var _map = new map({dir:'./_wilds', watch:true, css: './_wilds/_css.styl', js:['./__clnt.js']}) // map _wilds modules

_usr.buildUsers(function (user) { // fix this
   Object[user.host] = new Bricoleur(user) // not too sure about this prob a temp hack
   _map.out.pipe(Object[user.host].in)
})

_map.survey(function (err) {
 console.log('map survey complete')
})

_map.autoUpdate(function (res) {
  console.log(res)
  ws.emit('data', JSON.stringify({event:'clear'}))
})

var _rtr = new rtr() // do routing
var server = http.createServer(_rtr.handleReqs) // handle http requests
server.listen(port)

var wss = new ws({server:server})
wss.on('connection', _rtr.handleSoc)
<|endoftext|>


<commit_before>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

import Loggers from './Loggers';

const logger = Loggers.create(module.filename, 'info');

export async function mapAsync(array, fn) {
   return await* array.map(async (item) => {
      try {
         let result = await fn();
         logger.info('mapAsync', result);
         return result;
      } catch (err) {
         logger.warn('mapAsync', err);
         throw err;
      }
   });
}

export async function reverse(array) {
   if (lodash.isArray(array)) {
      let result = array.slice(0);
      result.reverse();
      return result;
   }
}
<commit_msg>arrays<commit_after>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

import Loggers from './Loggers';

const logger = Loggers.create(module.filename, 'info');

export async function reverse(array) {
   if (lodash.isArray(array)) {
      let result = array.slice(0);
      lodash.reverse(result);
      return result;
   } else {
      logger.warn('reverse empty');
      return null;
   }
}

export async function mapAsync(array, fn) {
   return await* array.map(async (item) => {
      try {
         let result = await fn();
         logger.info('mapAsync', result);
         return result;
      } catch (err) {
         logger.warn('mapAsync', err);
         throw err;
      }
   });
}
<|endoftext|>


<commit_before>import { readAsArrayBuffer } from 'pouchdb-binary-utils';

export function bufferToHexString(buffer) {
    const hexCodes = [];
    const view = new DataView(buffer);

    for (let i = 0; i < view.byteLength; i += 4) {
        // Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
        const value = view.getUint32(i);
        // toString(16) will give the hex representation of the number without padding
        // We use concatenation and slice for padding
        hexCodes.push(`00000000${value.toString(16)}`.slice(-8));
    }

    // Join all the hex strings into one
    return hexCodes.join('');
}

export function blobToArrayBuffer(blob) {
    return new Promise(resolve => readAsArrayBuffer(blob, resolve));
}

export function deepAssign(to, ...rest) {
    for (let src of rest) {
        for (let prop in src) {
            const value = src[prop];
            if (typeof value === 'object' && !Array.isArray(value)) {
                to[prop] = deepAssign(to[prop] || {}, value);
            } else {
                to[prop] = value;
            }
        }
    }
    return to;
}
<commit_msg>Allow deepAssign to delete props.<commit_after>import { readAsArrayBuffer } from 'pouchdb-binary-utils';

export function bufferToHexString(buffer) {
    const hexCodes = [];
    const view = new DataView(buffer);

    for (let i = 0; i < view.byteLength; i += 4) {
        // Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
        const value = view.getUint32(i);
        // toString(16) will give the hex representation of the number without padding
        // We use concatenation and slice for padding
        hexCodes.push(`00000000${value.toString(16)}`.slice(-8));
    }

    // Join all the hex strings into one
    return hexCodes.join('');
}

export function blobToArrayBuffer(blob) {
    return new Promise(resolve => readAsArrayBuffer(blob, resolve));
}

export function deepAssign(to, ...rest) {
    for (let src of rest) {
        for (let prop in src) {
            const value = src[prop];
            if (typeof value === 'object' && !Array.isArray(value)) {
                to[prop] = deepAssign(to[prop] || {}, value);
            } else if (value === undefined && to[prop] !== undefined){
                delete to[prop];
            } else {
                to[prop] = value;
            }
        }
    }
    return to;
}
<|endoftext|>


<commit_before>angular
    .module('app')
    .controller('userEditController', [
        '$rootScope',
        '$scope',
        '$location',
        '$routeParams',
        'authService',
        'userService',
        'vendorService',
        'FormHelper',
        function($rootScope, $scope, $location, $routeParams, Auth, User, Vendor, FormHelper) {

            $scope.modelObject = User;

            Auth.canUserDoAction('edit-users');

            // Options you can set user roles
            $scope.roles = [{
                value: 'salesRep',
                label: 'Sales Rep'
            }, {
                value: 'vendorRep',
                label: 'Vendor Rep'
            }, {
                value: 'admin',
                label: 'Admin'
            }];

            $scope.canChangeRole = function() {
                if ($scope.user._id === Auth.getCurrentUser()._id && $scope.user.role === 'admin') {
                    return false;
                } else {
                    return true;
                }
            };

            $scope.canDeleteUser = function() {
                if ($scope.user._id !== Auth.getCurrentUser()._id) {
                    return true;
                } else {
                    return false;
                }
            };

            // empty user object
            $scope.user = {};
            var user = {};

            // filepicker settings
            // @todo move to global config
            filepicker.setKey('AJNc7mfA3SCxs3gRjg7EBz');

            // pick logo function
            // simple callback assigans to user logo when complete
            $scope.pickImage = function() {
                filepicker.pick(function(FPFile) {
                    console.log(FPFile.url);
                    if (!$scope.user.avatar) {
                        $scope.user.avatar = {};
                    }
                    $scope.user.avatar.original = FPFile.url;
                    $scope.$apply();
                });
            };

            // utility function to go back to the user list
            // @todo this function is used in many places, find a way to streamline it
            $scope.cancel = function() {
                $location.url('/dashboard/users');
            };

            // get user ID for edit pages
            var userId = $routeParams.id;
            $scope.formAction = 'Add';

            // get and store the user
            if (userId) {
                // get the user
                User.getById(userId).then(function(response) {
                    $scope.user = response;

                    $scope.initialRole = $scope.user.role;

                    // get vendors for this user
                    // @todo this will now save when we udate the vendors, so we need to fix this!
                    refreshVendors();
                });
                //console.log($scope.user);
                $scope.formAction = 'Update';
            }

            function updateVendorRelationships() {
                // process each program, checking if its active for the vendor
                _.each($scope.vendors, function(item, key) {

                    // API saves an array of _ids
                    if (item.active) {

                        // check if the user is currently the sales or vendor rep for this vendor
                        if (item.salesRep && item.salesRep._id == $scope.user._id) item.salesRep = null;
                        if (item.vendorRep && item.vendorRep._id == $scope.user._id) item.vendorRep = null;

                        // now set their proper role
                        item[$scope.user.role] = $scope.user._id;
                        console.log(item);

                        Vendor.update(item);

                        //Vendor.update();
                    }

                });
            }

            var formTabMap = [
                'basicForm',
                'usersVendors',
                'passwordForm'
            ];

            // activated when user clicks the save button
            $scope.save = function(doRedirect) {

                if (!userId) {

                    // create new item
                    User.add($scope.user).then(function(response) {
                        $scope.user = response;
                        // this ensures that on the next save, vendorId is set and the previous if() doesnt run
                        userId = $scope.user._id;
                        //saveChangesPrompt.removeListener();

                        if (doRedirect) {
                            $location.url('/dashboard/users');
                        } else {
                            FormHelper.setPristine($scope.$$childTail[formTabMap[$scope.activeTab]]);
                        }

                    });

                } else {

                    if ($scope.initialRole !== $scope.user.role) {

                        console.log('NEED TO update users vendors');

                        if (confirm('Changing a users role will remove all their vendor associations. Are you sure you wish to continue?')) {

                            _.each($scope.vendors, function(item, key) {
                                // check if the user is currently the sales or vendor rep for this vendor

                                if (item.active) {
                                    if (item.salesRep && item.salesRep._id == $scope.user._id) item.salesRep = null;
                                    if (item.vendorRep && item.vendorRep._id == $scope.user._id) item.vendorRep = null;
                                    item.active = false;
                                }

                                console.log(item);
                                Vendor.update(item);
                            });

                            updateVendorRelationships();

                            // update existing item
                            User.update($scope.user);

                            //saveChangesPrompt.removeListener();

                            if (doRedirect) {
                                $location.url('/dashboard/users');
                            } else {
                                FormHelper.setPristine($scope.$$childTail[formTabMap[$scope.activeTab]]);
                            }

                        }

                    } else {
                        updateVendorRelationships();

                        // update existing item
                        User.update($scope.user);

                        //saveChangesPrompt.removeListener();

                        if (doRedirect) {
                            $location.url('/dashboard/users');
                        } else {
                            FormHelper.setPristine($scope.$$childTail[formTabMap[$scope.activeTab]]);
                        }
                    }

                }

            };

            // --------

            /**
             * Gets all the programs, making two calls and merging the results
             * In our view we sort and filter so the active programs appear first
             *
             */
            $scope.vendors = [];

            function refreshVendors() {

                User.getUsersNonVendors($scope.user._id).then(function(response) {
                    $scope.vendors = $scope.vendors.concat(response);
                });

                User.getUsersVendors($scope.user._id).then(function(response) {
                    _.each(response, function(item) {
                        item.active = true; // set to active for this vendor
                    });
                    $scope.vendors = $scope.vendors.concat(response);
                });

            }

            $scope.toggleActive = function(item) {
                item.active = item.active ? false : true;
            };

            // -------

            /**
             * Adds a sales reps id to the passed vendor
             * @param vendor {object} vendor object
             *
             */
            $scope.addVendor = function(vendor) {
                console.log('Vendor id: ' + vendor._id);

                vendor.salesRepId = $scope.user._id;

                Vendor.update(vendor).then(function(response) {
                    console.log(response);
                    refreshVendors();
                });
            };

            $scope.removeVendor = function(vendor) {

                console.log('Vendor id: ' + vendor._id);

                vendor.salesRepId = '';

                Vendor.update(vendor).then(function(response) {
                    console.log(response);
                    refreshVendors();
                });
            };

            $scope.tabs = ['Basic information', 'Vendors', 'Password'];

            $scope.tabs = [{
                name: 'Basic information',
                active: true,
                permission: 'none'
            }, {
                name: 'Vendors',
                active: true,
                permission: 'changeVendors-users'
            }, {
                name: 'Password',
                active: true,
                permission: 'changePassword-users'
            }];

            /**
             * Tab functions.
             * @todo make into a directive
             * @todo make observe / boardcast so we can watch for changes in this scope
             *
             */

            $scope.activeTab = 0;
            $scope.tabs[$scope.activeTab].selected = true;

            // used for active class
            $scope.isActiveTab = function(id) {
                return $scope.tabs[id] && $scope.tabs[id].selected ? true : false;
            };

            // used to set active tab
            $scope.changeTab = function(tab) {

                // @todo, this will need to be more generic if we make into a directive.
                $scope.tabs[$scope.activeTab].selected = false;

                $scope.activeTab = tab;

                $scope.tabs[$scope.activeTab].selected = true;

            };

        }
    ]);
<commit_msg>Refactor userEdit save method to use CommonInterface service<commit_after>angular
    .module('app')
    .controller('userEditController', [
        '$rootScope',
        '$scope',
        '$location',
        '$routeParams',
        'authService',
        'userService',
        'vendorService',
        'FormHelper',
        'CommonInterface',
        function($rootScope, $scope, $location, $routeParams, Auth, User, Vendor, FormHelper, CommonInterface) {

            $scope.modelObject = User;

            Auth.canUserDoAction('edit-users');

            // Options you can set user roles
            $scope.roles = [{
                value: 'salesRep',
                label: 'Sales Rep'
            }, {
                value: 'vendorRep',
                label: 'Vendor Rep'
            }, {
                value: 'admin',
                label: 'Admin'
            }];

            $scope.canChangeRole = function() {
                if ($scope.user._id === Auth.getCurrentUser()._id && $scope.user.role === 'admin') {
                    return false;
                } else {
                    return true;
                }
            };

            $scope.canDeleteUser = function() {
                if ($scope.user._id !== Auth.getCurrentUser()._id) {
                    return true;
                } else {
                    return false;
                }
            };

            // empty user object
            $scope.user = {};
            var user = {};

            // filepicker settings
            // @todo move to global config
            filepicker.setKey('AJNc7mfA3SCxs3gRjg7EBz');

            // pick logo function
            // simple callback assigans to user logo when complete
            $scope.pickImage = function() {
                filepicker.pick(function(FPFile) {
                    console.log(FPFile.url);
                    if (!$scope.user.avatar) {
                        $scope.user.avatar = {};
                    }
                    $scope.user.avatar.original = FPFile.url;
                    $scope.$apply();
                });
            };

            // utility function to go back to the user list
            // @todo this function is used in many places, find a way to streamline it
            $scope.cancel = function() {
                $location.url('/dashboard/users');
            };

            // get user ID for edit pages
            var userId = $routeParams.id;
            $scope.formAction = 'Add';

            // get and store the user
            if (userId) {
                // get the user
                User.getById(userId).then(function(response) {
                    $scope.user = response;

                    $scope.initialRole = $scope.user.role;

                    // get vendors for this user
                    // @todo this will now save when we udate the vendors, so we need to fix this!
                    refreshVendors();
                });
                //console.log($scope.user);
                $scope.formAction = 'Update';
            }

            function updateVendorRelationships() {
                // process each program, checking if its active for the vendor
                _.each($scope.vendors, function(item, key) {

                    // API saves an array of _ids
                    if (item.active) {

                        // check if the user is currently the sales or vendor rep for this vendor
                        if (item.salesRep && item.salesRep._id == $scope.user._id) item.salesRep = null;
                        if (item.vendorRep && item.vendorRep._id == $scope.user._id) item.vendorRep = null;

                        // now set their proper role
                        item[$scope.user.role] = $scope.user._id;
                        console.log(item);

                        Vendor.update(item);

                        //Vendor.update();
                    }

                });
            }

            var formTabMap = [
                'basicForm',
                'usersVendors',
                'passwordForm'
            ];

            // activated when user clicks the save button
            $scope.save = function(doRedirect) {
                CommonInterface.save({
                    Model: User,
                    instance: $scope.user,
                    id: userId,
                    form: $scope.$$childTail[formTabMap[$scope.activeTab]],
                    redirectUrl: '/dashboard/users',
                    doRedirect: doRedirect,
                    strategy: function() {
                        if ($scope.initialRole !== $scope.user.role) {
                            console.log('NEED TO update users vendors');
                            if (confirm('Changing a users role will remove all their vendor associations. Are you sure you wish to continue?')) {
                                _.each($scope.vendors, function(item, key) {
                                    // check if the user is currently the sales or vendor rep for this vendor
                                    if (item.active) {
                                        if (item.salesRep && item.salesRep._id == $scope.user._id) item.salesRep = null;
                                        if (item.vendorRep && item.vendorRep._id == $scope.user._id) item.vendorRep = null;
                                        item.active = false;
                                    }
                                    console.log(item);
                                    Vendor.update(item);
                                });
                            }
                        }
                        updateVendorRelationships();
                    }
                });
            };
            // --------

            /**
             * Gets all the programs, making two calls and merging the results
             * In our view we sort and filter so the active programs appear first
             *
             */
            $scope.vendors = [];

            function refreshVendors() {

                User.getUsersNonVendors($scope.user._id).then(function(response) {
                    $scope.vendors = $scope.vendors.concat(response);
                });

                User.getUsersVendors($scope.user._id).then(function(response) {
                    _.each(response, function(item) {
                        item.active = true; // set to active for this vendor
                    });
                    $scope.vendors = $scope.vendors.concat(response);
                });

            }

            $scope.toggleActive = function(item) {
                item.active = item.active ? false : true;
            };

            // -------

            /**
             * Adds a sales reps id to the passed vendor
             * @param vendor {object} vendor object
             *
             */
            $scope.addVendor = function(vendor) {
                console.log('Vendor id: ' + vendor._id);

                vendor.salesRepId = $scope.user._id;

                Vendor.update(vendor).then(function(response) {
                    console.log(response);
                    refreshVendors();
                });
            };

            $scope.removeVendor = function(vendor) {

                console.log('Vendor id: ' + vendor._id);

                vendor.salesRepId = '';

                Vendor.update(vendor).then(function(response) {
                    console.log(response);
                    refreshVendors();
                });
            };

            $scope.tabs = ['Basic information', 'Vendors', 'Password'];

            $scope.tabs = [{
                name: 'Basic information',
                active: true,
                permission: 'none'
            }, {
                name: 'Vendors',
                active: true,
                permission: 'changeVendors-users'
            }, {
                name: 'Password',
                active: true,
                permission: 'changePassword-users'
            }];

            /**
             * Tab functions.
             * @todo make into a directive
             * @todo make observe / boardcast so we can watch for changes in this scope
             *
             */

            $scope.activeTab = 0;
            $scope.tabs[$scope.activeTab].selected = true;

            // used for active class
            $scope.isActiveTab = function(id) {
                return $scope.tabs[id] && $scope.tabs[id].selected ? true : false;
            };

            // used to set active tab
            $scope.changeTab = function(tab) {

                // @todo, this will need to be more generic if we make into a directive.
                $scope.tabs[$scope.activeTab].selected = false;

                $scope.activeTab = tab;

                $scope.tabs[$scope.activeTab].selected = true;

            };

        }
    ]);
<|endoftext|>


<commit_before>console.log("loading express...");
var express = require('express');
var fs = require('fs');
var app = express();
var post_properties = false;

express.static.mime.define({'application/json': ['fire']});

app.all('*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
});

// By default, use reference implementation
var __appdir = "www/firerest/";
var config_file = 'www/firerest/config.json';
var dirs = ['bootstrap', 'img', 'css', 'js', 'lib'];
for (var i = 0; i < dirs.length; i++) {
  var urlpath = '/firerest/' + dirs[i];
  var filepath = __appdir + dirs[i];
  app.use(urlpath, express.static(filepath));
  console.log("Mapping urlpath:" + urlpath + " to:" + filepath);
}
app.get('/', function(req,res) { res.sendfile('www/index.html'); });
app.get('/index.html', function(req,res) { res.sendfile('www/index.html'); });
app.get('/firerest/cvtest.html', function(req,res) { res.sendfile('www/firerest/cvtest.html'); });

app.use(express.bodyParser());

// If possible, use FireFUSE
console.log("Looking for FireFUSE...");
var firefuse_dir = "/dev/firefuse";
var cv_dir = "/dev/firefuse/cv";
if (fs.existsSync(cv_dir)) {
  config_file = firefuse_dir + '/config.json';
  app.use('/firerest/cv', express.static(cv_dir));
  console.log("Found FireFUSE!");
  console.log("Mapping /firerest/cv to: " + cv_dir);
  post_properties = true;
} else {
  app.use('/firerest/cv', express.static('www/firerest/cv'));
  console.log("FireFUSE is not available. FireREST is demo mode only" );
}

app.get('/firerest/config.json', function(req,res) { res.sendfile(config_file); });

app.post(/.*\/properties.json$/, function(req,res,next) {
  var filepath = req.path.replace(/^\/firerest/, firefuse_dir);
  var data = '';
  req.on('data', function(datum){ data += datum; });
  req.on('end', function (){
    if (post_properties) {
      console.log("POST file:"+filepath+" json:" + data);
      fs.writeFile(filePath, data, function() { res.end(); });
    } else {
      console.log("POST HTTP405 file:"+filepath+" json:" + data);
      res.send(405, {error:"This FireREST web service does not support properties.json updates"});
    }
  });
});

///////////////////////// CHOOSE HTTP PORT ////////////////////////
// Choose port 80 if you are comfortable having your web server operate with root-level access
//var firerest_port=80; // sudo node server/firerest.js
var firerest_port=8080; // node server/firerest.js

app.listen(firerest_port);
console.log('FireREST listening on port ' + firerest_port);
<commit_msg>syntax error<commit_after>console.log("loading express...");
var express = require('express');
var fs = require('fs');
var app = express();
var post_properties = false;

express.static.mime.define({'application/json': ['fire']});

app.all('*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
});

// By default, use reference implementation
var __appdir = "www/firerest/";
var config_file = 'www/firerest/config.json';
var dirs = ['bootstrap', 'img', 'css', 'js', 'lib'];
for (var i = 0; i < dirs.length; i++) {
  var urlpath = '/firerest/' + dirs[i];
  var filepath = __appdir + dirs[i];
  app.use(urlpath, express.static(filepath));
  console.log("Mapping urlpath:" + urlpath + " to:" + filepath);
}
app.get('/', function(req,res) { res.sendfile('www/index.html'); });
app.get('/index.html', function(req,res) { res.sendfile('www/index.html'); });
app.get('/firerest/cvtest.html', function(req,res) { res.sendfile('www/firerest/cvtest.html'); });

app.use(express.bodyParser());

// If possible, use FireFUSE
console.log("Looking for FireFUSE...");
var firefuse_dir = "/dev/firefuse";
var cv_dir = "/dev/firefuse/cv";
if (fs.existsSync(cv_dir)) {
  config_file = firefuse_dir + '/config.json';
  app.use('/firerest/cv', express.static(cv_dir));
  console.log("Found FireFUSE!");
  console.log("Mapping /firerest/cv to: " + cv_dir);
  post_properties = true;
} else {
  app.use('/firerest/cv', express.static('www/firerest/cv'));
  console.log("FireFUSE is not available. FireREST is demo mode only" );
}

app.get('/firerest/config.json', function(req,res) { res.sendfile(config_file); });

app.post(/.*\/properties.json$/, function(req,res,next) {
  var filepath = req.path.replace(/^\/firerest/, firefuse_dir);
  var data = '';
  req.on('data', function(datum){ data += datum; });
  req.on('end', function (){
    if (post_properties) {
      console.log("POST file:"+filepath+" json:" + data);
      fs.writeFile(filepath, data, function() { res.end(); });
    } else {
      console.log("POST HTTP405 file:"+filepath+" json:" + data);
      res.send(405, {error:"This FireREST web service does not support properties.json updates"});
    }
  });
});

///////////////////////// CHOOSE HTTP PORT ////////////////////////
// Choose port 80 if you are comfortable having your web server operate with root-level access
//var firerest_port=80; // sudo node server/firerest.js
var firerest_port=8080; // node server/firerest.js

app.listen(firerest_port);
console.log('FireREST listening on port ' + firerest_port);
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

define([], function() {

//********************************************************************************************* //
//Constants

const Cu = Components.utils;

// ********************************************************************************************* //
// Firebug Trace - FBTrace

var scope = {};

try
{
    Cu["import"]("resource://fbtrace/firebug-trace-service.js", scope);
}
catch (err)
{
    Cu.reportError("FBTrace is not installed, use empty implementation");

    scope.traceConsoleService =
    {
        getTracer: function(prefDomain)
        {
            var TraceAPI = ["dump", "sysout", "setScope", "matchesNode", "time", "timeEnd"];
            var TraceObj = {};
            for (var i=0; i<TraceAPI.length; i++)
                TraceObj[TraceAPI[i]] = function() {};
            return TraceObj;
        }
    };
}

// ********************************************************************************************* //

return scope.traceConsoleService.getTracer("extensions.firebug");

// ********************************************************************************************* //
});<commit_msg>[1.9][&1.8] Do not display a message if FBTrace isn't installed (confusing Firebug users).<commit_after>/* See license.txt for terms of usage */

define([], function() {

//********************************************************************************************* //
//Constants

const Cu = Components.utils;

// ********************************************************************************************* //
// Firebug Trace - FBTrace

var scope = {};

try
{
    Cu["import"]("resource://fbtrace/firebug-trace-service.js", scope);
}
catch (err)
{
    scope.traceConsoleService =
    {
        getTracer: function(prefDomain)
        {
            var TraceAPI = ["dump", "sysout", "setScope", "matchesNode", "time", "timeEnd"];
            var TraceObj = {};
            for (var i=0; i<TraceAPI.length; i++)
                TraceObj[TraceAPI[i]] = function() {};
            return TraceObj;
        }
    };
}

// ********************************************************************************************* //

return scope.traceConsoleService.getTracer("extensions.firebug");

// ********************************************************************************************* //
});<|endoftext|>


<commit_before>import React from 'react';
import { props, t, skinnable } from '../utils';
import Select from 'react-select';
import find from 'lodash/find';
import omit from 'lodash/omit';
import sortBy from 'lodash/orderBy';
import findIndex from 'lodash/findIndex';
import last from 'lodash/last';
import dropRight from 'lodash/dropRight';
import FlexView from 'react-flexview';
import cx from 'classnames';
import { warn } from '../utils/log';

const isEmptyArray = x => t.Array.is(x) && x.length === 0;

const defaultOptionGroupRenderer = title => title;

export const Props = {
  value: t.maybe(t.union([t.Number, t.String, t.Object, t.list(t.Object)])),
  valueLink: t.maybe(t.struct({
    value: t.maybe(t.union([t.Number, t.String, t.Object, t.list(t.Object)])),
    requestChange: t.Function
  })),
  onChange: t.maybe(t.Function),
  onValueClick: t.maybe(t.Function),
  options: t.list(t.Object),
  size: t.maybe(t.enums.of(['medium', 'small'])),
  disabled: t.maybe(t.Boolean),
  searchable: t.maybe(t.Boolean),
  clearable: t.maybe(t.Boolean),
  backspaceRemoves: t.maybe(t.Boolean),
  multi: t.maybe(t.Boolean),
  flat: t.maybe(t.Boolean),
  autoBlur: t.maybe(t.Boolean),
  simpleValue: t.maybe(t.Boolean),
  menuPosition: t.maybe(t.enums.of(['top', 'bottom'])),
  menuRenderer: t.maybe(t.Function),
  groupByKey: t.maybe(t.String),
  optionGroupRenderer: t.maybe(t.Function),
  placeholder: t.maybe(t.union([t.String, t.ReactElement])),
  noResultsText: t.maybe(t.String),
  allowCreate: t.maybe(t.Boolean),
  addLabelText: t.maybe(t.String),
  valueRenderer: t.maybe(t.Function),
  optionRenderer: t.maybe(t.Function),
  delimiter: t.maybe(t.String),
  onInputChange: t.maybe(t.Function),
  onFocus: t.maybe(t.Function),
  onBlur: t.maybe(t.Function),
  onBlurResetsInput: t.maybe(t.Boolean),
  onCloseResetsInput: t.maybe(t.Boolean),
  isLoading: t.maybe(t.Boolean),
  id: t.maybe(t.String),
  className: t.maybe(t.String),
  style: t.maybe(t.Object)
};

/** A dropdown field based on [react-select](https://github.com/JedWatson/react-select)
 * @param value - selected value
 * @param valueLink - defines actions to be taken when a particular value is selected
 * @param onChange - called when value is changed
 * @param options - available options
 * @param size - medium | small
 * @param disabled - true if disabled
 * @param searchable - true if it should be possible to search the desired value by writing into the dropdown
 * @param clearable - true if it should be possible to reset the selected value
 * @param backspaceRemoves - whether pressing backspace removes the last item when there is no input value
 * @param multi - true if it should be possible to select multiple values
 * @param flat - whether it should have a flat style
 * @param autoBlur - whether it should blur automatically when the user selects a value
 * @param simpleValue - if true, selected values will be passed to onChange as comma-separated string of values (eg "1,2,3") instead of array of objects
 * @param optionRenderer - the function that can be used to override the default renderer of options
 * @param valueRenderer - the function that can be used to override the default renderer of the selected value
 * @param menuRenderer - the function that can be used to override the default drop-down list of options
 * @param groupByKey - the field name to group by
 * @param optionGroupRenderer - the function that gets used to render the content of an option group
 * @param menuPosition - whether the menu should be rendered on top or bottom when it's open
 * @param placeholder - placeholder shown when no value is selected
 * @param noResultsText - if searchable, message shown in the menu when no results are found
 * @param allowCreate - whether it should be possible to create new options
 * @param addLabelText - if allowCreate is true, message shown to hint the user to press Enter to create a new option
 * @param delimiter - if multi is true, string used to separate selected values
 * @param onFocus - called when dropdown is focused
 * @param onBlur - called when dropdown is blurred
 * @param onInputChange - called when the value of the `input` is changed
 * @param onValueClick - called when user clicks on the selected value
 * @param onBlurResetsInput - whether it should clear the input box on blur
 * @param onCloseResetsInput - wheter it should clear the input box on close
 * @param isLoading - whether it is loading options asynchronously
 */
@skinnable()
@props(Props, { strict: true })
export default class Dropdown extends React.Component {

  static defaultProps = {
    delimiter: ',',
    size: 'medium',
    disabled: false,
    searchable: false,
    clearable: false,
    multi: false,
    flat: false,
    autoBlur: true,
    simpleValue: true,
    groupByKey: 'optionGroup',
    optionGroupRenderer: defaultOptionGroupRenderer,
    menuPosition: 'bottom'
  }

  componentDidMount() {
    this.logWarnings();
  }

  logWarnings = () => {
    if (this.props.children) {
      warn('You\'re passing children. Not expected behaviour');
    }
  };

  getValue = () => (
    this.props.valueLink ? this.props.valueLink.value : this.props.value
  );

  valueToOption = (value, options) => {
    if (t.String.is(value) || t.Number.is(value)) {
      const { multi, delimiter } = this.props;
      if (multi) {
        const values = String(value).split(delimiter);
        return values.map(v => find(options, { value: v }));
      }
      return find(options, { value });
    }
    return value;
  };

  sortOptionsByGroup = (options) => {
    const { groupByKey } = this.props;
    return sortBy(options, option => option[groupByKey] ? findIndex(options, o => option[groupByKey] === o[groupByKey]) : -1);
  }

  getOnChange = () => this.props.valueLink ? this.props.valueLink.requestChange : this.props.onChange;

  getCustomClassNames() {
    const { size, flat, clearable, menuPosition } = this.props;
    return cx({
      'is-medium': size === 'medium',
      'is-small': size === 'small',
      'is-flat': flat,
      'is-clearable': clearable,
      'menu-position-top': menuPosition === 'top'
    });
  }

  _onChange = _value => {
    const onChange = this.getOnChange();
    const value = isEmptyArray(_value) ? null : _value;
    return onChange(value);
  }

  onInputKeyDown = (e) => {
    if (e.keyCode === 38 || e.keyCode === 40) {
      if (isEmptyArray(this.props.options)) {
        e.preventDefault();
      }
    }
  }

  focus = () => { this.select.focus(); }

  optionGroupRenderer = ( title ) => {
    if (title === 'undefined') {
      return null;
    }
    else {
      return (
        <FlexView className='Select-option-group'>
          {this.props.optionGroupRenderer(title)}
        </FlexView>
      );
    }
  }

  _menuRenderer = ({
    focusedOption,
    instancePrefix,
    onFocus,
    onSelect,
    optionClassName,
    optionComponent,
    optionRenderer,
    options,
    valueArray,
    valueKey
  }) => {

    const Option = optionComponent;
    const { groupByKey } = this.props;
    const groupedOptions = options.reduce((acc, o, i) => {
      const shouldCreateNewGroup = i === 0 || o[groupByKey] !== options[i - 1][groupByKey];
      if (shouldCreateNewGroup) {
        const newGroup = {
          optionGroupTitle: o[groupByKey] || '',
          optionGroup: [o]
        };
        return [...acc, newGroup];
      } else {
        const lastGroup = last(acc);
        return [...dropRight(acc), {
          optionGroupTitle: lastGroup.optionGroupTitle,
          optionGroup: [...lastGroup.optionGroup, o]
        }];
      }
    }, []);

    return groupedOptions.map(({ optionGroup, optionGroupTitle }) => {
      return (
        <FlexView column key={optionGroupTitle}>
          {this.optionGroupRenderer(optionGroupTitle)}
          {optionGroup.map((option, i) => {
            const isSelected = valueArray && valueArray.indexOf(option) > -1;
            const isFocused = option === focusedOption;
            const optionRef = isFocused ? 'focused' : null;
            const optionClass = cx(optionClassName, {
              'Select-option': true,
              'is-selected': isSelected,
              'is-focused': isFocused,
              'is-disabled': option.disabled
            });
            return (
              <Option
                className={optionClass}
                instancePrefix={instancePrefix}
                isDisabled={option.disabled}
                isFocused={isFocused}
                isSelected={isSelected}
                key={`option-${i}-${option[valueKey]}`}
                onFocus={onFocus}
                onSelect={onSelect}
                option={option}
                optionIndex={i}
                ref={optionRef}
              >
                {optionRenderer(option, i)}
              </Option>
            );
          })}
        </FlexView>
      );
    });
  }

  getLocals() {
    const {
      _onChange,
      onInputKeyDown,
      _menuRenderer,
      props: { className, options, backspaceRemoves, clearable, ...props }
    } = this;

    return {
      ...omit(props, 'valueLink'),
      options: this.sortOptionsByGroup(options),
      clearable,
      backspaceRemoves: t.Nil.is(backspaceRemoves) ? clearable : backspaceRemoves,
      resetValue: null,
      className: cx('dropdown', className, this.getCustomClassNames()),
      value: this.valueToOption(this.getValue(), options),
      onInputKeyDown,
      onChange: _onChange,
      menuRenderer: this.props.menuRenderer || _menuRenderer
    };
  }

  template(locals) {
    return <Select {...locals} ref={select => { this.select = select; }} />;
  }
}
<commit_msg>code cleaning and comments<commit_after>import React from 'react';
import { props, t, skinnable } from '../utils';
import Select from 'react-select';
import find from 'lodash/find';
import omit from 'lodash/omit';
import sortBy from 'lodash/orderBy';
import findIndex from 'lodash/findIndex';
import last from 'lodash/last';
import dropRight from 'lodash/dropRight';
import FlexView from 'react-flexview';
import cx from 'classnames';
import { warn } from '../utils/log';

const isEmptyArray = x => t.Array.is(x) && x.length === 0;

const defaultOptionGroupRenderer = title => title;

export const Props = {
  value: t.maybe(t.union([t.Number, t.String, t.Object, t.list(t.Object)])),
  valueLink: t.maybe(t.struct({
    value: t.maybe(t.union([t.Number, t.String, t.Object, t.list(t.Object)])),
    requestChange: t.Function
  })),
  onChange: t.maybe(t.Function),
  onValueClick: t.maybe(t.Function),
  options: t.list(t.Object),
  size: t.maybe(t.enums.of(['medium', 'small'])),
  disabled: t.maybe(t.Boolean),
  searchable: t.maybe(t.Boolean),
  clearable: t.maybe(t.Boolean),
  backspaceRemoves: t.maybe(t.Boolean),
  multi: t.maybe(t.Boolean),
  flat: t.maybe(t.Boolean),
  autoBlur: t.maybe(t.Boolean),
  simpleValue: t.maybe(t.Boolean),
  menuPosition: t.maybe(t.enums.of(['top', 'bottom'])),
  menuRenderer: t.maybe(t.Function),
  groupByKey: t.maybe(t.String),
  optionGroupRenderer: t.maybe(t.Function),
  placeholder: t.maybe(t.union([t.String, t.ReactElement])),
  noResultsText: t.maybe(t.String),
  allowCreate: t.maybe(t.Boolean),
  addLabelText: t.maybe(t.String),
  valueRenderer: t.maybe(t.Function),
  optionRenderer: t.maybe(t.Function),
  delimiter: t.maybe(t.String),
  onInputChange: t.maybe(t.Function),
  onFocus: t.maybe(t.Function),
  onBlur: t.maybe(t.Function),
  onBlurResetsInput: t.maybe(t.Boolean),
  onCloseResetsInput: t.maybe(t.Boolean),
  isLoading: t.maybe(t.Boolean),
  id: t.maybe(t.String),
  className: t.maybe(t.String),
  style: t.maybe(t.Object)
};

/** A dropdown field based on [react-select](https://github.com/JedWatson/react-select)
 * @param value - selected value
 * @param valueLink - defines actions to be taken when a particular value is selected
 * @param onChange - called when value is changed
 * @param options - available options
 * @param size - medium | small
 * @param disabled - true if disabled
 * @param searchable - true if it should be possible to search the desired value by writing into the dropdown
 * @param clearable - true if it should be possible to reset the selected value
 * @param backspaceRemoves - whether pressing backspace removes the last item when there is no input value
 * @param multi - true if it should be possible to select multiple values
 * @param flat - whether it should have a flat style
 * @param autoBlur - whether it should blur automatically when the user selects a value
 * @param simpleValue - if true, selected values will be passed to onChange as comma-separated string of values (eg "1,2,3") instead of array of objects
 * @param optionRenderer - the function that can be used to override the default renderer of options
 * @param valueRenderer - the function that can be used to override the default renderer of the selected value
 * @param menuRenderer - the function that can be used to override the default drop-down list of options
 * @param groupByKey - the field name to group by
 * @param optionGroupRenderer - the function that gets used to render the content of an option group
 * @param menuPosition - whether the menu should be rendered on top or bottom when it's open
 * @param placeholder - placeholder shown when no value is selected
 * @param noResultsText - if searchable, message shown in the menu when no results are found
 * @param allowCreate - whether it should be possible to create new options
 * @param addLabelText - if allowCreate is true, message shown to hint the user to press Enter to create a new option
 * @param delimiter - if multi is true, string used to separate selected values
 * @param onFocus - called when dropdown is focused
 * @param onBlur - called when dropdown is blurred
 * @param onInputChange - called when the value of the `input` is changed
 * @param onValueClick - called when user clicks on the selected value
 * @param onBlurResetsInput - whether it should clear the input box on blur
 * @param onCloseResetsInput - wheter it should clear the input box on close
 * @param isLoading - whether it is loading options asynchronously
 */
@skinnable()
@props(Props, { strict: true })
export default class Dropdown extends React.Component {

  static defaultProps = {
    delimiter: ',',
    size: 'medium',
    disabled: false,
    searchable: false,
    clearable: false,
    multi: false,
    flat: false,
    autoBlur: true,
    simpleValue: true,
    groupByKey: 'optionGroup',
    optionGroupRenderer: defaultOptionGroupRenderer,
    menuPosition: 'bottom'
  }

  componentDidMount() {
    this.logWarnings();
  }

  logWarnings = () => {
    if (this.props.children) {
      warn('You\'re passing children. Not expected behaviour');
    }
  };

  getValue = () => (
    this.props.valueLink ? this.props.valueLink.value : this.props.value
  );

  valueToOption = (value, options) => {
    if (t.String.is(value) || t.Number.is(value)) {
      const { multi, delimiter } = this.props;
      if (multi) {
        const values = String(value).split(delimiter);
        return values.map(v => find(options, { value: v }));
      }
      return find(options, { value });
    }
    return value;
  };

  sortOptionsByGroup = (options) => {
    const { groupByKey } = this.props;
    return sortBy(options, option => option[groupByKey] ? findIndex(options, o => option[groupByKey] === o[groupByKey]) : -1);
  }

  getOnChange = () => this.props.valueLink ? this.props.valueLink.requestChange : this.props.onChange;

  getCustomClassNames() {
    const { size, flat, clearable, menuPosition } = this.props;
    return cx({
      'is-medium': size === 'medium',
      'is-small': size === 'small',
      'is-flat': flat,
      'is-clearable': clearable,
      'menu-position-top': menuPosition === 'top'
    });
  }

  _onChange = _value => {
    const onChange = this.getOnChange();
    const value = isEmptyArray(_value) ? null : _value;
    return onChange(value);
  }

  onInputKeyDown = (e) => {
    if (e.keyCode === 38 || e.keyCode === 40) {
      if (isEmptyArray(this.props.options)) {
        e.preventDefault();
      }
    }
  }

  focus = () => { this.select.focus(); }

  optionGroupRenderer = ( title ) => {
    return title ? (
      <FlexView className='Select-option-group'>
        {this.props.optionGroupRenderer(title)}
      </FlexView>
    ) : null;
  }

  _menuRenderer = ({
    focusedOption,
    instancePrefix,
    onFocus,
    onSelect,
    optionClassName,
    optionComponent,
    optionRenderer,
    options,
    valueArray,
    valueKey
  }) => {

    const Option = optionComponent;
    const { groupByKey } = this.props;
    const groupedOptions = options.reduce((acc, o, i) => {

      // options are already sorted by group, so we know when a new group starts
      // just by checking the previous option
      const shouldCreateNewGroup = i === 0 || o[groupByKey] !== options[i - 1][groupByKey];

      if (shouldCreateNewGroup) {
        const newGroup = {
          optionGroupTitle: o[groupByKey],
          optionGroup: [o]
        };
        return [...acc, newGroup];
      } else {
        const lastGroup = last(acc);
        return [...dropRight(acc), {
          optionGroupTitle: lastGroup.optionGroupTitle,
          optionGroup: [...lastGroup.optionGroup, o]
        }];
      }
    }, []);

    return groupedOptions.map(({ optionGroup, optionGroupTitle }) => {
      return (
        <FlexView column key={optionGroupTitle || ''}>
          {this.optionGroupRenderer(optionGroupTitle)}
          {optionGroup.map((option, i) => {
            const isSelected = valueArray && valueArray.indexOf(option) > -1;
            const isFocused = option === focusedOption;
            const optionRef = isFocused ? 'focused' : null;
            const optionClass = cx(optionClassName, {
              'Select-option': true,
              'is-selected': isSelected,
              'is-focused': isFocused,
              'is-disabled': option.disabled
            });
            return (
              <Option
                className={optionClass}
                instancePrefix={instancePrefix}
                isDisabled={option.disabled}
                isFocused={isFocused}
                isSelected={isSelected}
                key={`option-${i}-${option[valueKey]}`}
                onFocus={onFocus}
                onSelect={onSelect}
                option={option}
                optionIndex={i}
                ref={optionRef}
              >
                {optionRenderer(option, i)}
              </Option>
            );
          })}
        </FlexView>
      );
    });
  }

  getLocals() {
    const {
      _onChange,
      onInputKeyDown,
      _menuRenderer,
      props: { className, options, backspaceRemoves, clearable, ...props }
    } = this;

    return {
      ...omit(props, 'valueLink'),
      options: this.sortOptionsByGroup(options),
      clearable,
      backspaceRemoves: t.Nil.is(backspaceRemoves) ? clearable : backspaceRemoves,
      resetValue: null,
      className: cx('dropdown', className, this.getCustomClassNames()),
      value: this.valueToOption(this.getValue(), options),
      onInputKeyDown,
      onChange: _onChange,
      menuRenderer: this.props.menuRenderer || _menuRenderer
    };
  }

  template(locals) {
    return <Select {...locals} ref={select => { this.select = select; }} />;
  }
}
<|endoftext|>