<commit_before>define(["Ti/_/browser", "Ti/_/declare", "Ti/_/UI/KineticScrollView", "Ti/_/lang", "Ti/_/dom", "Ti/_/style", "Ti/UI", "Ti/_/event"],
	function(browser, declare, KineticScrollView, lang, dom, style, UI, event) {

	var setStyle = style.set,
		is = require.is,
		isDef = lang.isDef,
		unitize = dom.unitize,
		once = require.on.once,

		// The maximum angle, in radians, from the axis a swipe is allowed to travel before it is no longer considered a swipe
		angleThreshold = Math.PI/6, // 30 degrees

		// Velocity bounds, used to make sure that animations don't become super long or super short
		minVelocity = 0.4,
		maxVelocity = 3,

		// This sets the minimum velocity that determines whether a swipe was a flick or a drag
		velocityThreshold = 0.4,

		// This determines the minimum distance scale (i.e. width divided by this value) before a flick requests a page turn
		minimumFlickDistanceScaleFactor = 20,

		// This determines the minimum distance scale (i.e. width divided by this value) before a drag requests a page turn
		minimumDragDistanceScaleFactor = 2;

	return declare("Ti.UI.ScrollableView", KineticScrollView, {

		constructor: function(args){

			// Create the content container
			this._initKineticScrollView(UI.createView({
				left: 0,
				top: 0,
				width: UI.SIZE,
				height: "100%",
				layout: "constrainingHorizontal"
			}), "horizontal");

			// Create the paging control container
			this._add(this._pagingControlContainer = UI.createView({
				width: "100%",
				height: 20,
				bottom: 0,
				backgroundColor: "black",
				opacity: 0,
				touchEnabled: false
			}));

			this._pagingControlContainer._add(this._pagingControlContentContainer = UI.createView({
				width: UI.SIZE,
				height: "100%",
				top: 0,
				touchEnabled: false,
				layout: "constrainingHorizontal"
			}));

			// State variables
			this.properties.__values__.views = [];
			this._viewToRemoveAfterScroll = -1;

			require.on(this, "postlayout", this._updateTranslation);
		},

		_handleDragStart: function() {
			var currentPage = this.currentPage;
			if (~currentPage) {
				this._showView(currentPage - 1);
				this._showView(currentPage + 1);
				this.fireEvent("dragStart");
			}
		},

		_handleDrag: function(e) {
			var currentPage = this.currentPage,
				currentView = this.views[currentPage];
			if (currentView) {
				this.fireEvent("scroll", {
					currentPage: currentPage,
					currentPageAsFloat: currentPage - e.distanceX / currentView._measuredWidth,
					view: currentView
				});
			}
		},

		_handleDragCancel: function() {
			var currentPage = this.currentPage;
			if (~currentPage) {
				this._hideView(currentPage - 1);
				this._hideView(currentPage + 1);
			}
		},

		_handleDragEnd: function(e, velocityX) {
			if (~this.currentPage && isDef(velocityX)) {
				velocityX = Math.max(minVelocity, Math.min(maxVelocity, velocityX));
				var self = this,
					views = self.views,
					contentContainer = self._contentContainer,
					currentPage = self.currentPage,
					distance = e.distanceX,
					normalizedWidth = views[currentPage]._measuredWidth / (Math.abs(velocityX) > velocityThreshold ?
						minimumFlickDistanceScaleFactor :
						minimumDragDistanceScaleFactor),
					destinationPosition,
					destination = views[currentPage],
					destinationIndex = currentPage;

				// Determine the animation characteristics
				if (distance > normalizedWidth && currentPage > 0) {
					// Previous page
					destinationIndex = currentPage - 1;
					distance = (destination = views[destinationIndex])._measuredLeft - self._currentTranslationX;
				} else if (distance < -normalizedWidth && currentPage < views.length - 1) {
					// Next page
					destinationIndex = currentPage + 1;
					distance = self._currentTranslationX - (destination = views[destinationIndex])._measuredLeft;
				}
				destinationPosition = -destination._measuredLeft;

				// Fire the drag end event
				self.fireEvent("dragEnd", {
					currentPage: destinationIndex,
					view: destination
				});

				// Animate the view. Note: the 1.724 constance was calculated, not estimated. It is NOT for tweaking.
				// If tweaking is needed, tweak the velocity algorithm in KineticScrollView.
				self._animateToPosition(destinationPosition, 0, Math.abs(1.724 * (destinationPosition - self._currentTranslationX) / velocityX), "ease-out", function(){
					destinationIndex !== currentPage - 1 && self._hideView(currentPage - 1);
					destinationIndex !== currentPage && self._hideView(currentPage);
					destinationIndex !== currentPage + 1 && self._hideView(currentPage + 1);
					self.properties.__values__.currentPage = destinationIndex;
					setTimeout(function(){
						self.fireEvent("scrollEnd",{
							currentPage: destinationIndex,
							view: destination
						});
					}, 1);
				});
			}
		},

		_hideView: function(index) {
			var views = this.views;
			index >= 0 && index < views.length && setStyle(views[index].domNode, "display", "none");
		},

		_showView: function(index) {
			var views = this.views;
			index >= 0 && index < views.length && setStyle(views[index].domNode, "display", "inherit");
		},

		addView: function(view){
			if (view) {
				this.views.push(view);
				this._contentContainer._add(view);
				if (this.views.length == 1) {
					this.properties.__values__.currentPage = 0;
				} else {
					setStyle(view.domNode, "display", "none");
				}
			}
		},

		removeView: function(view) {

			// Get and validate the location of the view
			var viewIndex = is(view,"Number") ? view : this.views.indexOf(view);
			if (viewIndex < 0 || viewIndex >= this.views.length) {
				return;
			}

			// Update the view if this view was currently visible
			if (viewIndex == this.currentPage && this.views.length !== 1) {
				this._viewToRemoveAfterScroll = viewIndex;
				this.scrollToView(viewIndex == this.views.length - 1 ? --viewIndex : ++viewIndex);
			} else {
				this._removeViewFromList(viewIndex);
			}
		},

		_removeViewFromList: function(viewIndex) {

			var contentContainer = this._contentContainer,
				self = this;

			// Update the current view if necessary once everything has been re-laid out.
			if (viewIndex < this.currentPage) {
				self.properties.__values__.currentPage--;
			}

			// Remove the view and update the paging control
			contentContainer._remove(self.views.splice(viewIndex,1)[0]);
			!self.views.length && (self.properties.__values__.currentPage = -1);
			once(UI, "postlayout", function() {
				setTimeout(function(){
					self._updateTranslation();
				}, 1);
			});
			self._updatePagingControl(self.currentPage);
		},

		_updateTranslation: function() {
			~this.currentPage && this._setTranslation(-this.views[this.currentPage]._measuredLeft, 0);
		},

		scrollToView: function(view) {
			var viewIndex = is(view,"Number") ? view : this.views.indexOf(view),
				self = this;

			// Sanity check
			if (viewIndex < 0 || viewIndex >= this.views.length || viewIndex == this.currentPage) {
				return;
			}

			function scroll(){

				// Calculate the views to be scrolled
				var contentContainer = self._contentContainer,
					currentPage = self.currentPage,
					destination = -self.views[viewIndex]._measuredLeft,
					i;

					// Calculate a weighted duration so that larger views take longer to scroll.
					duration = 400 + 0.3 * (Math.abs(viewIndex - self.currentPage) * contentContainer._measuredWidth);

				// Make the views that will be seen visible
				if (currentPage < viewIndex) {
					for(i = currentPage + 1; i <= viewIndex; i++) {
						self._showView(i);
					}
				} else {
					for(i = viewIndex; i < currentPage; i++) {
						self._showView(i);
					}
				}

				// Animate the views
				self._updatePagingControl(viewIndex);
				self._animateToPosition(destination, 0, duration, "ease-in-out", function(){
					self.properties.__values__.currentPage = viewIndex;
					if (currentPage < viewIndex) {
						for(i = currentPage; i < viewIndex; i++) {
							self._hideView(i);
						}
					} else {
						for(i = viewIndex + 1; i <= currentPage; i++) {
							self._hideView(i);
						}
					}
					if (self._viewToRemoveAfterScroll !== -1) {
						destination += self.views[self._viewToRemoveAfterScroll]._measuredWidth;
						self._removeViewFromList(self._viewToRemoveAfterScroll);
						self._viewToRemoveAfterScroll = -1;
					}
					self.fireEvent("scrollEnd",{
						currentPage: viewIndex,
						view: self.views[viewIndex]
					});
				});
			}

			// If the scrollableView hasn't been laid out yet, we must wait until it is
			if (self._contentContainer.domNode.offsetWidth) {
				scroll();
			} else {
				once("postlayout", scroll);
			}
		},

		_showPagingControl: function() {
			if (!this.showPagingControl) {
				this._pagingControlContainer.opacity = 0;
				return;
			}
			if (this._isPagingControlActive) {
				return;
			}
			this._isPagingControlActive = true;
			this._pagingControlContainer.animate({
				duration: 250,
				opacity: 0.75
			});
			this.pagingControlTimeout > 0 && setTimeout(lang.hitch(this,function() {
				this._pagingControlContainer.animate({
					duration: 750,
					opacity: 0
				});
				this._isPagingControlActive = false;
			}),this.pagingControlTimeout);
		},

		_updatePagingControl: function(newIndex, hidePagingControl) {
			if (this.showPagingControl) {
				this._pagingControlContentContainer._removeAllChildren();
				var diameter = this.pagingControlHeight / 2;
				for (var i = 0; i < this.views.length; i++) {
					var indicator = UI.createView({
						width: diameter,
						height: diameter,
						left: 5,
						right: 5,
						backgroundColor: i === newIndex ? "white" : "grey",
						borderRadius: unitize(diameter / 2)
					});
					this._pagingControlContentContainer._add(indicator);
				}
				!hidePagingControl && this._showPagingControl();
			}
		},

		_defaultWidth: UI.FILL,

		_defaultHeight: UI.FILL,

		properties: {
			clipViews: {

			},
			currentPage: {
				set: function(value, oldValue) {
					if (value >= 0 && value < this.views.length) {
						this.scrollToView(value);
						return value;
					}
					return oldValue;
				}
			},
			pagingControlColor: {
				set: function(value) {
					this._pagingControlContainer.backgroundColor = value;
					return value;
				},
				value: "black"
			},
			pagingControlHeight: {
				set: function(value) {
					this._pagingControlContainer.height = value;
					return value;
				},
				value: 20
			},
			pagingControlTimeout: {
				set: function(value) {
					this.pagingControlTimeout == 0 && this._updatePagingControl();
					return value;
				},
				value: 1250
			},
			showPagingControl: {
				set: function(value) {
					this.pagingControlTimeout == 0 && this._updatePagingControl();
					return value;
				},
				value: false
			},
			views: {
				set: function(value) {

					// Value must be an array
					if (!is(value,"Array")) {
						return;
					}

					// Add the views to the content container
					var i = 0,
						len = value.length,
						contentContainer = this._contentContainer,
						view;
					contentContainer._removeAllChildren();
					for(; i < len; i++) {
						(view = value[i]).width = "100%";
						view.height = "100%";
						contentContainer._add(view);
					}

					return value;
				},
				post: function() {
					this._updatePagingControl(this.currentPage,true);
				}
			}
		}

	});

});<commit_msg>[TIMOB-9370] Beefed up showing/hiding views when multiple swipes are occuring.<commit_after>define(["Ti/_/browser", "Ti/_/declare", "Ti/_/UI/KineticScrollView", "Ti/_/lang", "Ti/_/dom", "Ti/_/style", "Ti/UI", "Ti/_/event"],
	function(browser, declare, KineticScrollView, lang, dom, style, UI, event) {

	var setStyle = style.set,
		is = require.is,
		isDef = lang.isDef,
		unitize = dom.unitize,
		once = require.on.once,

		// The maximum angle, in radians, from the axis a swipe is allowed to travel before it is no longer considered a swipe
		angleThreshold = Math.PI/6, // 30 degrees

		// Velocity bounds, used to make sure that animations don't become super long or super short
		minVelocity = 0.4,
		maxVelocity = 3,

		// This sets the minimum velocity that determines whether a swipe was a flick or a drag
		velocityThreshold = 0.4,

		// This determines the minimum distance scale (i.e. width divided by this value) before a flick requests a page turn
		minimumFlickDistanceScaleFactor = 20,

		// This determines the minimum distance scale (i.e. width divided by this value) before a drag requests a page turn
		minimumDragDistanceScaleFactor = 2;

	return declare("Ti.UI.ScrollableView", KineticScrollView, {

		constructor: function(args){

			// Create the content container
			this._initKineticScrollView(UI.createView({
				left: 0,
				top: 0,
				width: UI.SIZE,
				height: "100%",
				layout: "constrainingHorizontal"
			}), "horizontal");

			// Create the paging control container
			this._add(this._pagingControlContainer = UI.createView({
				width: "100%",
				height: 20,
				bottom: 0,
				backgroundColor: "black",
				opacity: 0,
				touchEnabled: false
			}));

			this._pagingControlContainer._add(this._pagingControlContentContainer = UI.createView({
				width: UI.SIZE,
				height: "100%",
				top: 0,
				touchEnabled: false,
				layout: "constrainingHorizontal"
			}));

			// State variables
			this.properties.__values__.views = [];
			this._viewToRemoveAfterScroll = -1;

			require.on(this, "postlayout", this._updateTranslation);
		},

		_handleDragStart: function() {
			var currentPage = this.currentPage;
			if (~currentPage) {
				this._showView(currentPage - 1);
				this._showView(currentPage);
				this._showView(currentPage + 1);
				this.fireEvent("dragStart");
			}
		},

		_handleDrag: function(e) {
			var currentPage = this.currentPage,
				currentView = this.views[currentPage];
			if (currentView) {
				this.fireEvent("scroll", {
					currentPage: currentPage,
					currentPageAsFloat: currentPage - e.distanceX / currentView._measuredWidth,
					view: currentView
				});
			}
		},

		_handleDragCancel: function() {
			var currentPage = this.currentPage;
			if (~currentPage) {
				this._hideView(currentPage - 1);
				this._showView(currentPage);
				this._hideView(currentPage + 1);
			}
		},

		_handleDragEnd: function(e, velocityX) {
			if (~this.currentPage && isDef(velocityX)) {
				velocityX = Math.max(minVelocity, Math.min(maxVelocity, velocityX));
				var self = this,
					views = self.views,
					contentContainer = self._contentContainer,
					currentPage = self.currentPage,
					distance = e.distanceX,
					normalizedWidth = views[currentPage]._measuredWidth / (Math.abs(velocityX) > velocityThreshold ?
						minimumFlickDistanceScaleFactor :
						minimumDragDistanceScaleFactor),
					destinationPosition,
					destination = views[currentPage],
					destinationIndex = currentPage;

				// Determine the animation characteristics
				if (distance > normalizedWidth && currentPage > 0) {
					// Previous page
					destinationIndex = currentPage - 1;
					distance = (destination = views[destinationIndex])._measuredLeft - self._currentTranslationX;
				} else if (distance < -normalizedWidth && currentPage < views.length - 1) {
					// Next page
					destinationIndex = currentPage + 1;
					distance = self._currentTranslationX - (destination = views[destinationIndex])._measuredLeft;
				}
				destinationPosition = -destination._measuredLeft;

				// Fire the drag end event
				self.fireEvent("dragEnd", {
					currentPage: destinationIndex,
					view: destination
				});

				// Animate the view. Note: the 1.724 constance was calculated, not estimated. It is NOT for tweaking.
				// If tweaking is needed, tweak the velocity algorithm in KineticScrollView.
				self._animateToPosition(destinationPosition, 0, Math.abs(1.724 *
						(destinationPosition - self._currentTranslationX) / velocityX), "ease-out", function(){
					destinationIndex !== currentPage - 1 && self._hideView(currentPage - 1);
					destinationIndex !== currentPage && self._hideView(currentPage);
					destinationIndex !== currentPage + 1 && self._hideView(currentPage + 1);
					self.properties.__values__.currentPage = destinationIndex;
					self._showView(destinationIndex);
					setTimeout(function(){
						self.fireEvent("scrollEnd",{
							currentPage: destinationIndex,
							view: destination
						});
					}, 1);
				});
			}
		},

		_hideView: function(index) {
			var views = this.views;
			index >= 0 && index < views.length && setStyle(views[index].domNode, "display", "none");
		},

		_showView: function(index) {
			var views = this.views;
			index >= 0 && index < views.length && setStyle(views[index].domNode, "display", "inherit");
		},

		addView: function(view){
			if (view) {
				this.views.push(view);
				this._contentContainer._add(view);
				if (this.views.length == 1) {
					this.properties.__values__.currentPage = 0;
				} else {
					setStyle(view.domNode, "display", "none");
				}
			}
		},

		removeView: function(view) {

			// Get and validate the location of the view
			var viewIndex = is(view,"Number") ? view : this.views.indexOf(view);
			if (viewIndex < 0 || viewIndex >= this.views.length) {
				return;
			}

			// Update the view if this view was currently visible
			if (viewIndex == this.currentPage && this.views.length !== 1) {
				this._viewToRemoveAfterScroll = viewIndex;
				this.scrollToView(viewIndex == this.views.length - 1 ? --viewIndex : ++viewIndex);
			} else {
				this._removeViewFromList(viewIndex);
			}
		},

		_removeViewFromList: function(viewIndex) {

			var contentContainer = this._contentContainer,
				self = this;

			// Update the current view if necessary once everything has been re-laid out.
			if (viewIndex < this.currentPage) {
				self.properties.__values__.currentPage--;
			}

			// Remove the view and update the paging control
			contentContainer._remove(self.views.splice(viewIndex,1)[0]);
			!self.views.length && (self.properties.__values__.currentPage = -1);
			once(UI, "postlayout", function() {
				setTimeout(function(){
					self._updateTranslation();
				}, 1);
			});
			self._updatePagingControl(self.currentPage);
		},

		_updateTranslation: function() {
			~this.currentPage && this._setTranslation(-this.views[this.currentPage]._measuredLeft, 0);
		},

		scrollToView: function(view) {
			var viewIndex = is(view,"Number") ? view : this.views.indexOf(view),
				self = this;

			// Sanity check
			if (viewIndex < 0 || viewIndex >= this.views.length || viewIndex == this.currentPage) {
				return;
			}

			function scroll(){

				// Calculate the views to be scrolled
				var contentContainer = self._contentContainer,
					currentPage = self.currentPage,
					destination = -self.views[viewIndex]._measuredLeft,
					i;

					// Calculate a weighted duration so that larger views take longer to scroll.
					duration = 400 + 0.3 * (Math.abs(viewIndex - self.currentPage) * contentContainer._measuredWidth);

				// Make the views that will be seen visible
				if (currentPage < viewIndex) {
					for(i = currentPage + 1; i <= viewIndex; i++) {
						self._showView(i);
					}
				} else {
					for(i = viewIndex; i < currentPage; i++) {
						self._showView(i);
					}
				}

				// Animate the views
				self._updatePagingControl(viewIndex);
				self._animateToPosition(destination, 0, duration, "ease-in-out", function(){
					self.properties.__values__.currentPage = viewIndex;
					if (currentPage < viewIndex) {
						for(i = currentPage; i < viewIndex; i++) {
							self._hideView(i);
						}
					} else {
						for(i = viewIndex + 1; i <= currentPage; i++) {
							self._hideView(i);
						}
					}
					if (self._viewToRemoveAfterScroll !== -1) {
						destination += self.views[self._viewToRemoveAfterScroll]._measuredWidth;
						self._removeViewFromList(self._viewToRemoveAfterScroll);
						self._viewToRemoveAfterScroll = -1;
					}
					self.fireEvent("scrollEnd",{
						currentPage: viewIndex,
						view: self.views[viewIndex]
					});
				});
			}

			// If the scrollableView hasn't been laid out yet, we must wait until it is
			if (self._contentContainer.domNode.offsetWidth) {
				scroll();
			} else {
				once("postlayout", scroll);
			}
		},

		_showPagingControl: function() {
			if (!this.showPagingControl) {
				this._pagingControlContainer.opacity = 0;
				return;
			}
			if (this._isPagingControlActive) {
				return;
			}
			this._isPagingControlActive = true;
			this._pagingControlContainer.animate({
				duration: 250,
				opacity: 0.75
			});
			this.pagingControlTimeout > 0 && setTimeout(lang.hitch(this,function() {
				this._pagingControlContainer.animate({
					duration: 750,
					opacity: 0
				});
				this._isPagingControlActive = false;
			}),this.pagingControlTimeout);
		},

		_updatePagingControl: function(newIndex, hidePagingControl) {
			if (this.showPagingControl) {
				this._pagingControlContentContainer._removeAllChildren();
				var diameter = this.pagingControlHeight / 2;
				for (var i = 0; i < this.views.length; i++) {
					var indicator = UI.createView({
						width: diameter,
						height: diameter,
						left: 5,
						right: 5,
						backgroundColor: i === newIndex ? "white" : "grey",
						borderRadius: unitize(diameter / 2)
					});
					this._pagingControlContentContainer._add(indicator);
				}
				!hidePagingControl && this._showPagingControl();
			}
		},

		_defaultWidth: UI.FILL,

		_defaultHeight: UI.FILL,

		properties: {
			clipViews: {

			},
			currentPage: {
				set: function(value, oldValue) {
					if (value >= 0 && value < this.views.length) {
						this.scrollToView(value);
						return value;
					}
					return oldValue;
				}
			},
			pagingControlColor: {
				set: function(value) {
					this._pagingControlContainer.backgroundColor = value;
					return value;
				},
				value: "black"
			},
			pagingControlHeight: {
				set: function(value) {
					this._pagingControlContainer.height = value;
					return value;
				},
				value: 20
			},
			pagingControlTimeout: {
				set: function(value) {
					this.pagingControlTimeout == 0 && this._updatePagingControl();
					return value;
				},
				value: 1250
			},
			showPagingControl: {
				set: function(value) {
					this.pagingControlTimeout == 0 && this._updatePagingControl();
					return value;
				},
				value: false
			},
			views: {
				set: function(value) {

					// Value must be an array
					if (!is(value,"Array")) {
						return;
					}

					// Add the views to the content container
					var i = 0,
						len = value.length,
						contentContainer = this._contentContainer,
						view;
					contentContainer._removeAllChildren();
					for(; i < len; i++) {
						(view = value[i]).width = "100%";
						view.height = "100%";
						contentContainer._add(view);
					}

					return value;
				},
				post: function() {
					this._updatePagingControl(this.currentPage,true);
				}
			}
		}

	});

});<|endoftext|>


<commit_before>import React, { Component } from 'react';

import { RadioCheckGroup }  from '../../../../index';

export default class RadioCheckGroupSection extends Component {

  constructor(props){
    super(props);
    this.state = {
      checkboxOptions         : {"tea":"default","coffee":"disabled","soda":"","water":"disabled"},
      checkboxSelectedOptions : ["coffee","water","tea"],
      radioOptions1           : {"tea":"default","coffee":"disabled","soda":"","water":"disabled"},
      radioSelectedOptions1   : ["coffee"],
      radioOptions2           : {"tea":"default","coffee":"","soda":"","water":""},
      radioSelectedOptions2   : ["tea"]
    };

    this.radioHandler1   = _radioHandler1.bind(this);
    this.radioHandler2   = _radioHandler2.bind(this);
    this.checkboxHandler = _checkboxHandler.bind(this);
  }

  render() {

    return (

RadioCheckGroup

Props:

RadioCheckGroup:

inputType:String(required)       === "radio or checkbox"

id:String(required)              === "a unique name"

legendText:String(required)      === "a desciptive label"

options:Object(required)         === {'{<option>:<inputState>,<option>:<inputState>, ... }'} where inputState is one of 'default','disabled'.

name:String(required)            === "a desciptive name"

selectedOptions:Array            === "the checked options (radio only supports a single selected option by definition)"

changeHandler:Function(required) === "handles populating the selected options from the options (see source for example https://github.com/Pearson-Higher-Ed/compounds/blob/inputs/demo/demoPages/sections/inputsPageSections/RadioCheckGroupSection.js)"

RadioButton Group (disabled):

          <RadioCheckGroup
            inputType       = "radio"
            id              = "radiobutton1"
            name            = "radiobutton1"
            legendText      = "These are radio buttons"
            options         = {this.state.radioOptions1}
            selectedOptions = {this.state.radioSelectedOptions1}
            changeHandler   = {this.radioHandler1}
            />

{'<RadioCheckGroup inputType="radio" id="radiobutton1" legendText="radiobutton1" options={{"tea":"default","coffee":"disabled","soda":"","water":"disabled"}} selectedOptions={["tea"]} changeHandler={() => {}} />'}

RadioButton Group:

          <RadioCheckGroup
            inputType       = "radio"
            id              = "radiobutton2"
            name            = "radiobutton2"
            legendText      = "These are radio buttons"
            options         = {this.state.radioOptions2}
            selectedOptions = {this.state.radioSelectedOptions2}
            changeHandler   = {this.radioHandler2}
            />

{'<RadioCheckGroup inputType="radio" id="radiobutton2" legendText="radiobutton2" options={{"tea":"default","coffee":"","soda":"","water":""}} selectedOptions={["tea"]} changeHandler={() => {}} />'}

Checkbox Group:

          <RadioCheckGroup
            inputType       = "checkbox"
            id              = "checkbox1"
            name            = "checkbox1"
            legendText      = "These are checkboxes"
            options         = {this.state.checkboxOptions}
            selectedOptions = {this.state.checkboxSelectedOptions}
            changeHandler   = {this.checkboxHandler}
            />

{'<RadioCheckGroup inputType="checkbox" id="checkbox1" legendText="checkbox1" options={{"tea":"default","coffee":"disabled","soda":"","water":"disabled"}} selectedOptions={["coffee","water","tea"]} changeHandler={() => {}} />'}

    )
  }
}

function _checkboxHandler (newSelectionArray) {
  this.setState({ checkboxSelectedOptions: newSelectionArray });
}

function _radioHandler1 (newSelectionArray) {
  this.setState({ radioSelectedOptions1: newSelectionArray });
}

function _radioHandler2 (newSelectionArray) {
  this.setState({ radioSelectedOptions2: newSelectionArray });
}
<commit_msg>Update demo page to reflect RadioCheckGroup changes<commit_after>import React, { Component } from 'react';

import { RadioCheckGroup }  from '../../../../index';

export default class RadioCheckGroupSection extends Component {

  constructor(props) {
    super(props);
    this.state = {
      checkboxOptions         : [{value: 0, label: 'coffee'}, {value: 1, label: 'tea'}, {value: 2, label: 'hot cocoa'}],
      radioOptions1           : [{value: 0, label: 'coffee', checked: true, disabled: true}, {value: 1, label: 'tea', disabled: true}, {value: 2, label: 'hot cocoa', disabled: true}],
      radioOptions2           : [{value: 0, label: 'coffee', checked: true}, {value: 1, label: 'tea'}, {value: 2, label: 'hot cocoa'}]
    };

    this.checkboxHandler = this.checkboxHandler.bind(this);
    this.radioHandler = this.radioHandler.bind(this);
  }

  checkboxHandler (e) {
    const checkboxOptions = this.state.checkboxOptions.map((option) => {
      if (option.value.toString() === e.target.value) {
        return option.checked ? Object.assign(option, { checked: false }) : Object.assign(option, { checked: true });
      }
      return option
    });

    this.setState({
      checkboxOptions
    });
  }

  radioHandler (options) {
    const updatedOptions = (e) => {
      const radioOptions = this.state[options].map((option) => {
        if (option.value.toString() === e.target.value) {
          return Object.assign(option, { checked: true });
        }
        return Object.assign(option, { checked: false });
      });

      this.setState({
        [options]: radioOptions
      });
    }
    return updatedOptions
  }

  render() {

    return (

RadioCheckGroup

Props:

RadioCheckGroup:

inputType:String(required)       === "radio or checkbox"

id:String(required)              === "a unique name"

legendText:String(required)      === "a desciptive label"

options:Array(required)          === "[{'value', 'label', 'checked', 'disabled'}, {'value', 'label', 'checked', 'disabled'}] where checked and disabled take a boolean."

name:String(required)            === "a desciptive name"

changeHandler:Function(required) === "handles populating the selected options from the options (see source for example https://github.com/Pearson-Higher-Ed/compounds/blob/inputs/demo/demoPages/sections/inputsPageSections/RadioCheckGroupSection.js)"

RadioButton Group (disabled):

          <RadioCheckGroup
            inputType       = "radio"
            id              = "radiobutton1"
            name            = "radiobutton1"
            legendText      = "These are radio buttons"
            options         = {this.state.radioOptions1}
            changeHandler   = {this.radioHandler('radioOptions1')}
            />

{'<RadioCheckGroup inputType="radio" id="radiobutton1" legendText="radiobutton1" options={[{value: 0, label: "coffee", checked: true, disabled: true}, {value: 1, label: "tea", disabled: true}, {value: 2, label: "hot cocoa", disabled: true}]} changeHandler={() => {}} />'}

RadioButton Group:

          <RadioCheckGroup
            inputType       = "radio"
            id              = "radiobutton2"
            name            = "radiobutton2"
            legendText      = "These are radio buttons"
            options         = {this.state.radioOptions2}
            changeHandler   = {this.radioHandler('radioOptions2')}
            />

{'<RadioCheckGroup inputType="radio" id="radiobutton2" legendText="radiobutton2" options={[{value: 0, label: "coffee", checked: true}, {value: 1, label: "tea"}, {value: 2, label: "hot cocoa", disabled: true}]} changeHandler={() => {}} />'}

Checkbox Group:

          <RadioCheckGroup
            inputType       = "checkbox"
            id              = "checkbox1"
            name            = "checkbox1"
            legendText      = "These are checkboxes"
            options         = {this.state.checkboxOptions}
            changeHandler   = {this.checkboxHandler}
            />

{'<RadioCheckGroup inputType="checkbox" id="checkbox1" legendText="checkbox1" options={[{value: 0, label: "coffee", checked: true}, {value: 1, label: "tea"}, {value: 2, label: "hot cocoa"}]} changeHandler={() => {}} />'}

    )
  }
}
<|endoftext|>


<commit_before>(function($TD, $TDX){
  //
  // Variable: Collection of all <a> tags.
  //
  var links = document.getElementsByTagName("A");

  //
  // Function: Adds an event listener to all elements in the array or collection.
  //
  var addEventListener = function(collection, type, listener){
    for(let index = 0; index < collection.length; index++){
      collection[index].addEventListener(type, listener);
    }
  };

  //
  // Block: Hook into links to bypass default open function and t.co.
  //
  addEventListener(links, "click", function(e){
    let ele = e.currentTarget;

    $TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
    e.preventDefault();

    if ($TDX.skipOnLinkClick){
      let parentClasses = ele.parentNode.classList;

      if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
        $TD.loadNextNotification();
      }
    }
  });

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  addEventListener(links, "contextmenu", function(e){
    $TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
  });

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    addEventListener(links, "mouseenter", function(e){
      var me = e.currentTarget;

      var url = me.getAttribute("data-full-url");
      return if !url;

      var text = me.textContent;
      return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis

      if ($TDX.expandLinksOnHover){
        tooltipTimer = window.setTimeout(function(){
          me.setAttribute("td-prev-text", text);
          me.innerHTML = url.replace(/^https?:\/\/(www\.)?/, "");
        }, 200);
      }
      else{
        tooltipTimer = window.setTimeout(function(){
          $TD.displayTooltip(url, true);
          tooltipDisplayed = true;
        }, 400);
      }
    });

    addEventListener(links, "mouseleave", function(e){
      return if !e.currentTarget.hasAttribute("data-full-url");

      if ($TDX.expandLinksOnHover){
        var prevText = e.currentTarget.getAttribute("td-prev-text");

        if (prevText){
          e.currentTarget.innerHTML = prevText;
        }
      }

      window.clearTimeout(tooltipTimer);

      if (tooltipDisplayed){
        tooltipDisplayed = false;
        $TD.displayTooltip(null, true);
      }
    });

    addEventListener(links, "mousemove", function(e){
      if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
        var url = e.currentTarget.getAttribute("data-full-url");
        return if !url;

        $TD.displayTooltip(url, true);
        prevMouseX = e.clientX;
        prevMouseY = e.clientY;
      }
    });
  })();

  //
  // Block: Setup a skip button.
  //
  if (!document.body.hasAttribute("td-example-notification")){
    document.body.insertAdjacentHTML("afterbegin", `
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
  <path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
</svg>
`);

    document.getElementById("td-skip").addEventListener("click", function(){
      $TD.loadNextNotification();
    });
  }

  //
  // Block: Setup a hover class on body.
  //
  document.body.addEventListener("mouseenter", function(){
    document.body.classList.add("td-hover");
  });

  document.body.addEventListener("mouseleave", function(){
    document.body.classList.remove("td-hover");
  });
})($TD, $TDX);
<commit_msg>Fix clipboard html styles when copying text from notifications<commit_after>(function($TD, $TDX){
  //
  // Variable: Collection of all <a> tags.
  //
  var links = document.getElementsByTagName("A");

  //
  // Function: Adds an event listener to all elements in the array or collection.
  //
  var addEventListener = function(collection, type, listener){
    for(let index = 0; index < collection.length; index++){
      collection[index].addEventListener(type, listener);
    }
  };

  //
  // Block: Hook into links to bypass default open function and t.co.
  //
  addEventListener(links, "click", function(e){
    let ele = e.currentTarget;

    $TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
    e.preventDefault();

    if ($TDX.skipOnLinkClick){
      let parentClasses = ele.parentNode.classList;

      if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
        $TD.loadNextNotification();
      }
    }
  });

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  addEventListener(links, "contextmenu", function(e){
    $TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
  });

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    addEventListener(links, "mouseenter", function(e){
      var me = e.currentTarget;

      var url = me.getAttribute("data-full-url");
      return if !url;

      var text = me.textContent;
      return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis

      if ($TDX.expandLinksOnHover){
        tooltipTimer = window.setTimeout(function(){
          me.setAttribute("td-prev-text", text);
          me.innerHTML = url.replace(/^https?:\/\/(www\.)?/, "");
        }, 200);
      }
      else{
        tooltipTimer = window.setTimeout(function(){
          $TD.displayTooltip(url, true);
          tooltipDisplayed = true;
        }, 400);
      }
    });

    addEventListener(links, "mouseleave", function(e){
      return if !e.currentTarget.hasAttribute("data-full-url");

      if ($TDX.expandLinksOnHover){
        var prevText = e.currentTarget.getAttribute("td-prev-text");

        if (prevText){
          e.currentTarget.innerHTML = prevText;
        }
      }

      window.clearTimeout(tooltipTimer);

      if (tooltipDisplayed){
        tooltipDisplayed = false;
        $TD.displayTooltip(null, true);
      }
    });

    addEventListener(links, "mousemove", function(e){
      if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
        var url = e.currentTarget.getAttribute("data-full-url");
        return if !url;

        $TD.displayTooltip(url, true);
        prevMouseX = e.clientX;
        prevMouseY = e.clientY;
      }
    });
  })();

  //
  // Block: Work around clipboard HTML formatting.
  //
  document.addEventListener("copy", function(e){
    window.setTimeout($TD.fixClipboard, 0);
  });

  // Block: Setup a skip button.
  //
  if (!document.body.hasAttribute("td-example-notification")){
    document.body.insertAdjacentHTML("afterbegin", `
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
  <path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
</svg>
`);

    document.getElementById("td-skip").addEventListener("click", function(){
      $TD.loadNextNotification();
    });
  }

  //
  // Block: Setup a hover class on body.
  //
  document.body.addEventListener("mouseenter", function(){
    document.body.classList.add("td-hover");
  });

  document.body.addEventListener("mouseleave", function(){
    document.body.classList.remove("td-hover");
  });
})($TD, $TDX);
<|endoftext|>


<commit_before>exports.packages = {
	properties : {
		backgroundColor : 'white'

	},
	childTemplates : [{
		type : 'Ti.UI.ImageView',
		bindId : 'pic_url',
		properties : {
			width : '100dip',
			height : 'auto',
			top : '10dip',
			left : '-10dip'
		}
	}, {
		type : 'Ti.UI.View',
		properties : {
			width : Ti.UI.FILL,
			layout : 'vertical',
			left : '90dip',
			height : Ti.UI.SIZE,
			top : '5dip',
			bottom : '5dip',
			right : '20dip',
		},
		childTemplates : [{
			type : 'Ti.UI.Label',
			bindId : 'package_name',

			properties : {
				color : '#222',
				width : Ti.UI.FILL,
				height : Ti.UI.SIZE,
				font : {
					fontSize : '18dip',
					fontWeight : 'bold',
					fontFamily : 'OpenBaskerville0.0.75'
				},
				left : 0,
				top : 0
			}
		}, {
			type : 'Ti.UI.Label',
			bindId : 'meta_text',

			properties : {
				color : '#070',
				height : Ti.UI.SIZE,
				font : {
					fontSize : '13dip',
					fontFamily : 'OpenBaskerville0.0.75'
				},
				left : 0,
				top : '5dip'
			}
		}]
	}]
};

<commit_msg>Template<commit_after>exports.packages = {
	properties : {
		backgroundColor : 'white'
	},
	childTemplates : [{
		type : 'Ti.UI.ImageView',
		bindId : 'pic_url',
		properties : {
			width : '100dip',
			height : 'auto',
			top : '10dip',
			left : '-10dip'
		}
	}, {
		type : 'Ti.UI.View',
		properties : {
			width : Ti.UI.FILL,
			layout : 'vertical',
			left : '90dip',
			height : Ti.UI.SIZE,
			top : '5dip',
			bottom : '5dip',
			right : '20dip',
		},
		childTemplates : [{
			type : 'Ti.UI.Label',
			bindId : 'package_name',

			properties : {
				color : '#222',
				width : Ti.UI.FILL,
				height : Ti.UI.SIZE,
				font : {
					fontSize : '18dip',
					fontWeight : 'bold',
					fontFamily : 'OpenBaskerville0.0.75'
				},
				left : 0,
				top : 0
			}
		}, {
			type : 'Ti.UI.Label',
			bindId : 'meta_text',

			properties : {
				color : '#070',
				height : Ti.UI.SIZE,
				font : {
					fontSize : '13dip',
					fontFamily : 'OpenBaskerville0.0.75'
				},
				left : 0,
				top : '5dip'
			}
		}]
	}]
};

<|endoftext|>


<commit_before>/**
 * @author pengys5
 */
define(["jquery", "vue", "moment", "text!timeAxisHtml", "rangeSlider", "daterangepicker", "timers"], function ($, Vue, moment, timeAxisHtml, ionRangeSlider, daterangepicker) {
    var vueData = {
        timeBucket: 0,
        hasAutoUpdate: false,
        handler: undefined,
        starting: true,
    };
    var vue;
    var slider;
    var dateFormat = "YYYYMMDDHHmm";
    var timeBucket;
    var sliderFormat = "MM/DD HH:mm";
    var subtract = "hours";
    var subtractValue = 1;
    var timeBucketType = "minute";

    function load() {
        $.ajaxSettings.async = false;
        $.getJSON("/time/sync/allInstance", function (data) {
            vueData.timeBucket = data.timeBucket;
            timeBucket = data.timeBucket;
        });
        return this;
    }

    function minute() {
        dateFormat = "YYYYMMDDHHmm";
        sliderFormat = "MM/DD HH:mm";
        subtract = "hours";
        subtractValue = 1;
        timeBucketType = "minute";
        return this;
    }

    function second() {
        dateFormat = "YYYYMMDDHHmmss";
        sliderFormat = "MM/DD HH:mm:ss";
        subtract = "minutes";
        subtractValue = 3;
        timeBucketType = "second";
        return this;
    }

    function autoUpdate() {
        vueData.hasAutoUpdate = true;
        $('body').everyTime('2s', function () {
            timeBucket = moment(timeBucket, "YYYYMMDDHHmmss").add(2, "seconds").format("YYYYMMDDHHmmss");
            vueData.timeBucket = moment(timeBucket, "YYYYMMDDHHmmss").format(dateFormat);
            console.log("timeBucket : " + timeBucket + " vue time bucket : " + vueData.timeBucket);

            var endTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").format(dateFormat);
            var startTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").subtract(subtractValue, subtract).format(dateFormat);
            updateTimeAxis(startTimeStr, endTimeStr);
        });
        return this;
    }

    function stopAutoUpdate() {
        $('body').stopTime();
    }

    function render(renderToDivId) {
        $("#" + renderToDivId).html(timeAxisHtml);
        var endTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").format(dateFormat);
        var startTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").subtract(subtractValue, subtract).format(dateFormat);

        $("#timeAxisInput").ionRangeSlider({
            min: moment(startTimeStr, dateFormat).format("x"),
            max: moment(endTimeStr, dateFormat).format("x"),
            from: moment(endTimeStr, dateFormat).format("x"),
            grid: true,
            force_edges: true,
            prettify: function (num) {
                var m = moment(num, "x").locale("ru");
                return m.format(sliderFormat);
            },
            onStart: function (data) {
                _callHandler(data);
            },
            onChange: function (data) {
                _callHandler(data);
            },
            onUpdate: function (data) {
                _callHandler(data);
            }
        });
        slider = $("#timeAxisInput").data("ionRangeSlider");

        vue = new Vue({
            el: '#timeAxisButton',
            data: vueData,
            methods: {
                startOrStop: function () {
                    if (vueData.starting) {
                        vueData.starting = false;
                        $("#timeAxisButton").removeClass("fa-pause").addClass("fa-play");
                        stopAutoUpdate();
                    } else {
                        vueData.starting = true;
                        $("#timeAxisButton").removeClass("fa-play").addClass("fa-pause");
                        autoUpdate();
                    }
                }
            }
        });
    }

    function registryTimeChangedHandler(handler) {
        vueData.handler = handler;
        return this;
    }

    function _callHandler(data) {
        var startTime = moment(data.min).format(dateFormat);
        var endTime = moment(data.from).format(dateFormat);
        console.log("startTime: " + startTime + ", endTime: " + endTime);
        vueData.handler(timeBucketType, startTime, endTime);
    }

    function updateTimeAxis(from, to) {
        console.log("update time axis, from:" + from + ", to:" + to);
        slider.update({
            min: moment(from, dateFormat).format("x"),
            max: moment(to, dateFormat).format("x"),
            from: moment(to, dateFormat).format("x")
        });
    }

    function bindDatePicker() {
        $("#dateRangeInput").val(moment().format("MM/DD/YYYY HH:mm"));

        $('#dateRangeInput').daterangepicker({
            singleDatePicker: true,
            timePicker: true,
            timePicker24Hour: true,
            timePickerSeconds: false,
            maxDate: moment(),
            "opens": "left",
            locale: {
                format: 'MM/DD/YYYY HH:mm'
            }
        }, function (start) {
            var endTimeStr = start.format("YYYYMMDDHHmm");
            var startTimeStr = start.subtract(1, "hours").format("YYYYMMDDHHmm");

            updateTimeAxis(startTimeStr, endTimeStr);
        });

        $("#dataRangeBtn").click(function () {
            var drp = $('#dateRangeInput').data('daterangepicker');
            drp.show();
        });
    }

    return {
        load: load,
        render: render,
        autoUpdate: autoUpdate,
        minute: minute,
        second: second,
        registryTimeChangedHandler: registryTimeChangedHandler
    }
});<commit_msg>Fixed https://github.com/wu-sheng/sky-walking/issues/416<commit_after>/**
 * @author pengys5
 */
define(["jquery", "vue", "moment", "text!timeAxisHtml", "rangeSlider", "daterangepicker", "timers"], function ($, Vue, moment, timeAxisHtml, ionRangeSlider, daterangepicker) {
    var vueData = {
        timeBucket: 0,
        hasAutoUpdate: false,
        handler: undefined,
        starting: true,
    };
    var vue;
    var slider;
    var dateFormat = "YYYYMMDDHHmm";
    var sliderFormat = "MM/DD HH:mm";
    var subtract = "hours";
    var subtractValue = 1;
    var timeBucketType = "minute";
    var timeDifferent = 0;

    function load() {
        $.ajaxSettings.async = false;
        $.getJSON("/time/sync/allInstance", function (data) {
            timeDifferent = moment().format("x") - moment(data.timeBucket, "YYYYMMDDHHmmss").format("x");
            console.log("timeDifferent: " + timeDifferent);
        });
        return this;
    }

    function _calCurrentTimeBucket() {
        return moment(moment().format("x") - timeDifferent, "x");
    }

    function minute() {
        dateFormat = "YYYYMMDDHHmm";
        sliderFormat = "MM/DD HH:mm";
        subtract = "hours";
        subtractValue = 1;
        timeBucketType = "minute";
        return this;
    }

    function second() {
        dateFormat = "YYYYMMDDHHmmss";
        sliderFormat = "MM/DD HH:mm:ss";
        subtract = "minutes";
        subtractValue = 3;
        timeBucketType = "second";
        return this;
    }

    function autoUpdate() {
        vueData.hasAutoUpdate = true;
        $('body').everyTime('2s', function () {
            var bucketMoment = _calCurrentTimeBucket();
            vueData.timeBucket = bucketMoment.format("YYYYMMDDHHmmss");
            console.log("auto update, end time: " + vueData.timeBucket);
            var endTimeStr = bucketMoment.format(dateFormat);
            var startTimeStr = bucketMoment.subtract(subtractValue, subtract).format(dateFormat);
            updateTimeAxis(startTimeStr, endTimeStr);
        });
        return this;
    }

    function stopAutoUpdate() {
        $('body').stopTime();
    }

    function render(renderToDivId) {
        $("#" + renderToDivId).html(timeAxisHtml);
        var endTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").format(dateFormat);
        var startTimeStr = moment(vueData.timeBucket, "YYYYMMDDHHmmss").subtract(subtractValue, subtract).format(dateFormat);

        $("#timeAxisInput").ionRangeSlider({
            min: moment(startTimeStr, dateFormat).format("x"),
            max: moment(endTimeStr, dateFormat).format("x"),
            from: moment(endTimeStr, dateFormat).format("x"),
            grid: true,
            force_edges: true,
            prettify: function (num) {
                var m = moment(num, "x").locale("ru");
                return m.format(sliderFormat);
            },
            onStart: function (data) {
                _callHandler(data);
            },
            onChange: function (data) {
                _callHandler(data);
            },
            onUpdate: function (data) {
                _callHandler(data);
            }
        });
        slider = $("#timeAxisInput").data("ionRangeSlider");

        vue = new Vue({
            el: '#timeAxisButton',
            data: vueData,
            methods: {
                startOrStop: function () {
                    if (vueData.starting) {
                        vueData.starting = false;
                        $("#timeAxisButton").removeClass("fa-pause").addClass("fa-play");
                        stopAutoUpdate();
                    } else {
                        vueData.starting = true;
                        $("#timeAxisButton").removeClass("fa-play").addClass("fa-pause");
                        autoUpdate();
                    }
                }
            }
        });
    }

    function registryTimeChangedHandler(handler) {
        vueData.handler = handler;
        return this;
    }

    function _callHandler(data) {
        var startTime = moment(data.min).format(dateFormat);
        var endTime = moment(data.from).format(dateFormat);
        console.log("startTime: " + startTime + ", endTime: " + endTime);
        vueData.handler(timeBucketType, startTime, endTime);
    }

    function updateTimeAxis(from, to) {
        console.log("update time axis, from:" + from + ", to:" + to);
        slider.update({
            min: moment(from, dateFormat).format("x"),
            max: moment(to, dateFormat).format("x"),
            from: moment(to, dateFormat).format("x")
        });
    }

    function bindDatePicker() {
        $("#dateRangeInput").val(moment().format("MM/DD/YYYY HH:mm"));

        $('#dateRangeInput').daterangepicker({
            singleDatePicker: true,
            timePicker: true,
            timePicker24Hour: true,
            timePickerSeconds: false,
            maxDate: moment(),
            "opens": "left",
            locale: {
                format: 'MM/DD/YYYY HH:mm'
            }
        }, function (start) {
            var endTimeStr = start.format("YYYYMMDDHHmm");
            var startTimeStr = start.subtract(1, "hours").format("YYYYMMDDHHmm");

            updateTimeAxis(startTimeStr, endTimeStr);
        });

        $("#dataRangeBtn").click(function () {
            var drp = $('#dateRangeInput').data('daterangepicker');
            drp.show();
        });
    }

    return {
        load: load,
        render: render,
        autoUpdate: autoUpdate,
        minute: minute,
        second: second,
        registryTimeChangedHandler: registryTimeChangedHandler
    }
});<|endoftext|>


<commit_before>var win = Titanium.UI.currentWindow;

var uploadGPS = '';
var latitude;
var longitude;
var coordinates = 'coordinates';
Titanium.Geolocation.purpose = "Recieve User Location";
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
Titanium.Geolocation.distanceFilter = 10;

//Creation of a new Directory
var newDir = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,'mydir');
newDir.createDirectory();
Titanium.API.info('Path to newdir: ' + newDir.nativePath);

var gps_recorded = Titanium.Filesystem.getFile(newDir.nativePath, 'coordinates');
var uploadGPS = gps_recorded.read();

//
// Geolocation Text
//

var updatedLocationLabel = Titanium.UI.createLabel({
	text:'Updated Location',
	font:{fontSize:12, fontWeight:'bold'},
	color:'#111',
	top:280, //Base number
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLocationLabel);

var updatedLocation = Titanium.UI.createLabel({
	text:'Updated Location not fired',
	font:{fontSize:11},
	color:'#444',
	top:300, // + 20 difference
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLocation);

var updatedLatitude = Titanium.UI.createLabel({
	text:'',
	font:{fontSize:11},
	color:'#444',
	top:320, // + 40 difference
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLatitude);

//
// Get current position - This fires once
//

Titanium.Geolocation.getCurrentPosition(function(e){
		if (!e.success || e.error)
		{
			currentLocation.text = 'error: ' + JSON.stringify(e.error);
			alert('error ' + JSON.stringify(e.error));
			return;
		}
		var longitude = e.coords.longitude;
		var latitude = e.coords.latitude;

		//Titanium.App.Properties.setDouble('longitde',longitude);
		//Titanium.App.Properties.setDouble('latitude',latitude);

		//Establishes a JSON array
		var datatoWrite = {"usersgps" : [{"latitude":latitude}, {"longitude":longitude}]};
		//Data to write?
		var newFile = Titanium.Filesystem.getFile(newDir.nativePath,'coordinates');
		newFile.write(JSON.stringify(datatoWrite));
});
Titanium.Geolocation.addEventListener('location', function(e){
		if (!e.success || e.error)
		{
			updatedLocation.text = 'error:' + JSON.stringify(e.error);
			updatedLatitude.text = '';
			updatedLocationAccuracy.text = '';
			updatedLocationTime.text = '';
			return;
		}
		longitude = e.coords.longitude;
		latitude = e.coords.latitude;
		updatedLocation.text = 'long:' + longitude;
		updatedLatitude.text = 'lat: '+ latitude;

		//Titanium.App.Properties.setDouble('longitde',longitude);
		//Titanium.App.Properties.setDouble('latitude',latitude);

		var datatoWrite = {"usersgps" : [{"latitude":latitude}, {"longitude":longitude}]};

		//Data to write? This is apparent overkill
		var newFile = Titanium.Filesystem.getFile(newDir.nativePath,'coordinates');
		newFile.write(JSON.stringify(datatoWrite));
});

//var datatoWrite = {"latitude":latitude, "longitude":longitude};
//datatoWrite.write(JSON.stringify(datatoWrite));

var gps_coordinates = {
	"coords": uploadGPS,
	"name": coordinates
};

//
//Button - Coords Upload
//

var upload_coords = Titanium.UI.createButton({
	title:'Coords Upload',
	height:40,
	width:145,
	right:80,
	top:220
});
win.add(upload_coords);
	upload_coords.addEventListener('click', function(e) {
		var xhr = Titanium.Network.createHTTPClient();
		xhr.onerror = function(e)
		{
			Titanium.UI.createAlertDialog({title:'Error', message:e.error}).show();
			Titanium.API.info('IN ERROR' + e.error);
		};
		xhr.setTimeout(20000);
		xhr.onload = function(e)
		{
			Titanium.UI.createAlertDialog({title:'Success', message:'status code ' + this.status}).show();
			Titanium.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState);
		};
		xhr.onsendstream = function(e)
		{
			Titanium.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress);
		};
		//open the client
		xhr.open('POST', 'http://localhost/gps_audio.php', false);
		xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
		xhr.send(gps_coordinates);
	});
<commit_msg>Trying to fix the JSON array<commit_after>var win = Titanium.UI.currentWindow;

var uploadGPS = '';
var latitude;
var longitude;
var coordinates = 'coordinates';
Titanium.Geolocation.purpose = "Recieve User Location";
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
Titanium.Geolocation.distanceFilter = 10;

//Creation of a new Directory
var newDir = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,'mydir');
newDir.createDirectory();
Titanium.API.info('Path to newdir: ' + newDir.nativePath);

var gps_recorded = Titanium.Filesystem.getFile(newDir.nativePath, 'coordinates');
var uploadGPS = gps_recorded.read();
Titanium.API.info(uploadGPS);
//
// Geolocation Text
//

var updatedLocationLabel = Titanium.UI.createLabel({
	text:'Updated Location',
	font:{fontSize:12, fontWeight:'bold'},
	color:'#111',
	top:280, //Base number
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLocationLabel);

var updatedLocation = Titanium.UI.createLabel({
	text:'Updated Location not fired',
	font:{fontSize:11},
	color:'#444',
	top:300, // + 20 difference
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLocation);

var updatedLatitude = Titanium.UI.createLabel({
	text:'',
	font:{fontSize:11},
	color:'#444',
	top:320, // + 40 difference
	left:100, //same
	height:15, //same
	width:300 //same
});
win.add(updatedLatitude);

//
// Get current position - This fires once
//

Titanium.Geolocation.getCurrentPosition(function(e){
		if (!e.success || e.error)
		{
			currentLocation.text = 'error: ' + JSON.stringify(e.error);
			alert('error ' + JSON.stringify(e.error));
			return;
		}
		var longitude = e.coords.longitude;
		var latitude = e.coords.latitude;

		//Titanium.App.Properties.setDouble('longitde',longitude);
		//Titanium.App.Properties.setDouble('latitude',latitude);

		//Establishes a JSON array
		var datatoWrite = {"usersgps" :
									{
									"latitude":latitude,
									"longitude":longitude
									}
										};

		//Data to write?
		var newFile = Titanium.Filesystem.getFile(newDir.nativePath,'coordinates');
		newFile.write(JSON.stringify(datatoWrite));
});
Titanium.Geolocation.addEventListener('location', function(e){
		if (!e.success || e.error)
		{
			updatedLocation.text = 'error:' + JSON.stringify(e.error);
			updatedLatitude.text = '';
			updatedLocationAccuracy.text = '';
			updatedLocationTime.text = '';
			return;
		}
		longitude = e.coords.longitude;
		latitude = e.coords.latitude;
		updatedLocation.text = 'long:' + longitude;
		updatedLatitude.text = 'lat: '+ latitude;

		//Titanium.App.Properties.setDouble('longitde',longitude);
		//Titanium.App.Properties.setDouble('latitude',latitude);

		var datatoWrite = {"usersgps" :
									{
									"latitude":latitude,
									"longitude":longitude
									}
										};

		//Data to write? This is apparent overkill
		var newFile = Titanium.Filesystem.getFile(newDir.nativePath,'coordinates');
		newFile.write(JSON.stringify(datatoWrite));
});

//var datatoWrite = {"latitude":latitude, "longitude":longitude};
//datatoWrite.write(JSON.stringify(datatoWrite));

var gps_coordinates = {
	"coords": uploadGPS,
	"name": coordinates
};

//
//Button - Coords Upload
//

var upload_coords = Titanium.UI.createButton({
	title:'Coords Upload',
	height:40,
	width:145,
	right:80,
	top:220
});
win.add(upload_coords);
	upload_coords.addEventListener('click', function(e) {
		var xhr = Titanium.Network.createHTTPClient();
		xhr.onerror = function(e)
		{
			Titanium.UI.createAlertDialog({title:'Error', message:e.error}).show();
			Titanium.API.info('IN ERROR' + e.error);
		};
		xhr.setTimeout(20000);
		xhr.onload = function(e)
		{
			Titanium.UI.createAlertDialog({title:'Success', message:'status code ' + this.status}).show();
			Titanium.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState);
		};
		xhr.onsendstream = function(e)
		{
			Titanium.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress);
		};
		//open the client
		xhr.open('POST', 'http://localhost/gps_audio.php', false);
		xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
		xhr.send(gps_coordinates);
	});
<|endoftext|>


<commit_before>var AD = require('AppDev');
var $ = require('jquery');

module.exports = $.Window('AppDev.UI.ViewContactWindow', {
    dependencies: ['AddContactWindow', 'DatePickerWindow', 'Checkbox'],

    contactMethods: [
        {label: 'contact_call', callback: 'callContact', field: 'contact_phone'},
        {label: 'contact_SMS', callback: 'SMSContact', field: 'contact_phone'},
        {label: 'contact_email', callback: 'emailContact', field: 'contact_email'}
    ],
    actions: [{
        title: 'del',
        callback: function() {
            AD.UI.yesNoAlert('contactDeleteConfirmation').done(this.proxy(function() {
                // The user chose "Yes", so close the window and delete the contact
                this.dfd.reject();
                this.contact.destroy();
            }));
        },
        platform: 'Android',
        showAsAction: true,
        icon: '/images/ic_action_discard.png'
    }, {
        title: 'edit',
        callback: function() {
            // Open the EditContact window
            var _this = this;
            var $winEditContact = this.createWindow('AddContactWindow', {
                operation: 'edit',
                existingContact: this.contact
            });
            $winEditContact.getDeferred().done(function() {
                // Update the steps view after the contact is edited
                _this.updateSteps();
            });
        },
        rightNavButton: true,
        showAsAction: true,
        icon: '/images/ic_action_edit.png'
    }]
}, {
    init: function(options) {
        this.contact = this.options.contact;

        // Initialize the base $.Window object
        this._super({
            title: 'viewContact',
            autoOpen: true,
            createParams: {
                layout: 'vertical'
            }
        });

        this.smartBind(this.contact, 'updated.attr', function(property, value) {
            // Re-initialize the name label and contact buttons
            this.initialize();

            // Simulate a global model 'updated' event
            $(this.contact.constructor).trigger('updated', this.contact);
        });
    },

    // Create the child views
    create: function() {
        var contact = this.contact;

        // Create a container to hold the label and optional image
        var headerView = this.add(Ti.UI.createView({
            left: AD.UI.padding,
            top: AD.UI.padding,
            width: AD.UI.useableScreenWidth,
            height: Ti.UI.SIZE
        }));

        // Show the contact's image if it exists
        var localContact = contact.contact_recordId === null ? null : Ti.Contacts.getPersonByID(contact.contact_recordId);
        var contactImage = localContact && localContact.getImage();
        var hasImage = contactImage ? true : false;
        if (hasImage) {
            var dimensions = AD.UI.getImageScaledDimensions(contactImage, AD.UI.contactImageSize);
            headerView.add(Ti.UI.createImageView({
                left: 0,
                width: dimensions.width,
                height: dimensions.height,
                image: contactImage
            }));
        }

        // Create the contact label
        headerView.add(this.record('nameLabel', Ti.UI.createLabel({
            left: hasImage ? AD.UI.contactImageSize.width : 0,
            top: 0,
            text: null,
            textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
            width: Ti.UI.FILL,
            font: AD.UI.Fonts.header
        })));

        // Create the contact button bar which allows the user to call, SMS, or e-mail the contact
        if (AD.Platform.isiOS) {
            // Create a button bar under iOS
            this.contactBBLabels = this.constructor.contactMethods.map(function(method) {
                return $.extend({
                    title: AD.Localize(method.label)
                }, method);
            });
            var contactBB = this.add('contactBB', Ti.UI.createButtonBar({
                left: AD.UI.padding,
                top: AD.UI.padding * 2,
                width: AD.UI.useableScreenWidth,
                height: AD.UI.buttonHeight,
                style: Ti.UI.iPhone.SystemButtonStyle.BAR,
                labels: this.contactBBLabels
            }));
            contactBB.addEventListener('click', this.proxy(function(event) {
                var callbackName = this.constructor.contactMethods[event.index].callback;
                // callbackName will be undefined if event.index is null
                // because the user clicked the button bar, but not a button
                if (typeof callbackName !== 'undefined') {
                    // Determine the proper callback to use and call it
                    var callback = $.isFunction(callback) ? callback : this[callbackName];
                    callback.call(this);
                }
            }));
        }
        else {
            // Create multiple buttons under any other platform

            // The buttons are spaced evenly and horizontally with AD.UI.padding units of padding between them
            var buttonCount = this.constructor.contactMethods.length;
            var buttonWidth = (AD.UI.useableScreenWidth - (buttonCount - 1) * AD.UI.padding) / buttonCount;
            var contactButtonView = this.add(Ti.UI.createView({
                left: AD.UI.padding,
                top: AD.UI.padding * 2,
                width: AD.UI.useableScreenWidth,
                height: AD.UI.buttonHeight
            }));
            this.constructor.contactMethods.forEach(function(method, index) {
                var buttonParams = $.extend({
                    left: (buttonWidth + AD.UI.padding) * index,
                    width: buttonWidth,
                    height: AD.UI.buttonHeight,
                    titleid: method.label
                }, method);
                var button = this.record(method.label, Ti.UI.createButton(buttonParams));
                contactButtonView.add(button);
                button.addEventListener('click', this.proxy(method.callback));
            }, this);
        }

        // Create the tags row
        var _this = this;
        var tagsRow = this.add('tags', this.createRow());
        tagsRow.top = AD.UI.padding;
        tagsRow.add(Ti.UI.createImageView({
            left: AD.UI.padding,
            width: Ti.UI.SIZE,
            height: Ti.UI.SIZE,
            image: '/images/tags.png'
        }));
        var tagsLabel = Ti.UI.createLabel({
            left: AD.UI.padding * 2 + 25,
            right: AD.UI.padding,
            top: 0,
            height: Ti.UI.FILL,
            font: AD.UI.Fonts.mediumSmall
        });
        var updateTagLabel = function() {
            // Get an array of the lab
            var tagLabels = contact.getTags().map(function(tag) { return tag.attr('tag_label'); });
            tagsLabel.text = (tagLabels.join(', ') || AD.Localize('none'));
        };
        updateTagLabel();
        tagsRow.add(tagsLabel);
        tagsRow.addEventListener('click', function() {
            _this.createWindow('ChooseOptionsWindow', {
                groupName: 'tag',
                Model: 'Tag',
                initial: contact.getTags().map(function(tag) { return tag.attr('tag_uuid'); }),
                editable: true
            }).getDeferred().done(function(options) {
                contact.setTags(options);
                updateTagLabel();
            });
        });

        // Create the campus steps view
        var scrollableStepsView = Ti.UI.createScrollView({
            left: 0,
            top: 0,
            layout: 'vertical',
            scrollType: 'vertical',
            contentHeight: 'auto',
            showVerticalScrollIndicator: true
        });
        this.add(scrollableStepsView);

        var $stepsView = this.record('steps', $.View.create(Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            layout: 'vertical'
        })));
        scrollableStepsView.add($stepsView.getView());

        var personalStepsHeaderRow = Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            backgroundColor: 'lightgray'
        });
        personalStepsHeaderRow.add(Ti.UI.createLabel({
            left: AD.UI.padding,
            top: AD.UI.padding,
            bottom: AD.UI.padding,
            width: Ti.UI.SIZE,
            height: Ti.UI.SIZE,
            textid: 'personalSteps'
        }));
        personalStepsHeaderRow.addEventListener('click', function() {
            _this.addPersonalSteps();
        });
        scrollableStepsView.add(personalStepsHeaderRow);

        // Create the personal steps view
        var $personalStepsView = this.record('personalSteps', $.View.create(Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            layout: 'vertical'
        })));
        scrollableStepsView.add($personalStepsView.getView());

        this.updateSteps();
    },

    // Initialize the child views
    initialize: function() {
        this.getChild('nameLabel').text = this.contact.getLabel();

        // Update the enabled status of each of the contact buttons

        // Extract the button array
        var buttons = AD.Platform.isiOS ? this.contactBBLabels : this.constructor.contactMethods.map(function(method) {
            return this.getChild(method.label);
        }, this);
        buttons.forEach(function(button) {
            button.enabled = this.contact.attr(button.field) ? true : false;
        }, this);
        if (AD.Platform.isiOS) {
            // Force the button bar to recognize the new button states
            this.getChild('contactBB').labels = buttons;
        }
    },

    // Create and return a basic UI element representing a row
    createRow: function() {
        return Ti.UI.createView({
            left: 0,
            top: 0,
            height: AD.UI.buttonHeight,
            borderWidth: 1,
            borderColor: 'black'
        });
    },

    // (Re)create the steps UI
    updateSteps: function() {
        // Remove the steps view in order to replace it
        var $stepsView = this.get$Child('steps');
        var $personalStepsView = this.get$Child('personalSteps');

        $stepsView.removeAllChildren();
        $personalStepsView.removeAllChildren();

        // Display all the steps associated with this contact's campus
        // and all the completed steps, regardless of their campus
        var steps = this.contact.getSteps();
        var stepsLength = steps.length;
        steps.forEach(function(contactStep) {
            var $stepView = this.createStepRow(contactStep);
            if (contactStep.attr('campus_uuid')) {
                $stepsView.add($stepView);
            }
            else {
                $personalStepsView.add($stepView);
            }
        }, this);
    },

    createStepRow: function(contactStep) {
        // Lookup the associated ContactStep
        var step_uuid = contactStep.attr('step_uuid');
        var _this = this;
        var $newRow = $.View.create(this.createRow());

        // Create the step title
        var stepLabel = $newRow.add(Ti.UI.createLabel({
            left: AD.UI.padding,
            right: AD.UI.padding,
            top: 0,
            height: Ti.UI.FILL,
            text: contactStep.getLabel(),
            font: AD.UI.Fonts.mediumSmall
        }));

        var stepCompletedDate = contactStep.attr('step_date');
        var stepCompleted = stepCompletedDate !== null;

        // Create the switch to toggle the step's completion status
        var $completedCheckbox = new AD.UI.Checkbox({
            createParams: {
                right: AD.UI.padding
            },
            value: stepCompleted
        });
        var completedCheckbox = $completedCheckbox.getView();
        completedCheckbox.addEventListener('change', function(event) {
            // The step's completion state has been changed
            stepCompleted = event.value;
            stepCompletedDate = stepCompleted ? $.today() : null;
            contactStep.attr('step_date', stepCompletedDate).save();
            updateRow();
        });
        $newRow.add($completedCheckbox);

        // Create the button to set the step completion date
        var dateButton = Ti.UI.createButton({
            right: AD.UI.Checkbox.defaultSize + AD.UI.padding * 2,
            width: AD.Platform.isiOS ? 90 : 120,
            height: AD.UI.buttonHeight,
            title: ''
        });
        dateButton.addEventListener('click', function() {
            // Set the completion date of the step
            _this.createWindow('DatePickerWindow', {
                minDate: new Date(2012, 0, 1), // January 1, 2012
                maxDate: $.today(),
                initialDate: stepCompletedDate
            }).getDeferred().done(function(completedDate) {
                stepCompletedDate = completedDate;
                contactStep.attr('step_date', stepCompletedDate).save();
                updateRow();
            });
        });
        $newRow.add(dateButton);

        // Update the checkbox image and the title and visibility of the dateButton
        var updateRow = function() {
            if (stepCompleted) {
                dateButton.visible = true;
                dateButton.title = $.formatDate(stepCompletedDate);
            }
            else {
                dateButton.visible = false;
            }
            stepLabel.right = AD.UI.padding + (dateButton.visible ? (dateButton.right + dateButton.width) : (completedCheckbox.right + completedCheckbox.width));
        };
        updateRow();

        return $newRow;
    },

    // Helper functions that allow the user to contact the contact via telephone, SMS, or e-mail
    callContact: function() {
        // Remove all non-numeric numbers from the phone number
        var strippedPhoneNumber = this.contact.contact_phone.replace(/\D/g, '');
        Ti.Platform.openURL('tel:'+strippedPhoneNumber); // tel:xxxxxxxx format
        if (AD.Platform.isiOS) {
            Ti.App.iOS.registerBackgroundService({ url: 'Return.js' });
        }
    },
    SMSContact: function() {
        // Remove all non-numeric numbers from the phone number
        var strippedPhoneNumber = this.contact.contact_phone.replace(/\D/g, '');
        Ti.Platform.openURL('sms:'+strippedPhoneNumber); // sms:xxxxxxxx format
    },
    emailContact: function() {
        // Display the email dialog
        var emailDialog = Ti.UI.createEmailDialog({
            toRecipients: [this.contact.contact_email]
        });
        emailDialog.open();
    },

    // Display a window allowing the user to create personal steps and apply them to the current contact
    addPersonalSteps: function() {
        var contact = this.contact;
        var _this = this;
        this.createWindow('ChooseOptionsWindow', {
            groupName: 'personalStep',
            Model: 'Step',
            initial: contact.getPersonalSteps().map(function(step) { return step.attr('step_uuid'); }),
            editable: true
        }).getDeferred().done(function(options) {
            contact.setPersonalSteps(options);
            _this.updateSteps();
        });
    }
});
<commit_msg>Move updateSteps call from create into initialize for consistency<commit_after>var AD = require('AppDev');
var $ = require('jquery');

module.exports = $.Window('AppDev.UI.ViewContactWindow', {
    dependencies: ['AddContactWindow', 'DatePickerWindow', 'Checkbox'],

    contactMethods: [
        {label: 'contact_call', callback: 'callContact', field: 'contact_phone'},
        {label: 'contact_SMS', callback: 'SMSContact', field: 'contact_phone'},
        {label: 'contact_email', callback: 'emailContact', field: 'contact_email'}
    ],
    actions: [{
        title: 'del',
        callback: function() {
            AD.UI.yesNoAlert('contactDeleteConfirmation').done(this.proxy(function() {
                // The user chose "Yes", so close the window and delete the contact
                this.dfd.reject();
                this.contact.destroy();
            }));
        },
        platform: 'Android',
        showAsAction: true,
        icon: '/images/ic_action_discard.png'
    }, {
        title: 'edit',
        callback: function() {
            // Open the EditContact window
            var _this = this;
            var $winEditContact = this.createWindow('AddContactWindow', {
                operation: 'edit',
                existingContact: this.contact
            });
            $winEditContact.getDeferred().done(function() {
                // Update the steps view after the contact is edited
                _this.updateSteps();
            });
        },
        rightNavButton: true,
        showAsAction: true,
        icon: '/images/ic_action_edit.png'
    }]
}, {
    init: function(options) {
        this.contact = this.options.contact;

        // Initialize the base $.Window object
        this._super({
            title: 'viewContact',
            autoOpen: true,
            createParams: {
                layout: 'vertical'
            }
        });

        this.smartBind(this.contact, 'updated.attr', function(property, value) {
            // Re-initialize the name label and contact buttons
            this.initialize();

            // Simulate a global model 'updated' event
            $(this.contact.constructor).trigger('updated', this.contact);
        });
    },

    // Create the child views
    create: function() {
        var contact = this.contact;

        // Create a container to hold the label and optional image
        var headerView = this.add(Ti.UI.createView({
            left: AD.UI.padding,
            top: AD.UI.padding,
            width: AD.UI.useableScreenWidth,
            height: Ti.UI.SIZE
        }));

        // Show the contact's image if it exists
        var localContact = contact.contact_recordId === null ? null : Ti.Contacts.getPersonByID(contact.contact_recordId);
        var contactImage = localContact && localContact.getImage();
        var hasImage = contactImage ? true : false;
        if (hasImage) {
            var dimensions = AD.UI.getImageScaledDimensions(contactImage, AD.UI.contactImageSize);
            headerView.add(Ti.UI.createImageView({
                left: 0,
                width: dimensions.width,
                height: dimensions.height,
                image: contactImage
            }));
        }

        // Create the contact label
        headerView.add(this.record('nameLabel', Ti.UI.createLabel({
            left: hasImage ? AD.UI.contactImageSize.width : 0,
            top: 0,
            text: null,
            textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
            width: Ti.UI.FILL,
            font: AD.UI.Fonts.header
        })));

        // Create the contact button bar which allows the user to call, SMS, or e-mail the contact
        if (AD.Platform.isiOS) {
            // Create a button bar under iOS
            this.contactBBLabels = this.constructor.contactMethods.map(function(method) {
                return $.extend({
                    title: AD.Localize(method.label)
                }, method);
            });
            var contactBB = this.add('contactBB', Ti.UI.createButtonBar({
                left: AD.UI.padding,
                top: AD.UI.padding * 2,
                width: AD.UI.useableScreenWidth,
                height: AD.UI.buttonHeight,
                style: Ti.UI.iPhone.SystemButtonStyle.BAR,
                labels: this.contactBBLabels
            }));
            contactBB.addEventListener('click', this.proxy(function(event) {
                var callbackName = this.constructor.contactMethods[event.index].callback;
                // callbackName will be undefined if event.index is null
                // because the user clicked the button bar, but not a button
                if (typeof callbackName !== 'undefined') {
                    // Determine the proper callback to use and call it
                    var callback = $.isFunction(callback) ? callback : this[callbackName];
                    callback.call(this);
                }
            }));
        }
        else {
            // Create multiple buttons under any other platform

            // The buttons are spaced evenly and horizontally with AD.UI.padding units of padding between them
            var buttonCount = this.constructor.contactMethods.length;
            var buttonWidth = (AD.UI.useableScreenWidth - (buttonCount - 1) * AD.UI.padding) / buttonCount;
            var contactButtonView = this.add(Ti.UI.createView({
                left: AD.UI.padding,
                top: AD.UI.padding * 2,
                width: AD.UI.useableScreenWidth,
                height: AD.UI.buttonHeight
            }));
            this.constructor.contactMethods.forEach(function(method, index) {
                var buttonParams = $.extend({
                    left: (buttonWidth + AD.UI.padding) * index,
                    width: buttonWidth,
                    height: AD.UI.buttonHeight,
                    titleid: method.label
                }, method);
                var button = this.record(method.label, Ti.UI.createButton(buttonParams));
                contactButtonView.add(button);
                button.addEventListener('click', this.proxy(method.callback));
            }, this);
        }

        // Create the tags row
        var _this = this;
        var tagsRow = this.add('tags', this.createRow());
        tagsRow.top = AD.UI.padding;
        tagsRow.add(Ti.UI.createImageView({
            left: AD.UI.padding,
            width: Ti.UI.SIZE,
            height: Ti.UI.SIZE,
            image: '/images/tags.png'
        }));
        var tagsLabel = Ti.UI.createLabel({
            left: AD.UI.padding * 2 + 25,
            right: AD.UI.padding,
            top: 0,
            height: Ti.UI.FILL,
            font: AD.UI.Fonts.mediumSmall
        });
        var updateTagLabel = function() {
            // Get an array of the lab
            var tagLabels = contact.getTags().map(function(tag) { return tag.attr('tag_label'); });
            tagsLabel.text = (tagLabels.join(', ') || AD.Localize('none'));
        };
        updateTagLabel();
        tagsRow.add(tagsLabel);
        tagsRow.addEventListener('click', function() {
            _this.createWindow('ChooseOptionsWindow', {
                groupName: 'tag',
                Model: 'Tag',
                initial: contact.getTags().map(function(tag) { return tag.attr('tag_uuid'); }),
                editable: true
            }).getDeferred().done(function(options) {
                contact.setTags(options);
                updateTagLabel();
            });
        });

        // Create the campus steps view
        var scrollableStepsView = Ti.UI.createScrollView({
            left: 0,
            top: 0,
            layout: 'vertical',
            scrollType: 'vertical',
            contentHeight: 'auto',
            showVerticalScrollIndicator: true
        });
        this.add(scrollableStepsView);

        var $stepsView = this.record('steps', $.View.create(Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            layout: 'vertical'
        })));
        scrollableStepsView.add($stepsView.getView());

        var personalStepsHeaderRow = Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            backgroundColor: 'lightgray'
        });
        personalStepsHeaderRow.add(Ti.UI.createLabel({
            left: AD.UI.padding,
            top: AD.UI.padding,
            bottom: AD.UI.padding,
            width: Ti.UI.SIZE,
            height: Ti.UI.SIZE,
            textid: 'personalSteps'
        }));
        personalStepsHeaderRow.addEventListener('click', function() {
            _this.addPersonalSteps();
        });
        scrollableStepsView.add(personalStepsHeaderRow);

        // Create the personal steps view
        var $personalStepsView = this.record('personalSteps', $.View.create(Ti.UI.createView({
            left: 0,
            top: 0,
            width: Ti.UI.FILL,
            height: Ti.UI.SIZE,
            layout: 'vertical'
        })));
        scrollableStepsView.add($personalStepsView.getView());
    },

    // Initialize the child views
    initialize: function() {
        this.getChild('nameLabel').text = this.contact.getLabel();

        this.updateSteps();

        // Update the enabled status of each of the contact buttons

        // Extract the button array
        var buttons = AD.Platform.isiOS ? this.contactBBLabels : this.constructor.contactMethods.map(function(method) {
            return this.getChild(method.label);
        }, this);
        buttons.forEach(function(button) {
            button.enabled = this.contact.attr(button.field) ? true : false;
        }, this);
        if (AD.Platform.isiOS) {
            // Force the button bar to recognize the new button states
            this.getChild('contactBB').labels = buttons;
        }
    },

    // Create and return a basic UI element representing a row
    createRow: function() {
        return Ti.UI.createView({
            left: 0,
            top: 0,
            height: AD.UI.buttonHeight,
            borderWidth: 1,
            borderColor: 'black'
        });
    },

    // (Re)create the steps UI
    updateSteps: function() {
        // Remove the steps view in order to replace it
        var $stepsView = this.get$Child('steps');
        var $personalStepsView = this.get$Child('personalSteps');

        $stepsView.removeAllChildren();
        $personalStepsView.removeAllChildren();

        // Display all the steps associated with this contact's campus
        // and all the completed steps, regardless of their campus
        var steps = this.contact.getSteps();
        var stepsLength = steps.length;
        steps.forEach(function(contactStep) {
            var $stepView = this.createStepRow(contactStep);
            if (contactStep.attr('campus_uuid')) {
                $stepsView.add($stepView);
            }
            else {
                $personalStepsView.add($stepView);
            }
        }, this);
    },

    createStepRow: function(contactStep) {
        // Lookup the associated ContactStep
        var step_uuid = contactStep.attr('step_uuid');
        var _this = this;
        var $newRow = $.View.create(this.createRow());

        // Create the step title
        var stepLabel = $newRow.add(Ti.UI.createLabel({
            left: AD.UI.padding,
            right: AD.UI.padding,
            top: 0,
            height: Ti.UI.FILL,
            text: contactStep.getLabel(),
            font: AD.UI.Fonts.mediumSmall
        }));

        var stepCompletedDate = contactStep.attr('step_date');
        var stepCompleted = stepCompletedDate !== null;

        // Create the switch to toggle the step's completion status
        var $completedCheckbox = new AD.UI.Checkbox({
            createParams: {
                right: AD.UI.padding
            },
            value: stepCompleted
        });
        var completedCheckbox = $completedCheckbox.getView();
        completedCheckbox.addEventListener('change', function(event) {
            // The step's completion state has been changed
            stepCompleted = event.value;
            stepCompletedDate = stepCompleted ? $.today() : null;
            contactStep.attr('step_date', stepCompletedDate).save();
            updateRow();
        });
        $newRow.add($completedCheckbox);

        // Create the button to set the step completion date
        var dateButton = Ti.UI.createButton({
            right: AD.UI.Checkbox.defaultSize + AD.UI.padding * 2,
            width: AD.Platform.isiOS ? 90 : 120,
            height: AD.UI.buttonHeight,
            title: ''
        });
        dateButton.addEventListener('click', function() {
            // Set the completion date of the step
            _this.createWindow('DatePickerWindow', {
                minDate: new Date(2012, 0, 1), // January 1, 2012
                maxDate: $.today(),
                initialDate: stepCompletedDate
            }).getDeferred().done(function(completedDate) {
                stepCompletedDate = completedDate;
                contactStep.attr('step_date', stepCompletedDate).save();
                updateRow();
            });
        });
        $newRow.add(dateButton);

        // Update the checkbox image and the title and visibility of the dateButton
        var updateRow = function() {
            if (stepCompleted) {
                dateButton.visible = true;
                dateButton.title = $.formatDate(stepCompletedDate);
            }
            else {
                dateButton.visible = false;
            }
            stepLabel.right = AD.UI.padding + (dateButton.visible ? (dateButton.right + dateButton.width) : (completedCheckbox.right + completedCheckbox.width));
        };
        updateRow();

        return $newRow;
    },

    // Helper functions that allow the user to contact the contact via telephone, SMS, or e-mail
    callContact: function() {
        // Remove all non-numeric numbers from the phone number
        var strippedPhoneNumber = this.contact.contact_phone.replace(/\D/g, '');
        Ti.Platform.openURL('tel:'+strippedPhoneNumber); // tel:xxxxxxxx format
        if (AD.Platform.isiOS) {
            Ti.App.iOS.registerBackgroundService({ url: 'Return.js' });
        }
    },
    SMSContact: function() {
        // Remove all non-numeric numbers from the phone number
        var strippedPhoneNumber = this.contact.contact_phone.replace(/\D/g, '');
        Ti.Platform.openURL('sms:'+strippedPhoneNumber); // sms:xxxxxxxx format
    },
    emailContact: function() {
        // Display the email dialog
        var emailDialog = Ti.UI.createEmailDialog({
            toRecipients: [this.contact.contact_email]
        });
        emailDialog.open();
    },

    // Display a window allowing the user to create personal steps and apply them to the current contact
    addPersonalSteps: function() {
        var contact = this.contact;
        var _this = this;
        this.createWindow('ChooseOptionsWindow', {
            groupName: 'personalStep',
            Model: 'Step',
            initial: contact.getPersonalSteps().map(function(step) { return step.attr('step_uuid'); }),
            editable: true
        }).getDeferred().done(function(options) {
            contact.setPersonalSteps(options);
            _this.updateSteps();
        });
    }
});
<|endoftext|>