/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function(window, document, undefined) {
'use strict';
var ns = window.tau = window.tau || {},
nsConfig = window.tauConfig = window.tauConfig || {};
nsConfig.rootNamespace = 'tau';
nsConfig.fileName = 'tau';
ns.version = '1.0.5';
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* global window, define */
/* eslint-disable no-console */
/**
 * #Core namespace
 * Object contains main framework methods.
 * @class ns
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 */
(function (document, console) {
	"use strict";
			var idNumberCounter = 0,
			currentDate = +new Date(),
			slice = [].slice,
			rootNamespace = "",
			fileName = "",
			infoForLog = function (args) {
				var dateNow = new Date();

				args.unshift("[" + rootNamespace + "][" + dateNow.toLocaleString() + "]");
			},
			ns = window.ns || window.tau || {},
			nsConfig = window.nsConfig || window.tauConfig || {};

		ns.info = ns.info || {
			profile: "custom"
		};
		ns.tauPerf = ns.tauPerf || {};

		window.ns = ns;
		window.nsConfig = nsConfig;

		window.tau = ns;
		window.tauConfig = nsConfig;

		rootNamespace = nsConfig.rootNamespace;
		fileName = nsConfig.fileName;

		/**
		 * Return unique id
		 * @method getUniqueId
		 * @static
		 * @return {string}
		 * @member ns
		 */
		ns.getUniqueId = function () {
			return rootNamespace + "-" + ns.getNumberUniqueId() + "-" + currentDate;
		};

		/**
		 * Return unique id
		 * @method getNumberUniqueId
		 * @static
		 * @return {number}
		 * @member ns
		 */
		ns.getNumberUniqueId = function () {
			return idNumberCounter++;
		};

		/**
		 * logs supplied messages/arguments
		 * @method log
		 * @static
		 * @member ns
		 */
		ns.log = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.log.apply(console, args);
			}
		};

		/**
		 * logs supplied messages/arguments ad marks it as warning
		 * @method warn
		 * @static
		 * @member ns
		 */
		ns.warn = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.warn.apply(console, args);
			}
		};

		/**
		 * logs supplied messages/arguments and marks it as error
		 * @method error
		 * @static
		 * @member ns
		 */
		ns.error = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.error.apply(console, args);
			}
		};

		/**
		 * get from nsConfig
		 * @method getConfig
		 * @param {string} key
		 * @param {*} [defaultValue] value returned when config is not set
		 * @return {*}
		 * @static
		 * @member ns
		 */
		ns.getConfig = function (key, defaultValue) {
			return nsConfig[key] === undefined ? defaultValue : nsConfig[key];
		};

		/**
		 * set in nsConfig
		 * @method setConfig
		 * @param {string} key
		 * @param {*} value
		 * @param {boolean} [asDefault=false] value should be treated as default (doesn't overwrites
		 * the config[key] if it already exists)
		 * @static
		 * @member ns
		 */
		ns.setConfig = function (key, value, asDefault) {
			if (!asDefault || nsConfig[key] === undefined) {
				nsConfig[key] = value;
			}
		};

		/**
		 * Return path for framework script file.
		 * @method getFrameworkPath
		 * @return {?string}
		 * @member ns
		 */
		ns.getFrameworkPath = function () {
			var scripts = document.getElementsByTagName("script"),
				countScripts = scripts.length,
				i,
				url,
				arrayUrl,
				count;

			for (i = 0; i < countScripts; i++) {
				url = scripts[i].src;
				arrayUrl = url.split("/");
				count = arrayUrl.length;
				if (arrayUrl[count - 1] === fileName + ".js" ||
					arrayUrl[count - 1] === fileName + ".min.js") {
					return arrayUrl.slice(0, count - 1).join("/");
				}
			}
			return null;
		};

		}(window.document, window.console));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define*/
/*
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function () {
	"use strict";
	
			// Default configuration properties for mobile
			ns.setConfig("autoBuildOnPageChange", true, true);
			ns.setConfig("loader", false, true);
			ns.setConfig("pageContainerBody", true, true);
			ns.setConfig("popupTransition", "slideup", true);
			ns.setConfig("pageTransition", "slide", true);
			ns.setConfig("enablePageScroll", false, true);
			}());

/*global window, define, ns*/
/*jslint bitwise: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function () {
	"use strict";
	
			// Default configuration properties
			ns.setConfig("rootDir", ns.getFrameworkPath(), true);
			ns.setConfig("version", "", true);
			ns.setConfig("allowCrossDomainPages", false, true);
			ns.setConfig("domCache", false, true);
			// .. other possible options
			ns.setConfig("autoBuildOnPageChange", true, true);
			ns.setConfig("autoInitializePage", true, true);
			ns.setConfig("dynamicBaseEnabled", true, true);
			ns.setConfig("pageTransition", "none", true);
			ns.setConfig("popupTransition", "none", true);
			ns.setConfig("popupFullSize", false, true);
			ns.setConfig("scrollEndEffectArea", "content", true);
			ns.setConfig("enablePopupScroll", false, true);
			// ns.setConfig('container', document.body); // for defining application container
			// same as above, but for wearable version
			ns.setConfig("pageContainer", document.body, true);
			ns.setConfig("findProfileFile", false, true);

			}());

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #Defaults settings object
 *
 * This module is deprecated, please use tau.setConfig and tau.getConfig functions or tauConfig
 * object.
 *
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author junhyeonLee <juneh.lee@samsung.com>
 * @author heeju Joo <heeju.joo@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author hagun.kim <hagun.kim@samsung.com>
 * @class ns.defaults
 * @since 2.0
 * @deprecated 3.0
 */
(function () {
	"use strict";
				var defaults = {};

			/**
			 * Helper function to define property on object defaults
			 * @param {string} name Property name to define
			 */
			function defineProperty(name) {
				Object.defineProperty(ns.defaults, name, {
					get: function () {
						ns.warn("tau.defaults are deprecated from Tizen 3.0, please use tau.getConfig.");
						return ns.getConfig(name);
					},
					set: function (value) {
						ns.warn("tau.defaults are deprecated from Tizen 3.0, please use tau.setConfig.");
						return ns.setConfig(name, value);
					}
				});
			}

			ns.defaults = defaults;

			/**
			 * @property {boolean} autoInitializePage=true
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("autoInitializePage");
			/**
			 * @property {boolean} dynamicBaseEnabled=true
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("dynamicBaseEnabled");
			/**
			 * @property {string} pageTransition="none"
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("pageTransition");
			/**
			 * @property {string} popupTransition="none"
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("popupTransition");
			/**
			 * @property {boolean} popupFullSize=false
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("popupFullSize");
			/**
			 * @property {boolean} enablePageScroll=false
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("enablePageScroll");
			/**
			 * @property {string} scrollEndEffectArea="content
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("scrollEndEffectArea");
			/**
			 * @property {boolean} enablePopupScroll=false
			 * @member ns.defaults
			 * @static
			 */
			defineProperty("enablePopupScroll");

			}());

/*global window, ns, define*/
/*jslint bitwise: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Support
 * Namespace with helpers function connected with browser properties
 * @class ns.support
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function (window, document) {
	"use strict";
	
			/* $.mobile.media method: pass a CSS media type or query and get a bool return
			 note: this feature relies on actual media query support for media queries, though types will
			  work most anywhere
			 examples:
			 $.mobile.media('screen') // tests for screen media type
			 $.mobile.media('screen and (min-width: 480px)') // tests for screen media type with window
			  width > 480px
			 $.mobile.media('\@media screen and (-webkit-min-device-pixel-ratio: 2)') // tests for webkit
			  2x pixel ratio (iPhone 4)
			 */
			// TODO: use window.matchMedia once at least one UA implements it
			var cacheMedia = {},
				testDiv = document.createElement("div"),
				fakeBody = document.createElement("body"),
				fakeBodyStyle = fakeBody.style,
				html = document.getElementsByTagName("html")[0],
				style,
				vendors = ["Webkit", "Moz", "O"],
				webos = window.palmGetResource, //only used to rule out scrollTop
				opera = window.opera,
				operamini = window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]",
				blackBerry,
				testDivStyle = testDiv.style;

			testDiv.id = "jquery-mediatest";
			fakeBody.appendChild(testDiv);

			/**
			 * Method checks \@media "query" support
			 * @method media
			 * @param {string} query
			 * @return {boolean}
			 * @static
			 * @member ns.support
			 */
			function media(query) {
				var styleBlock = document.createElement("style"),
					cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";

				if (query.cacheMedia === undefined) {
					//must set type for IE!
					styleBlock.type = "text/css";

					if (styleBlock.styleSheet) {
						styleBlock.styleSheet.cssText = cssrule;
					} else {
						styleBlock.appendChild(document.createTextNode(cssrule));
					}

					if (html.firstChild) {
						html.insertBefore(fakeBody, html.firstChild);
					} else {
						html.appendChild(fakeBody);
					}
					html.insertBefore(styleBlock, fakeBody);
					style = window.getComputedStyle(testDiv);
					cacheMedia[query] = (style.position === "absolute");
					styleBlock.parentNode.removeChild(styleBlock);
					fakeBody.parentNode.removeChild(fakeBody);
				}
				return cacheMedia[query];
			}

			function validStyle(prop, value, checkVend) {
				var div = document.createElement("div"),
					uc = function (txt) {
						return txt.charAt(0).toUpperCase() + txt.substr(1);
					},
					vendPref = function (vend) {
						return "-" + vend.charAt(0).toLowerCase() + vend.substr(1) + "-";
					},
					returnValue,
					checkStyle = function (vend) {
						var vendProp = vendPref(vend) + prop + ": " + value + ";",
							ucVend = uc(vend),
							propStyle = ucVend + uc(prop);

						div.setAttribute("style", vendProp);

						if (div.style[propStyle]) {
							returnValue = true;
						}
					},
					checkVendors = checkVend ? [checkVend] : vendors,
					checkVendorsLength = checkVendors.length,
					i;

				for (i = 0; i < checkVendorsLength; i++) {
					checkStyle(checkVendors[i]);
				}
				return !!returnValue;
			}

			/**
			 *
			 * @param {string} prop
			 * @return {boolean}
			 */
			function propExists(prop) {
				var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
					props = (prop + " " + vendors.join(ucProp + " ") + ucProp).split(" "),
					key;

				for (key = 0; key < props.length; key++) {
					if (props.hasOwnProperty(key) && fakeBodyStyle[props[key]] !== undefined) {
						return true;
					}
				}
				return false;
			}

			function transform3dTest() {
				var prop = "transform-3d";

				return validStyle("perspective", "10px", "moz") || media("(-" + vendors.join("-" + prop + "),(-") + "-" + prop + "),(" + prop + ")");
			}

			blackBerry = window.blackberry && !propExists("-webkit-transform");

			function baseTagTest() {
				var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
					head = document.head,
					base = head.querySelector("base"),
					fauxEle = null,
					hadBase = false,
					href = "",
					link,
					rebase;

				if (base) {
					href = base.getAttribute("href");
					base.setAttribute("href", fauxBase);
					hadBase = true;
				} else {
					base = fauxEle = document.createElement("base");
					base.setAttribute("href", fauxBase);
					head.appendChild(base);
				}

				link = document.createElement("a");
				link.href = "testurl";
				if (fakeBody.firstChild) {
					fakeBody.insertBefore(link, fakeBody.firstChild);
				} else {
					fakeBody.appendChild(link);
				}
				rebase = link.href;
				base.href = href || location.pathname;

				if (fauxEle) {
					head.removeChild(fauxEle);
				}

				// Restore previous base href if base had existed
				if (hadBase) {
					base.setAttribute("href", href);
				}

				// Tell jQuery not to append <base> in build mode
				if (location.hash === "#build") {
					return false;
				}

				return rebase.indexOf(fauxBase) === 0;
			}

			function cssPointerEventsTest() {
				var element = document.createElement("x"),
					documentElement = document.documentElement,
					getComputedStyle = window.getComputedStyle,
					supports,
					elementStyle = element.style;

				if (elementStyle.pointerEvents === undefined) {
					return false;
				}

				elementStyle.pointerEvents = "auto";
				elementStyle.pointerEvents = "x";
				documentElement.appendChild(element);
				supports = getComputedStyle && getComputedStyle(element, "").pointerEvents === "auto";
				documentElement.removeChild(element);
				return !!supports;
			}

			function boundingRect() {
				var div = document.createElement("div");

				return div.getBoundingClientRect !== undefined;
			}

			ns.support = {
				media: media,
				/**
				 * Informs browser support transition
				 * @property {boolean} cssTransitions
				 * @member ns.support
				 * @static
				 */
				cssTransitions: (window.WebKitTransitionEvent !== undefined || validStyle("transition", "height 100ms linear")) && !opera,
				/**
				 * Informs browser support history.pushStare method
				 * @property {boolean} pushState
				 * @member ns.support
				 * @static
				 */
				pushState: window.history.pushState && window.history.replaceState && true,
				/**
				 * Informs browser support media query "only all"
				 * @property {boolean} mediaquery
				 * @member ns.support
				 * @static
				 */
				mediaquery: media("only all"),
				/**
				 * Informs browser support content property on element
				 * @property {boolean} cssPseudoElement
				 * @member ns.support
				 * @static
				 */
				cssPseudoElement: !!propExists("content"),
				/**
				 * Informs browser support overflowScrolling property on element
				 * @property {boolean} touchOverflow
				 * @member ns.support
				 * @static
				 */
				touchOverflow: !!propExists("overflowScrolling"),
				/**
				 * Informs browser support CSS 3D transitions
				 * @property {boolean} cssTransform3d
				 * @member ns.support
				 * @static
				 */
				cssTransform3d: transform3dTest(),
				/**
				 * Informs browser support boxShadow property on element
				 * @property {boolean} boxShadow
				 * @member ns.support
				 * @static
				 */
				boxShadow: !!propExists("boxShadow") && !blackBerry,
				/**
				 * Informs browser support scrollTop property
				 * @property {boolean} scrollTop
				 * @member ns.support
				 * @static
				 */
				scrollTop: ((window.pageXOffset || document.documentElement.scrollTop || fakeBody.scrollTop) !== undefined && !webos && !operamini) ? true : false,
				/**
				 * Informs browser support dynamic change base tag
				 * @property {boolean} dynamicBaseTag
				 * @member ns.support
				 * @static
				 */
				dynamicBaseTag: baseTagTest(),
				/**
				 * Informs browser support CSS pointer events
				 * @property {boolean} cssPointerEvents
				 * @member ns.support
				 * @static
				 */
				cssPointerEvents: cssPointerEventsTest(),
				/**
				 * Prefix for animations
				 * @property ("-webkit-"|"-moz-"|"-o-"|""} cssAnimationPrefix
				 * @member ns.support
				 * @static
				 */
				cssAnimationPrefix: testDivStyle.hasOwnProperty("webkitAnimation") ? "-webkit-" :
					testDivStyle.hasOwnProperty("mozAnimation") ? "-moz-" :
						testDivStyle.hasOwnProperty("oAnimation") ? "-o-" : "",
				/**
				 * Informs browser support getBoundingClientRect
				 * @property {boolean} boundingRect
				 * @member ns.support
				 * @static
				 */
				boundingRect: boundingRect(),
				/**
				 * Object with browser information
				 * @property (Object} browser
				 * @property {boolean} browser.ie detects Internet Explorer
				 * @member ns.support
				 * @static
				 */
				browser: {
					ie: (function () {
						var v = 3,
							div = document.createElement("div"),
							a = div.all || [];

						do {
							div.innerHTML = "<!--[if gt IE " + (++v) + "]><br><![endif]-->";
						} while (a[0]);
						return v > 4 ? v : !v;
					}())
				},
				/**
				 * Informs that browser pass all tests for run framework
				 * @method gradeA
				 * @member ns.support
				 * @static
				 * @return {boolean}
				 */
				gradeA: function () {
					return ((this.mediaquery || (this.browser.ie && this.browser.ie >= 7)) &&
					(this.boundingRect || ((window.jQuery && window.jQuery.fn && window.jQuery.fn.jquery.match(/1\.[0-7+]\.[0-9+]?/)) !== null)));
				},
				/**
				 * Informs browser support touch events
				 * @property {boolean} touch
				 * @member ns.support
				 * @static
				 */
				touch: document.ontouchend !== undefined,
				/**
				 * Informs browser support orientation property
				 * @property {boolean} orientation
				 * @member ns.support
				 * @static
				 */
				orientation: window.orientation !== undefined && window.onorientationchange !== undefined
			};
			testDiv = null;
			fakeBody = null;
			}(window, window.document));

/*global window, ns, define, XMLHttpRequest, console, Blob */
/*jslint nomen: true, browser: true, plusplus: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Utilities
 *
 * The Tizen Advanced UI (TAU) framework provides utilities for easy-developing
 * and fully replaceable with jQuery method. When user using these DOM and
 * selector methods, it provide more light logic and it proves performance
 * of web app. The following table displays the utilities provided by the
 * TAU framework.
 *
 * @class ns.util
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var currentFrame = null,
				util = ns.util || {},
				// frames callbacks which should be run in next request animation frame
				waitingFrames = [],
				slice = [].slice,
				// inform that loop was added to request animation frame callback
				loopWork = false;

			/**
			 * Function which is use as workaround when any type of request animation frame not exists
			 * @param {Function} callback
			 * @method _requestAnimationFrameOnSetTimeout
			 * @static
			 * @member ns.util
			 * @protected
			 */
			util._requestAnimationFrameOnSetTimeout = function (callback) {
				currentFrame = window.setTimeout(callback.bind(callback, +new Date()), 1000 / 60);
			};

			/**
			 * Function which support every request animation frame.
			 * @method _loop
			 * @protected
			 * @static
			 * @member ns.util
			 */
			util._loop = function () {
				var loopWaitingFrames = slice.call(waitingFrames),
					currentFrameFunction = loopWaitingFrames.shift(),
					loopTime = performance.now();

				waitingFrames = [];

				while (currentFrameFunction) {
					currentFrameFunction();
					if (performance.now() - loopTime < 15) {
						currentFrameFunction = loopWaitingFrames.shift();
					} else {
						currentFrameFunction = null;
					}
				}
				if (loopWaitingFrames.length || waitingFrames.length) {
					waitingFrames.unshift.apply(waitingFrames, loopWaitingFrames);
					util.windowRequestAnimationFrame(util._loop);
				} else {
					loopWork = false;
				}
			};

			/**
			 * Find browser prefixed request animation frame function.
			 * @method _getRequestAnimationFrame
			 * @protected
			 * @static
			 * @member ns.util
			 */
			util._getRequestAnimationFrame = function () {
				return (window.requestAnimationFrame ||
					window.webkitRequestAnimationFrame ||
					window.mozRequestAnimationFrame ||
					window.oRequestAnimationFrame ||
					window.msRequestAnimationFrame ||
					util._requestAnimationFrameOnSetTimeout).bind(window);
			};

			/**
			 * Original requestAnimationFrame from object window.
			 * @method windowRequestAnimationFrame
			 * @static
			 * @member ns.util
			 */
			util.windowRequestAnimationFrame = util._getRequestAnimationFrame();

			/**
			 * Special requestAnimationFrame function which add functions to queue of callbacks
			 * @method requestAnimationFrame
			 * @static
			 * @member ns.util
			 */
			util.requestAnimationFrame = function (callback) {
				waitingFrames.push(callback);
				if (!loopWork) {
					util.windowRequestAnimationFrame(util._loop);
					loopWork = true;
				}
			};

			util._cancelAnimationFrameOnSetTimeout = function () {
				// probably wont work if there is any more than 1
				// active animationFrame but we are trying anyway
				window.clearTimeout(currentFrame);
			};

			util._getCancelAnimationFrame = function () {
				return (window.cancelAnimationFrame ||
					window.webkitCancelAnimationFrame ||
					window.mozCancelAnimationFrame ||
					window.oCancelAnimationFrame ||
					window.msCancelAnimationFrame ||
					util._cancelAnimationFrameOnSetTimeout).bind(window);
			};

			util.cancelAnimationFrame = util._getCancelAnimationFrame();

			/**
			 * fetchSync retrieves a text document synchronously, returns null on error
			 * @param {string} url
			 * @param {=string} [mime=""] Mime type of the resource
			 * @return {string|null}
			 * @static
			 * @member ns.util
			 */
			function fetchSync(url, mime) {
				var xhr = new XMLHttpRequest(),
					status;

				xhr.open("get", url, false);
				if (mime) {
					xhr.overrideMimeType(mime);
				}
				xhr.send();
				if (xhr.readyState === 4) {
					status = xhr.status;
					if (status === 200 || (status === 0 && xhr.responseText)) {
						return xhr.responseText;
					}
				}

				return null;
			}

			util.fetchSync = fetchSync;

			/**
			 * Removes all script tags with src attribute from document and returns them
			 * @param {HTMLElement} container
			 * @return {Array.<HTMLElement>}
			 * @protected
			 * @static
			 * @member ns.util
			 */
			function removeExternalScripts(container) {
				var scripts = slice.call(container.querySelectorAll("script[src]")),
					i = scripts.length,
					script;

				while (--i >= 0) {
					script = scripts[i];
					script.parentNode.removeChild(script);
				}

				return scripts;
			}

			util._removeExternalScripts = removeExternalScripts;

			/**
			 * Evaluates code, reason for a function is for an atomic call to evaluate code
			 * since most browsers fail to optimize functions with try-catch blocks, so this
			 * minimizes the effect, returns the function to run
			 * @param {string} code
			 * @return {Function}
			 * @static
			 * @member ns.util
			 */
			function safeEvalWrap(code) {
				return function () {
					try {
						window.eval(code);
					} catch (e) {
						if (e.stack) {
							ns.error(e.stack);
						} else if (e.name && e.message) {
							ns.error(e.name, e.message);
						} else {
							ns.error(e);
						}
					}
				};
			}

			util.safeEvalWrap = safeEvalWrap;

			/**
			 * Calls functions in supplied queue (array)
			 * @param {Array.<Function>} functionQueue
			 * @static
			 * @member ns.util
			 */
			function batchCall(functionQueue) {
				var i,
					length = functionQueue.length;

				for (i = 0; i < length; ++i) {
					functionQueue[i]();
				}
			}

			util.batchCall = batchCall;

			/**
			 * Creates new script elements for scripts gathered from a different document
			 * instance, blocks asynchronous evaluation (by renaming src attribute) and
			 * returns an array of functions to run to evaluate those scripts
			 * @param {Array.<HTMLElement>} scripts
			 * @param {HTMLElement} container
			 * @return {Array.<Function>}
			 * @protected
			 * @static
			 * @member ns.util
			 */
			function createScriptsSync(scripts, container) {
				var scriptElement,
					scriptBody,
					i,
					length,
					queue = [];

				// proper order of execution
				for (i = 0, length = scripts.length; i < length; ++i) {
					scriptBody = util.fetchSync(scripts[i].src, "text/plain");
					if (scriptBody) {
						scriptElement = document.adoptNode(scripts[i]);
						scriptElement.setAttribute("data-src", scripts[i].src);
						scriptElement.removeAttribute("src"); // block evaluation
						queue.push(util.safeEvalWrap(scriptBody));
						if (container) {
							container.appendChild(scriptElement);
						}
					}
				}

				return queue;
			}

			util._createScriptsSync = createScriptsSync;

			function removeInlineScripts(element) {
				var result = [],
					script;

				[].slice.call(element.querySelectorAll(
					"script:not([data-src]):not([type]):not([id]):not([src])"
					)).forEach(function (item) {
						script = document.createElement("script");
						script.innerText = item.textContent;
						item.parentNode.removeChild(item);
						result.push(script);
					});

				return result;
			}

			util._removeInlineScripts = removeInlineScripts;

			/**
			 * Method make asynchronous call of function
			 * @method async
			 * @inheritdoc #requestAnimationFrame
			 * @member ns.util
			 * @static
			 */
			util.async = util.requestAnimationFrame;

			/**
			 * Appends element from different document instance to current document in the
			 * container element and evaluates scripts (synchronously)
			 * @param {HTMLElement} element
			 * @param {HTMLElement} container
			 * @return {HTMLElement}
			 * @method importEvaluateAndAppendElement
			 * @member ns.util
			 * @static
			 */
			util.importEvaluateAndAppendElement = function (element, container) {
				var externalScriptsQueue =
						util._createScriptsSync(util._removeExternalScripts(element), element),
					inlineScripts = util._removeInlineScripts(element),
					newNode = document.importNode(element, true);

				container.appendChild(newNode); // append and eval inline
				inlineScripts.forEach(function (script) {
					container.appendChild(script);
				});
				util.batchCall(externalScriptsQueue);

				return newNode;
			};

			/**
			 * Checks if specified string is a number or not
			 * @method isNumber
			 * @param {string} query
			 * @return {boolean}
			 * @member ns.util
			 * @static
			 */
			util.isNumber = function (query) {
				var parsed = parseFloat(query);

				return !isNaN(parsed) && isFinite(parsed);
			};

			/**
			 * Reappear script tags to DOM structure to correct run script
			 * @method runScript
			 * @param {string} baseUrl
			 * @param {HTMLScriptElement} script
			 * @member ns.util
			 * @deprecated 2.3
			 */
			util.runScript = function (baseUrl, script) {
				var newScript = document.createElement("script"),
					scriptData,
					i,
					scriptAttributes = slice.call(script.attributes),
					src = script.getAttribute("src"),
					attribute,
					status;

				// 'src' may become null when none src attribute is set
				if (src !== null) {
					src = util.path.makeUrlAbsolute(src, baseUrl);
				}

				//Copy script tag attributes
				i = scriptAttributes.length;
				while (--i >= 0) {
					attribute = scriptAttributes[i];
					if (attribute.name !== "src") {
						newScript.setAttribute(attribute.name, attribute.value);
					} else {
						newScript.setAttribute("data-src", attribute.value);
					}
				}

				if (src) {
					scriptData = util.fetchSync(src, "text/plain");
									} else {
					scriptData = script.textContent;
				}

				if (scriptData) {
					// add the returned content to a newly created script tag
					newScript.src = window.URL.createObjectURL(new Blob([scriptData], {type: "text/javascript"}));
					newScript.textContent = scriptData; // for compatibility with some libs ex. template systems
				}
				script.parentNode.replaceChild(newScript, script);
			};

			ns.util = util;
			}(window, window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/**
 * #Array Utility
 *
 * Utility helps work with arrays.
 *
 * @class ns.util.array
 */
(function (ns) {
	"use strict";
	
			/**
			 * Convert values to common type and return information about type string or not.
			 * @param {number|string} low
			 * @param {number|string} high
			 * @return {{inival: *, endval: *, chars: boolean}}
			 */
			function convertTypes(low, high) {
				var inival,
					endval,
					chars = false;

				if (isNaN(low) && isNaN(high)) {
					chars = true;
					inival = low.charCodeAt(0);
					endval = high.charCodeAt(0);
				} else {
					inival = (isNaN(low) ? 0 : low);
					endval = (isNaN(high) ? 0 : high);
				}
				return {
					inival: inival,
					endval: endval,
					chars: chars
				};
			}

			/**
			 * Create an array containing the range of integers or characters
			 * from low to high (inclusive)
			 * @method range
			 * @param {number|string} low
			 * @param {number|string} high
			 * @param {number} step
			 * @static
			 * @return {Array} array containing continuos elements
			 * @member ns.util.array
			 */
			function range(low, high, step) {
				// Create an array containing the range of integers or characters
				// from low to high (inclusive)
				//
				// version: 1107.2516
				// discuss at: http://phpjs.org/functions/range
				// +   original by: Waldo Malqui Silva
				// *	example 1: range ( 0, 12 );
				// *	returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
				// *	example 2: range( 0, 100, 10 );
				// *	returns 2: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
				// *	example 3: range( 'a', 'i' );
				// *	returns 3: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
				// *	example 4: range( 'c', 'a' );
				// *	returns 4: ['c', 'b', 'a']
				var matrix = [],
					inival,
					endval,
					plus,
					walker = step || 1,
					chars,
					typeData;

				typeData = convertTypes(low, high);
				inival = typeData.inival;
				endval = typeData.endval;
				chars = typeData.chars;

				plus = inival <= endval;
				if (plus) {
					while (inival <= endval) {
						matrix.push((chars ? String.fromCharCode(inival) : inival));
						inival += walker;
					}
				} else {
					while (inival >= endval) {
						matrix.push((chars ? String.fromCharCode(inival) : inival));
						inival -= walker;
					}
				}

				return matrix;
			}

			function isCorrectType(object) {
				return Array.isArray(object) || object instanceof NodeList || typeof object === "function";
			}

			function hasCorrectLength(object) {
				var length = object.length;

				return (length === 0 || typeof length === "number" && length > 0 && (length - 1) in object);
			}

			/**
			 * Check object is array-like (array-like include array and
			 * collection)
			 * @method isArrayLike
			 * @param {Object} object
			 * @return {boolean} Whether array-like object or not
			 * @member ns.util.array
			 * @static
			 */
			function isArrayLike(object) {

				// if object exists and is different from window
				// window object has length property
				if (object && object !== object.window) {
					// If length value is not number, object is not array and collection.
					// Collection type is not array but has length value.
					// e.g) Array.isArray(document.childNodes) ==> false
					return isCorrectType(object) && hasCorrectLength(object);
				}
				return false;
			}

			/**
			 * Faster version of standard forEach method in array
			 * Confirmed that this method is 20 times faster then native
			 * @method forEach
			 * @param {Array} array
			 * @param {Function} callback
			 * @member ns.util.array
			 * @static
			 */
			function forEach(array, callback) {
				var i,
					length,
					convertedArray = array;

				if (!(array instanceof Array)) {
					convertedArray = [].slice.call(array);
				}
				length = convertedArray.length;
				for (i = 0; i < length; i++) {
					callback(convertedArray[i], i, convertedArray);
				}
			}


			/**
			 * Faster version of standard filter method in array
			 * @method filter
			 * @param {Array} array
			 * @param {Function} callback
			 * @member ns.util.array
			 * @static
			 */
			function filter(array, callback) {
				var result = [],
					i,
					length,
					value,
					convertedArray = array;

				if (!(array instanceof Array)) {
					convertedArray = [].slice.call(array);
				}
				length = convertedArray.length;
				for (i = 0; i < length; i++) {
					value = convertedArray[i];
					if (callback(value, i, convertedArray)) {
						result.push(value);
					}
				}
				return result;
			}

			/**
			 * Faster version of standard map method in array
			 * Confirmed that this method is 60% faster then native
			 * @method map
			 * @param {Array} array
			 * @param {Function} callback
			 * @member ns.util.array
			 * @static
			 */
			function map(array, callback) {
				var result = [],
					i,
					length,
					convertedArray = array;

				if (!(array instanceof Array)) {
					convertedArray = [].slice.call(array);
				}
				length = convertedArray.length;
				for (i = 0; i < length; i++) {
					result.push(callback(convertedArray[i], i, convertedArray));
				}
				return result;
			}

			/**
			 * Faster version of standard reduce method in array
			 * Confirmed that this method is 60% faster then native
			 * @method reduce
			 * @param {Array} array
			 * @param {Function} callback
			 * @param {*} [initialValue]
			 * @member ns.util.array
			 * @return {*}
			 * @static
			 */
			function reduce(array, callback, initialValue) {
				var i,
					length,
					value,
					result = initialValue,
					convertedArray = array;

				if (!(array instanceof Array)) {
					convertedArray = [].slice.call(array);
				}
				length = convertedArray.length;
				for (i = 0; i < length; i++) {
					value = convertedArray[i];
					if (result === undefined && i === 0) {
						result = value;
					} else {
						result = callback(result, value, i, convertedArray);
					}
				}
				return result;
			}

			ns.util.array = {
				range: range,
				isArrayLike: isArrayLike,
				forEach: forEach,
				filter: filter,
				map: map,
				reduce: reduce
			};

			}(ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/* global ns, define, CustomEvent */
/**
 * #Events
 *
 * The Tizen Advanced UI (TAU) framework provides events optimized for the Tizen
 * Web application. The following table displays the events provided by the TAU
 * framework.
 * @class ns.event
 */
(function (window, ns) {
	"use strict";
				/**
			 * Checks if specified variable is a array or not
			 * @method isArray
			 * @return {boolean}
			 * @member ns.event
			 * @private
			 * @static
			 */
			var instances = [],
				isArray = Array.isArray,
				isArrayLike = ns.util.array.isArrayLike,
				/**
				 * @property {RegExp} SPLIT_BY_SPACES_REGEXP
				 */
				SPLIT_BY_SPACES_REGEXP = /\s+/g,

				/**
				 * Returns trimmed value
				 * @method trim
				 * @param {string} value
				 * @return {string} trimmed string
				 * @static
				 * @private
				 * @member ns.event
				 */
				trim = function (value) {
					return value.trim();
				},

				/**
				 * Split string to array
				 * @method getEventsListeners
				 * @param {string|Array|Object} names string with one name of event, many names of events
				 * divided by spaces, array with names of widgets or object in which keys are names of
				 * events and values are callbacks
				 * @param {Function} globalListener
				 * @return {Array}
				 * @static
				 * @private
				 * @member ns.event
				 */
				getEventsListeners = function (names, globalListener) {
					var name,
						result = [],
						i;

					if (typeof names === "string") {
						names = names.split(SPLIT_BY_SPACES_REGEXP).map(trim);
					}

					if (isArray(names)) {
						for (i = 0; i < names.length; i++) {
							result.push({type: names[i], callback: globalListener});
						}
					} else {
						for (name in names) {
							if (names.hasOwnProperty(name)) {
								result.push({type: name, callback: names[name]});
							}
						}
					}
					return result;
				};

			/**
			 * Find instance by element
			 * @method findInstance
			 * @param {HTMLElement} element
			 * @return {ns.event.gesture.Instance}
			 * @member ns.event
			 * @static
			 * @private
			 */
			function findInstance(element) {
				var instance;

				instances.forEach(function (item) {
					if (item.element === element) {
						instance = item.instance;
					}
				});
				return instance;
			}

			/**
			 * Remove instance from instances by element
			 * @method removeInstance
			 * @param {HTMLElement} element
			 * @member ns.event
			 * @static
			 * @private
			 */
			function removeInstance(element) {
				instances.forEach(function (item, key) {
					if (item.element === element) {
						instances.splice(key, 1);
					}
				});
			}


			ns.event = {

				/**
				 * Triggers custom event fastOn element
				 * The return value is false, if at least one of the event
				 * handlers which handled this event, called preventDefault.
				 * Otherwise it returns true.
				 * @method trigger
				 * @param {HTMLElement|HTMLDocument} element
				 * @param {string} type
				 * @param {?*} [data=null]
				 * @param {boolean=} [bubbles=true]
				 * @param {boolean=} [cancelable=true]
				 * @return {boolean}
				 * @member ns.event
				 * @static
				 */
				trigger: function (element, type, data, bubbles, cancelable) {
					var evt = new CustomEvent(type, {
						"detail": data,
						//allow event to bubble up, required if we want to allow to listen fastOn document etc
						bubbles: typeof bubbles === "boolean" ? bubbles : true,
						cancelable: typeof cancelable === "boolean" ? cancelable : true
					});
										return element.dispatchEvent(evt);
				},

				/**
				 * Prevent default on original event
				 * @method preventDefault
				 * @param {Event} event
				 * @member ns.event
				 * @static
				 */
				preventDefault: function (event) {
					var originalEvent = event._originalEvent;
					// @todo this.isPropagationStopped = returnTrue;

					if (originalEvent && originalEvent.preventDefault) {
						originalEvent.preventDefault();
					}
					event.preventDefault();
				},

				/**
				 * Stop event propagation
				 * @method stopPropagation
				 * @param {Event} event
				 * @member ns.event
				 * @static
				 */
				stopPropagation: function (event) {
					var originalEvent = event._originalEvent;
					// @todo this.isPropagationStopped = returnTrue;

					if (originalEvent && originalEvent.stopPropagation) {
						originalEvent.stopPropagation();
					}
					event.stopPropagation();
				},

				/**
				 * Stop event propagation immediately
				 * @method stopImmediatePropagation
				 * @param {Event} event
				 * @member ns.event
				 * @static
				 */
				stopImmediatePropagation: function (event) {
					var originalEvent = event._originalEvent;
					// @todo this.isPropagationStopped = returnTrue;

					if (originalEvent && originalEvent.stopImmediatePropagation) {
						originalEvent.stopImmediatePropagation();
					}
					event.stopImmediatePropagation();
				},

				/**
				 * Return document relative cords for event
				 * @method documentRelativeCoordsFromEvent
				 * @param {Event} event
				 * @return {Object}
				 * @return {number} return.x
				 * @return {number} return.y
				 * @member ns.event
				 * @static
				 */
				documentRelativeCoordsFromEvent: function (event) {
					var _event = event ? event : window.event,
						client = {
							x: _event.clientX,
							y: _event.clientY
						},
						page = {
							x: _event.pageX,
							y: _event.pageY
						},
						posX = 0,
						posY = 0,
						touch0,
						body = document.body,
						documentElement = document.documentElement;

					if (event.type.match(/^touch/)) {
						touch0 = _event.targetTouches[0] || _event.originalEvent.targetTouches[0];
						page = {
							x: touch0.pageX,
							y: touch0.pageY
						};
						client = {
							x: touch0.clientX,
							y: touch0.clientY
						};
					}

					if (page.x || page.y) {
						posX = page.x;
						posY = page.y;
					} else if (client.x || client.y) {
						posX = client.x + body.scrollLeft + documentElement.scrollLeft;
						posY = client.y + body.scrollTop + documentElement.scrollTop;
					}

					return {x: posX, y: posY};
				},

				/**
				 * Return target relative cords for event
				 * @method targetRelativeCoordsFromEvent
				 * @param {Event} event
				 * @return {Object}
				 * @return {number} return.x
				 * @return {number} return.y
				 * @member ns.event
				 * @static
				 */
				targetRelativeCoordsFromEvent: function (event) {
					var target = event.target,
						cords = {
							x: event.offsetX,
							y: event.offsetY
						};

					if (cords.x === undefined || isNaN(cords.x) ||
						cords.y === undefined || isNaN(cords.y)) {
						cords = ns.event.documentRelativeCoordsFromEvent(event);
						cords.x -= target.offsetLeft;
						cords.y -= target.offsetTop;
					}

					return cords;
				},

				/**
				 * Add event listener to element
				 * @method fastOn
				 * @param {HTMLElement} element
				 * @param {string} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				fastOn: function (element, type, listener, useCapture) {
					element.addEventListener(type, listener, useCapture || false);
				},

				/**
				 * Remove event listener to element
				 * @method fastOff
				 * @param {HTMLElement} element
				 * @param {string} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				fastOff: function (element, type, listener, useCapture) {
					element.removeEventListener(type, listener, useCapture || false);
				},

				/**
				 * Add event listener to element with prefixes for all browsers
				 *
				 *	@example
				 * 		tau.event.prefixedFastOn(document, "animationEnd", function() {
				 *			console.log("animation ended");
				 *		});
				 *		// write "animation ended" on console on event "animationEnd", "webkitAnimationEnd", "mozAnimationEnd", "msAnimationEnd", "oAnimationEnd"
				 *
				 * @method fastPrefixedOn
				 * @param {HTMLElement} element
				 * @param {string} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				prefixedFastOn: function (element, type, listener, useCapture) {
					var nameForPrefix = type.charAt(0).toLocaleUpperCase() + type.substring(1);

					element.addEventListener(type.toLowerCase(), listener, useCapture || false);
					element.addEventListener("webkit" + nameForPrefix, listener, useCapture || false);
					element.addEventListener("moz" + nameForPrefix, listener, useCapture || false);
					element.addEventListener("ms" + nameForPrefix, listener, useCapture || false);
					element.addEventListener("o" + nameForPrefix.toLowerCase(), listener, useCapture || false);
				},

				/**
				 * Remove event listener to element with prefixes for all browsers
				 *
				 *	@example
				 *		tau.event.prefixedFastOff(document, "animationEnd", functionName);
				 *		// remove listeners functionName on events "animationEnd", "webkitAnimationEnd", "mozAnimationEnd", "msAnimationEnd", "oAnimationEnd"
				 *
				 * @method fastPrefixedOff
				 * @param {HTMLElement} element
				 * @param {string} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				prefixedFastOff: function (element, type, listener, useCapture) {
					var nameForPrefix = type.charAt(0).toLocaleUpperCase() + type.substring(1);

					element.removeEventListener(type.toLowerCase(), listener, useCapture || false);
					element.removeEventListener("webkit" + nameForPrefix, listener, useCapture || false);
					element.removeEventListener("moz" + nameForPrefix, listener, useCapture || false);
					element.removeEventListener("ms" + nameForPrefix, listener, useCapture || false);
					element.removeEventListener("o" + nameForPrefix.toLowerCase(), listener, useCapture || false);
				},

				/**
				 * Add event listener to element that can be added addEventListener
				 * @method on
				 * @param {HTMLElement|HTMLDocument|Window} element
				 * @param {string|Array|Object} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				on: function (element, type, listener, useCapture) {
					var i,
						j,
						elementsLength,
						typesLength,
						elements,
						listeners;

					if (isArrayLike(element)) {
						elements = element;
					} else {
						elements = [element];
					}
					elementsLength = elements.length;
					listeners = getEventsListeners(type, listener);
					typesLength = listeners.length;
					for (i = 0; i < elementsLength; i++) {
						if (typeof elements[i].addEventListener === "function") {
							for (j = 0; j < typesLength; j++) {
								ns.event.fastOn(elements[i], listeners[j].type, listeners[j].callback, useCapture);
							}
						}
					}
				},

				/**
				 * Remove event listener to element
				 * @method off
				 * @param {HTMLElement|HTMLDocument|Window} element
				 * @param {string|Array|Object} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				off: function (element, type, listener, useCapture) {
					var i,
						j,
						elementsLength,
						typesLength,
						elements,
						listeners;

					if (isArrayLike(element)) {
						elements = element;
					} else {
						elements = [element];
					}
					elementsLength = elements.length;
					listeners = getEventsListeners(type, listener);
					typesLength = listeners.length;
					for (i = 0; i < elementsLength; i++) {
						if (typeof elements[i].addEventListener === "function") {
							for (j = 0; j < typesLength; j++) {
								ns.event.fastOff(elements[i], listeners[j].type, listeners[j].callback, useCapture);
							}
						}
					}
				},

				/**
				 * Add event listener to element only for one trigger
				 * @method one
				 * @param {HTMLElement|HTMLDocument|window} element
				 * @param {string|Array|Object} type
				 * @param {Function} listener
				 * @param {boolean} [useCapture=false]
				 * @member ns.event
				 * @static
				 */
				one: function (element, type, listener, useCapture) {
					var arraySlice = [].slice,
						i,
						j,
						elementsLength,
						typesLength,
						elements,
						listeners,
						callbacks = [];

					if (isArrayLike(element)) {
						elements = arraySlice.call(element);
					} else {
						elements = [element];
					}
					elementsLength = elements.length;
					// pair type with listener
					listeners = getEventsListeners(type, listener);
					typesLength = listeners.length;
					// on each element
					for (i = 0; i < elementsLength; i++) {
						// if element has possibility of add listener
						if (typeof elements[i].addEventListener === "function") {
							callbacks[i] = [];
							// for each event type
							for (j = 0; j < typesLength; j++) {
								callbacks[i][j] = (function (i, j) {
									var args = arraySlice.call(arguments);

									ns.event.fastOff(elements[i], listeners[j].type, callbacks[i][j], useCapture);
									// remove the first argument of binding function
									args.shift();
									// remove the second argument of binding function
									args.shift();
									listeners[j].callback.apply(this, args);
								}).bind(null, i, j);
								ns.event.fastOn(elements[i], listeners[j].type, callbacks[i][j], useCapture);
							}
						}
					}
				},

				// disable is required because method has changing arguments
				/* eslint-disable jsdoc/check-param-names */

				/**
				 * Enable gesture handling on given HTML element or object
				 * @method enableGesture
				 * @param {HTMLElement} element
				 * @param {...Object} [gesture] Gesture object {@link ns.event.gesture}
				 * @member ns.event
				 */
				enableGesture: function (element) {
					var gestureInstance = findInstance(element),
						length = arguments.length,
						i = 1;

					if (!gestureInstance) {
						gestureInstance = new ns.event.gesture.Instance(element);
						instances.push({element: element, instance: gestureInstance});
					}

					for (; i < length; i++) {
						gestureInstance.addDetector(arguments[i]);
					}
				},

				/**
				 * Disable gesture handling from given HTML element or object
				 * @method disableGesture
				 * @param {HTMLElement} element
				 * @param {...Object} [gesture] Gesture object {@link ns.event.gesture}
				 * @member ns.event
				 */
				disableGesture: function (element) {
					var gestureInstance = findInstance(element),
						length = arguments.length,
						i = 1;

					if (!gestureInstance) {
						return;
					}

					if (length > 1) {
						gestureInstance.removeDetector(arguments[i]);
					} else {
						gestureInstance.destroy();
						removeInstance(element);
					}
				}

				/* eslint-disable jsdoc/check-param-names */
			};

			}(window, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Info
 *
 * Various TAU information
 * @class ns.info
 */
(function (window, document) {
	"use strict";
				/**
			 * @property {Object} info
			 * @property {string} [info.profile="default"] Current runtime profile
			 * @property {string} [info.theme="default"] Current runtime theme
			 * @property {string} info.version Current runtime version
			 * @member ns.info
			 * @static
			 */
			var eventUtils = ns.event,
				info = {
					profile: "default",
					theme: "default",
					version: ns.version,

					/**
					 * Refreshes information about runtime
					 * @method refreshTheme
					 * @param {Function} done Callback run when the theme is discovered
					 * @member ns.info
					 * @return {null|String}
					 * @static
					 */
					refreshTheme: function (done) {
						var el = document.createElement("span"),
							parent = document.body,
							themeName;

						if (document.readyState !== "interactive" && document.readyState !== "complete") {
							eventUtils.fastOn(document, "DOMContentLoaded", this.refreshTheme.bind(this, done));
							return null;
						}
						el.classList.add("tau-info-theme");

						parent.appendChild(el);
						themeName = window.getComputedStyle(el, ":after").content;
						parent.removeChild(el);

						if (themeName && themeName.length > 0) {
							this.theme = themeName;
						}

						themeName = themeName || null;

						if (done) {
							done(themeName);
						}

						return themeName;
					}
				};

			info.refreshTheme();

			ns.info = info;
			}(window, window.document));

/*global define: false, window: false, ns: false */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Selectors Utility
 * Object contains functions to get HTML elements by different selectors.
 * @class ns.util.selectors
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Damian Osipiuk <d.osipiuk@samsung.com>
 */
(function (document, ns) {
	"use strict";
				/**
			 * @method slice Alias for array slice method
			 * @member ns.util.selectors
			 * @private
			 * @static
			 */
			var slice = [].slice,
				/**
				 * @method matchesSelectorType
				 * @return {string|boolean}
				 * @member ns.util.selectors
				 * @private
				 * @static
				 */
				matchesSelectorType = (function () {
					var el = document.createElement("div");

					if (typeof el.webkitMatchesSelector === "function") {
						return "webkitMatchesSelector";
					}

					if (typeof el.mozMatchesSelector === "function") {
						return "mozMatchesSelector";
					}

					if (typeof el.msMatchesSelector === "function") {
						return "msMatchesSelector";
					}

					if (typeof el.matchesSelector === "function") {
						return "matchesSelector";
					}

					if (typeof el.matches === "function") {
						return "matches";
					}

					return "";
				}());

			/**
			 * Prefix selector with 'data-' and namespace if present
			 * @method getDataSelector
			 * @param {string} selector
			 * @return {string}
			 * @member ns.util.selectors
			 * @private
			 * @static
			 */
			function getDataSelector(selector) {
				var namespace = ns.getConfig("namespace");

				return "[data-" + (namespace ? namespace + "-" : "") + selector + "]";
			}

			/**
			 * Runs matches implementation of matchesSelector
			 * method on specified element
			 * @method matchesSelector
			 * @param {HTMLElement} element
			 * @param {string} selector
			 * @return {boolean}
			 * @static
			 * @member ns.util.selectors
			 */
			function matchesSelector(element, selector) {
				if (matchesSelectorType && element[matchesSelectorType]) {
					return element[matchesSelectorType](selector);
				}
				return false;
			}

			/**
			 * Return array with all parents of element.
			 * @method parents
			 * @param {HTMLElement} element
			 * @return {Array}
			 * @member ns.util.selectors
			 * @private
			 * @static
			 */
			function parents(element) {
				var items = [],
					current = element.parentNode;

				while (current && current !== document) {
					items.push(current);
					current = current.parentNode;
				}
				return items;
			}

			/**
			 * Checks if given element and its ancestors matches given function
			 * @method closest
			 * @param {HTMLElement} element
			 * @param {Function} testFunction
			 * @return {?HTMLElement}
			 * @member ns.util.selectors
			 * @static
			 * @private
			 */
			function closest(element, testFunction) {
				var current = element;

				while (current && current !== document) {
					if (testFunction(current)) {
						return current;
					}
					current = current.parentNode;
				}
				return null;
			}

			/**
			 * @method testSelector
			 * @param {string} selector
			 * @param {HTMLElement} node
			 * @return {boolean}
			 * @member ns.util.selectors
			 * @static
			 * @private
			 */
			function testSelector(selector, node) {
				return matchesSelector(node, selector);
			}

			/**
			 * @method testClass
			 * @param {string} className
			 * @param {HTMLElement} node
			 * @return {boolean}
			 * @member ns.util.selectors
			 * @static
			 * @private
			 */
			function testClass(className, node) {
				return node && node.classList && node.classList.contains(className);
			}

			/**
			 * @method testTag
			 * @param {string} tagName
			 * @param {HTMLElement} node
			 * @return {boolean}
			 * @member ns.util.selectors
			 * @static
			 * @private
			 */
			function testTag(tagName, node) {
				return node.tagName.toLowerCase() === tagName;
			}

			/**
			 * @class ns.util.selectors
			 */
			ns.util.selectors = {
				matchesSelector: matchesSelector,

				/**
				 * Return array with children pass by given selector.
				 * @method getChildrenBySelector
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getChildrenBySelector: function (context, selector) {
					return slice.call(context.children).filter(testSelector.bind(null, selector));
				},

				/**
				 * Return array with children pass by given data-namespace-selector.
				 * @method getChildrenByDataNS
				 * @param {HTMLElement} context
				 * @param {string} dataSelector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getChildrenByDataNS: function (context, dataSelector) {
					return slice.call(context.children).filter(testSelector.bind(null,
						getDataSelector(dataSelector)));
				},

				/**
				 * Return array with children with given class name.
				 * @method getChildrenByClass
				 * @param {HTMLElement} context
				 * @param {string} className
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getChildrenByClass: function (context, className) {
					return slice.call(context.children).filter(testClass.bind(null, className));
				},

				/**
				 * Return array with children with given tag name.
				 * @method getChildrenByTag
				 * @param {HTMLElement} context
				 * @param {string} tagName
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getChildrenByTag: function (context, tagName) {
					return slice.call(context.children).filter(testTag.bind(null, tagName));
				},

				/**
				 * Return array with all parents of element.
				 * @method getParents
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getParents: parents,

				/**
				 * Return array with all parents of element pass by given selector.
				 * @method getParentsBySelector
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getParentsBySelector: function (context, selector) {
					return parents(context).filter(testSelector.bind(null, selector));
				},

				/**
				 * Return array with all parents of element pass by given selector with namespace.
				 * @method getParentsBySelectorNS
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getParentsBySelectorNS: function (context, selector) {
					return parents(context).filter(testSelector.bind(null, getDataSelector(selector)));
				},

				/**
				 * Return array with all parents of element with given class name.
				 * @method getParentsByClass
				 * @param {HTMLElement} context
				 * @param {string} className
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getParentsByClass: function (context, className) {
					return parents(context).filter(testClass.bind(null, className));
				},

				/**
				 * Return array with all parents of element with given tag name.
				 * @method getParentsByTag
				 * @param {HTMLElement} context
				 * @param {string} tagName
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getParentsByTag: function (context, tagName) {
					return parents(context).filter(testTag.bind(null, tagName));
				},

				/**
				 * Return first element from parents of element pass by selector.
				 * @method getClosestBySelector
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {HTMLElement}
				 * @static
				 * @member ns.util.selectors
				 */
				getClosestBySelector: function (context, selector) {
					return closest(context, testSelector.bind(null, selector));
				},

				/**
				 * Return first element from parents of element pass by selector with namespace.
				 * @method getClosestBySelectorNS
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {HTMLElement}
				 * @static
				 * @member ns.util.selectors
				 */
				getClosestBySelectorNS: function (context, selector) {
					return closest(context, testSelector.bind(null, getDataSelector(selector)));
				},

				/**
				 * Return first element from parents of element with given class name.
				 * @method getClosestByClass
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {HTMLElement}
				 * @static
				 * @member ns.util.selectors
				 */
				getClosestByClass: function (context, selector) {
					return closest(context, testClass.bind(null, selector));
				},

				/**
				 * Return first element from parents of element with given tag name.
				 * @method getClosestByTag
				 * @param {HTMLElement} context
				 * @param {string} selector
				 * @return {HTMLElement}
				 * @static
				 * @member ns.util.selectors
				 */
				getClosestByTag: function (context, selector) {
					return closest(context, testTag.bind(null, selector));
				},

				/**
				 * Return array of elements from context with given data-selector
				 * @method getAllByDataNS
				 * @param {HTMLElement} context
				 * @param {string} dataSelector
				 * @return {Array}
				 * @static
				 * @member ns.util.selectors
				 */
				getAllByDataNS: function (context, dataSelector) {
					return slice.call(context.querySelectorAll(getDataSelector(dataSelector)));
				},

				/**
				 * Get scrollable parent element
				 * @method getScrollableParent
				 * @param {HTMLElement} element
				 * @return {HTMLElement}
				 * @static
				 * @member ns.util.selectors
				 */
				getScrollableParent: function (element) {
					var overflow,
						style;

					while (element && element !== document.body) {
						style = window.getComputedStyle(element);

						if (style) {
							overflow = style.getPropertyValue("overflow-y");
							if (overflow === "scroll" || (overflow === "auto" &&
								element.scrollHeight > element.clientHeight)) {
								return element;
							}
						}
						element = element.parentNode;
					}
					return null;
				}
			};
			}(window.document, ns));

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Object Utility
 * Object contains functions help work with objects.
 * @class ns.util.object
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function () {
	"use strict";
	
			var object = {
				/**
				 * Copy object to new object
				 * @method copy
				 * @param {Object} orgObject
				 * @return {Object}
				 * @static
				 * @member ns.util.object
				 */
				copy: function (orgObject) {
					return object.merge({}, orgObject);
				},

				/**
				 * Attach fields from second object to first object.
				 * @method fastMerge
				 * @param {Object} newObject
				 * @param {Object} orgObject
				 * @return {Object}
				 * @static
				 * @member ns.util.object
				 */
				fastMerge: function (newObject, orgObject) {
					var key;

					for (key in orgObject) {
						if (orgObject.hasOwnProperty(key)) {
							newObject[key] = orgObject[key];
						}
					}
					return newObject;
				},

				/**
				 * Attach fields from second and next object to first object.
				 * @method merge
				 * @return {Object}
				 * @static
				 * @member ns.util.object
				 */
				merge: function (/* newObject, orgObject, override */) {
					var newObject,
						orgObject,
						override,
						key,
						args = [].slice.call(arguments),
						argsLength = args.length,
						i;

					newObject = args.shift();
					override = true;
					if (typeof arguments[argsLength - 1] === "boolean") {
						override = arguments[argsLength - 1];
						argsLength--;
					}
					for (i = 0; i < argsLength; i++) {
						orgObject = args.shift();
						if (orgObject !== null) {
							for (key in orgObject) {
								if (orgObject.hasOwnProperty(key) && (override || newObject[key] === undefined)) {
									newObject[key] = orgObject[key];
								}
							}
						}
					}
					return newObject;
				},

				/**
				 * Function add to Constructor prototype Base object and add to prototype properties and methods from
				 * prototype object.
				 * @method inherit
				 * @param {Function} Constructor
				 * @param {Function} Base
				 * @param {Object} prototype
				 * @static
				 * @member ns.util.object
				 */
				/* jshint -W083 */
				inherit: function (Constructor, Base, prototype) {
					var basePrototype = new Base(),
						property,
						value;

					for (property in prototype) {
						if (prototype.hasOwnProperty(property)) {
							value = prototype[property];
							if (typeof value === "function") {
								basePrototype[property] = (function createFunctionWithSuper(Base, property, value) {
									var _super = function () {
										var superFunction = Base.prototype[property];

										if (superFunction) {
											return superFunction.apply(this, arguments);
										}
										return null;
									};

									return function () {
										var __super = this._super,
											returnValue;

										this._super = _super;
										returnValue = value.apply(this, arguments);
										this._super = __super;
										return returnValue;
									};
								}(Base, property, value));
							} else {
								basePrototype[property] = value;
							}
						}
					}

					Constructor.prototype = basePrototype;
					Constructor.prototype.constructor = Constructor;
				},

				/**
				 * Returns true if every property value corresponds value from 'value' argument
				 * @method hasPropertiesOfValue
				 * @param {Object} obj
				 * @param {*} [value=undefined]
				 * @return {boolean}
				 */
				hasPropertiesOfValue: function (obj, value) {
					var keys = Object.keys(obj),
						i = keys.length;

					// Empty array should return false
					if (i === 0) {
						return false;
					}

					while (--i >= 0) {
						if (obj[keys[i]] !== value) {
							return false;
						}
					}

					return true;
				},

				/**
				 * Remove properties from object.
				 * @method removeProperties
				 * @param {Object} object
				 * @param {Array} propertiesToRemove
				 * @return {Object}
				 */
				removeProperties: function (object, propertiesToRemove) {
					var length = propertiesToRemove.length,
						property,
						i;

					for (i = 0; i < length; i++) {
						property = propertiesToRemove[i];
						if (object.hasOwnProperty(property)) {
							delete object[property];
						}
					}
					return object;
				}
			};

			ns.util.object = object;
			}());

/*global window, define, ns, Node */
/*jslint nomen: true, plusplus: true, bitwise: false */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Engine
 * Main class with engine of library which control communication
 * between parts of framework.
 * @class ns.engine
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Michal Szepielak <m.szepielak@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author Przemyslaw Ciezkowski <p.ciezkowski@samsung.com>
 * @author Hyunkook, Cho <hk0713.cho@samsung.com>
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 * @author Piotr Ostalski <p.ostalski@samsung.com>
 */
(function (window, document) {
	"use strict";
				var slice = [].slice,
				/**
				 * @property {Object} eventUtils {@link ns.event}
				 * @private
				 * @static
				 * @member ns.engine
				 */
				eventUtils = ns.event,
				util = ns.util,
				objectUtils = util.object,
				selectors = util.selectors,
				arrayUtils = ns.util.array,
				/**
				 * @property {Object} widgetDefinitions Object with widgets definitions
				 * @private
				 * @static
				 * @member ns.engine
				 */
				widgetDefinitions = {},
				/**
				 * @property {Object} widgetBindingMap Object with widgets bindings
				 * @private
				 * @static
				 * @member ns.engine
				 */
				widgetBindingMap = {},
				location = window.location,
				/**
				 * engine mode, if true then engine only builds widgets
				 * @property {boolean} justBuild
				 * @private
				 * @static
				 * @member ns.engine
				 */
				justBuild = location.hash === "#build",
				/**
				 * @property {string} [TYPE_STRING="string"] local cache of string type name
				 * @private
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				TYPE_STRING = "string",
				/**
				 * @property {string} [TYPE_FUNCTION="function"] local cache of function type name
				 * @private
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				TYPE_FUNCTION = "function",
				/**
				 * @property {string} [DATA_BUILT="data-tau-built"] attribute informs that widget id build
				 * @private
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				DATA_BUILT = "data-tau-built",
				/**
				 * @property {string} [DATA_NAME="data-tau-name"] attribute contains widget name
				 * @private
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				DATA_NAME = "data-tau-name",
				/**
				 * @property {string} [DATA_BOUND="data-tau-bound"] attribute informs that widget id bound
				 * @private
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				DATA_BOUND = "data-tau-bound",
				/**
				 * @property {string} NAMES_SEPARATOR
				 * @private
				 * @static
				 * @readonly
				 */
				NAMES_SEPARATOR = ",",
				/**
				 * @property {string} [querySelectorWidgets="*[data-tau-built][data-tau-name]:not([data-tau-bound])"] query selector for all widgets which are built but not bound
				 * @private
				 * @static
				 * @member ns.engine
				 */
				querySelectorWidgets = "*[" + DATA_BUILT + "][" + DATA_NAME + "]:not([" + DATA_BOUND + "])",
				/**
				 * @method excludeBuildAndBound
				 * @private
				 * @static
				 * @param {string} widgetType
				 * @member ns.engine
				 * @return {string} :not([data-tau-built*='widgetName']):not([data-tau-bound*='widgetName'])
				 */
				excludeBuiltAndBound = function (widgetType) {
					return ":not([" + DATA_BUILT + "*='" + widgetType + "']):not([" + DATA_BOUND + "*='" + widgetType + "'])";
				},

				/**
				 * Engine event types
				 * @property {Object} eventType
				 * @property {string} eventType.INIT="tauinit" INIT of framework init event
				 * @property {string} eventType.WIDGET_BOUND="widgetbound" WIDGET_BOUND of widget bound event
				 * @property {string} eventType.WIDGET_DEFINED="widgetdefined" WIDGET_DEFINED of widget built event
				 * @property {string} eventType.WIDGET_BUILT="widgetbuilt" WIDGET_BUILT of widget built event
				 * @property {string} eventType.BOUND="bound" BOUND of bound event
				 * @static
				 * @readonly
				 * @member ns.engine
				 */
				eventType = {
					INIT: "tauinit",
					READY: "tauready",
					WIDGET_BOUND: "widgetbound",
					WIDGET_DEFINED: "widgetdefined",
					WIDGET_BUILT: "widgetbuilt",
					DESTROY: "taudestroy",
					BOUND: "bound",
					WIDGET_INIT: "init",
					STOP_ROUTING: "tauroutingstop"
				},
				engine;

			/**
			 * This function prepares selector for widget' definition
			 * @method selectorChange
			 * @param {string} selectorName
			 * @return {string} new selector
			 * @member ns.engine
			 * @static
			 */
			function selectorChange(selectorName) {
				if (selectorName.match(/\[data-role=/) && !selectorName.match(/:not\(\[data-role=/)) {
					return selectorName.trim();
				}
				return selectorName.trim() + ":not([data-role='none'])";
			}

			/**
			 * Function to define widget
			 * @method defineWidget
			 * @param {string} name
			 * @param {string} selector
			 * @param {Array} methods
			 * @param {Object} widgetClass
			 * @param {string} [namespace]
			 * @param {boolean} [redefine]
			 * @param {boolean} [widgetNameToLowercase=true]
			 * @return {boolean}
			 * @member ns.engine
			 * @static
			 */
			function defineWidget(name, selector, methods, widgetClass, namespace, redefine, widgetNameToLowercase, BaseElement, buildOptions) {
				var definition;
				// Widget name is absolutely required

				buildOptions = buildOptions || {};
				if (name) {
					if (!widgetDefinitions[name] || redefine) {
												methods = methods || [];
						methods.push("destroy", "disable", "enable", "option", "refresh", "value");
						definition = {
							name: name,
							methods: methods,
							selector: selector || "",
							selectors: selector ? selector.split(",").map(selectorChange) : [],
							widgetClass: widgetClass || null,
							namespace: namespace || "",
							widgetNameToLowercase: widgetNameToLowercase === undefined ? true : !!widgetNameToLowercase,
							BaseElement: BaseElement,
							buildOptions: buildOptions
						};

						widgetDefinitions[name] = definition;
						if (namespace) {
							widgetDefinitions[namespace + "." + name] = definition;
						}
						eventUtils.trigger(document, "widgetdefined", definition, false);
						return true;
					}
									} else {
					ns.error("Widget with selector [" + selector + "] defined without a name, aborting!");
				}
				return false;
			}


			/**
			 * Get widget instance from binding for given element and type
			 * @method getInstanceByElement
			 * @static
			 * @param {Object} binding
			 * @param {HTMLElement} element
			 * @param {string} [type] widget name, if is empty then return first built widget
			 * @return {?Object}
			 * @member ns.engine
			 */
			function getInstanceByElement(binding, element, type) {
				var widgetInstance,
					bindingElement,
					storedWidgetNames,
					names = type ? type.split(".") : [],
					name = names.pop(),
					namespace = names.pop();

				// If name is defined it's possible to fetch it instantly
				if (name) {
					widgetInstance = binding.instances[name];
				} else {
					storedWidgetNames = Object.keys(binding.instances);
					widgetInstance = binding.instances[storedWidgetNames[0]];
				}

				if (namespace && widgetInstance && widgetInstance.namespace !== namespace) {
					widgetInstance = null;
				}

				// Return only it instance of the proper widget exists
				if (widgetInstance) {
					
					// Check if widget instance has that same object referenced
					if (widgetInstance.element === element) {
						return widgetInstance;
					}
				}

				return null;
			}

			/**
			 * Get binding for element
			 * @method getBinding
			 * @static
			 * @param {HTMLElement|string} element
			 * @param {string} [type] widget name
			 * @return {?Object}
			 * @member ns.engine
			 */
			function getBinding(element, type) {
				var id = !element || typeof element === TYPE_STRING ? element : element.id,
					binding;

				if (typeof element === TYPE_STRING) {
					element = document.getElementById(id);
				}

				if (element) {
					// Fetch group of widget defined for this element
					binding = widgetBindingMap[id];

					if (binding && typeof binding === "object") {
						return getInstanceByElement(binding, element, type);
					}
				}

				return null;
			}

			/**
			 * Set binding of widget
			 * @method setBinding
			 * @param {ns.widget.BaseWidget} widgetInstance
			 * @static
			 * @member ns.engine
			 */
			function setBinding(widgetInstance) {
				var id = widgetInstance.element.id,
					type = widgetInstance.name,
					widgetBinding = widgetBindingMap[id];

				
				// If the HTMLElement never had a widget declared create an empty object
				if (!widgetBinding) {
					widgetBinding = {
						elementId: id,
						element: widgetInstance.element,
						instances: {}
					};
				}

				widgetBinding.instances[type] = widgetInstance;
				widgetBindingMap[id] = widgetBinding;
			}

			/**
			 * Returns all bindings for element or id gives as parameter
			 * @method getAllBindings
			 * @param {HTMLElement|string} element
			 * @return {?Object}
			 * @static
			 * @member ns.engine
			 */
			function getAllBindings(element) {
				var id = !element || typeof element === TYPE_STRING ? element : element.id;

				return (widgetBindingMap[id] && widgetBindingMap[id].instances) || null;
			}

			/**
			 * Removes given name from attributeValue string.
			 * Names should be separated with a NAMES_SEPARATOR
			 * @param {string} name
			 * @param {string} attributeValue
			 * @private
			 * @static
			 * @return {string}
			 */
			function _removeWidgetNameFromAttribute(name, attributeValue) {
				var widgetNames,
					searchResultIndex;

				// Split attribute value by separator
				widgetNames = attributeValue.split(NAMES_SEPARATOR);
				searchResultIndex = widgetNames.indexOf(name);

				if (searchResultIndex > -1) {
					widgetNames.splice(searchResultIndex, 1);
					attributeValue = widgetNames.join(NAMES_SEPARATOR);
				}

				return attributeValue;
			}

			function _removeAllBindingAttributes(element) {
				element.removeAttribute(DATA_BUILT);
				element.removeAttribute(DATA_BOUND);
				element.removeAttribute(DATA_NAME);
			}

			/**
			 * Remove binding data attributes for element.
			 * @method _removeBindingAttributes
			 * @param {HTMLElement} element
			 * @param {string} type widget type (name)
			 * @private
			 * @static
			 * @member ns.engine
			 */
			function _removeWidgetFromAttributes(element, type) {
				var dataBuilt,
					dataBound,
					dataName;

				// Most often case is that name is not defined
				if (!type) {
					_removeAllBindingAttributes(element);
				} else {
					dataBuilt = _removeWidgetNameFromAttribute(type, element.getAttribute(DATA_BUILT) || "");
					dataBound = _removeWidgetNameFromAttribute(type, element.getAttribute(DATA_BOUND) || "");
					dataName = _removeWidgetNameFromAttribute(type, element.getAttribute(DATA_NAME) || "");

					// Check if all attributes have at least one widget
					if (dataBuilt && dataBound && dataName) {
						element.setAttribute(DATA_BUILT, dataBuilt);
						element.setAttribute(DATA_BOUND, dataBound);
						element.setAttribute(DATA_NAME, dataName);
					} else {
						// If something is missing remove everything
						_removeAllBindingAttributes(element);
					}
				}
			}

			/**
			 * Method removes binding for single widget.
			 * @method _removeSingleBinding
			 * @param {Object} bindingGroup
			 * @param {string} type
			 * @return {boolean}
			 * @private
			 * @static
			 */
			function _removeSingleBinding(bindingGroup, type) {
				var widgetInstance = bindingGroup[type];

				if (widgetInstance) {
					if (widgetInstance.element && typeof widgetInstance.element.setAttribute === TYPE_FUNCTION) {
						_removeWidgetFromAttributes(widgetInstance.element, type);
					}

					delete bindingGroup[type];

					return true;
				}

				return false;
			}

			/**
			 * Remove group of bindings for all types of widgets based on the same element
			 * @method removeGroupBindingAllTypes
			 * @param {Object} bindingGroup
			 * @param {string} id widget element id
			 * @return {boolean}
			 * @static
			 * @member ns.engine
			 */
			function removeGroupBindingAllTypes(bindingGroup, id) {
				var singleSuccess,
					widgetName,
					fullSuccess = true;

				// Iterate over group of created widgets
				for (widgetName in bindingGroup) {
					if (bindingGroup.hasOwnProperty(widgetName)) {
						singleSuccess = _removeSingleBinding(bindingGroup, widgetName);
						
						// As we iterate over keys we are sure we want to remove this element
						// NOTE: Removing property by delete is slower than assigning null value
						bindingGroup[widgetName] = null;

						fullSuccess = (fullSuccess && singleSuccess);
					}
				}

				// If the object bindingGroup is empty or every key has a null value
				if (objectUtils.hasPropertiesOfValue(bindingGroup, null)) {
					// NOTE: Removing property by delete is slower than assigning null value
					widgetBindingMap[id] = null;
				}

				return fullSuccess;
			}

			/**
			 * Remove group of bindings for widgets based on the same element
			 * @method removeGroupBinding
			 * @param {Object} bindingGroup
			 * @param {string} type object name
			 * @param {string} id widget element id
			 * @return {boolean}
			 * @static
			 * @member ns.engine
			 */
			function removeGroupBinding(bindingGroup, type, id) {
				var success;

				if (!type) {
					success = removeGroupBindingAllTypes(bindingGroup, id);
				} else {
					success = _removeSingleBinding(bindingGroup, type);
					if (objectUtils.hasPropertiesOfValue(bindingGroup, null)) {
						widgetBindingMap[id] = null;
					}
				}
				return success;
			}

			/**
			 * Remove binding for widget based on element.
			 * @method removeBinding
			 * @param {HTMLElement|string} element
			 * @param {?string} [type=null] widget name
			 * @return {boolean}
			 * @static
			 * @member ns.engine
			 */
			function removeBinding(element, type) {
				var id = (typeof element === TYPE_STRING) ? element : element.id,
					binding = widgetBindingMap[id],
					bindingGroup;

				// [NOTICE] Due to backward compatibility calling removeBinding
				// with one parameter should remove all bindings

				if (binding) {
					if (typeof element === TYPE_STRING) {
						// Search based on current document may return bad results,
						// use previously defined element if it exists
						element = binding.element;
					}

					if (element) {
						_removeWidgetFromAttributes(element, type);
					}

					bindingGroup = widgetBindingMap[id] && widgetBindingMap[id].instances;
					if (bindingGroup) {
						return removeGroupBinding(bindingGroup, type, id);
					}

					if (widgetBindingMap[id].instances && (Object.keys(widgetBindingMap[id].instances).length === 0)) {
						widgetBindingMap[id] = null;
					}
				}

				return false;
			}

			/**
			 * Removes all bindings of widgets.
			 * @method removeAllBindings
			 * @param {HTMLElement|string} element
			 * @return {boolean}
			 * @static
			 * @member ns.engine
			 */
			function removeAllBindings(element) {
				// @TODO this should be coded in the other way around, removeAll should loop through all bindings and inside call removeBinding
				// but due to backward compatibility that code should be more readable
				return removeBinding(element);
			}

			/**
			 * If element not exist create base element for widget.
			 * @method ensureElement
			 * @param {HTMLElement} element
			 * @param {ns.widget.BaseWidget} Widget
			 * @return {HTMLElement}
			 * @static
			 * @private
			 * @member ns.engine
			 */
			function ensureElement(element, Widget) {
				if (!element || !(element instanceof HTMLElement)) {
					if (typeof Widget.createEmptyElement === TYPE_FUNCTION) {
						element = Widget.createEmptyElement();
					} else {
						element = document.createElement("div");
					}
				}
				return element;
			}

			/**
			 * Process core widget method
			 * - configure
			 * - build
			 * - init
			 * - bindEvents
			 * @method processWidget
			 * @param {HTMLElement} element base element of widget
			 * @param {Object} widgetInstance instance of widget
			 * @param {Object} definition definition of widget
			 * @param {ns.widget.BaseWidget} definition.widgetClass
			 * @param {string} definition.name
			 * @param {Object} [options] options for widget
			 * @private
			 * @static
			 * @member ns.engine
			 */
			function coreProcessWidget(element, widgetInstance, definition, options) {
				var widgetOptions = options || {},
					createFunction = widgetOptions.create,
					buildAttribute;

				
				widgetInstance.configure(definition, element, options);

				// Run .create method from widget options when a [widgetName]create event is triggered
				if (typeof createFunction === TYPE_FUNCTION) {
					eventUtils.one(element, definition.name.toLowerCase() + "create", createFunction);
				}

				if (element.id) {
					widgetInstance.id = element.id;
				}

				// Check if this type of widget was build for this element before
				buildAttribute = element.getAttribute(DATA_BUILT);
				if (!buildAttribute ||
					buildAttribute.split(NAMES_SEPARATOR).indexOf(widgetInstance.name) === -1) {
					element = widgetInstance.build(element);
				}

				if (element) {
					widgetInstance.element = element;

					setBinding(widgetInstance);

					widgetInstance.trigger(eventType.WIDGET_BUILT, widgetInstance, false);

					if (!justBuild) {
						widgetInstance.init(element);
					}

					widgetInstance.bindEvents(element, justBuild);

					widgetInstance.trigger(widgetInstance.widgetEventPrefix + eventType.WIDGET_INIT);
					widgetInstance.trigger(eventType.WIDGET_BOUND, widgetInstance, false);
					eventUtils.trigger(document, eventType.WIDGET_BOUND, widgetInstance);
				} else {
									}
			}

			/**
			 * Load widget
			 * @method processWidget
			 * @param {HTMLElement} element base element of widget
			 * @param {Object} definition definition of widget
			 * @param {ns.widget.BaseWidget} definition.widgetClass
			 * @param {string} definition.name
			 * @param {Object} [options] options for widget
			 * @return {?HTMLElement}
			 * @private
			 * @static
			 * @member ns.engine
			 */
			function processWidget(element, definition, options) {
				var Widget = definition.widgetClass,
					/**
					 * @type {ns.widget.BaseWidget} widgetInstance
					 */
					widgetInstance,
					parentEnhance,
					existingBinding;

				element = ensureElement(element, Widget);
				widgetInstance = Widget ? new Widget(element) : false;

				// if any parent has attribute data-enhance=false then stop building widgets
				parentEnhance = selectors.getParentsBySelectorNS(element, "enhance=false");

				// While processing widgets queue other widget may built this one before
				// it reaches it's turn
				existingBinding = getBinding(element, definition.name);
				if (existingBinding && existingBinding.element === element) {
					return element;
				}

				if (widgetInstance) {
					if (!parentEnhance.length) {
						coreProcessWidget(element, widgetInstance, definition, options);
					}
					return widgetInstance.element;
				}

				return null;
			}

			/**
			 * Destroys widget of given 'type' for given HTMLElement.
			 * [NOTICE] This method won't destroy any children widgets.
			 * @method destroyWidget
			 * @param {HTMLElement|string} element
			 * @param {string} type
			 * @static
			 * @member ns.engine
			 */
			function destroyWidget(element, type) {
				var widgetInstance;

				if (typeof element === TYPE_STRING) {
					element = document.getElementById(element);
				}

				
				// If type is not defined all widgets should be removed
				// this is for backward compatibility
				widgetInstance = getBinding(element, type);

				if (widgetInstance) {
					//Destroy widget
					widgetInstance.destroy();
					widgetInstance.trigger("widgetdestroyed");

					removeBinding(element, type);
				}
			}

			/**
			 * Calls destroy on group of widgets connected with given HTMLElement
			 * @method destroyGroupWidgets
			 * @param {HTMLElement|string} element
			 * @static
			 * @private
			 * @member ns.engine
			 */
			function destroyGroupWidgets(element) {
				var widgetName,
					widgetInstance,
					widgetGroup;

				widgetGroup = getAllBindings(element);
				for (widgetName in widgetGroup) {
					if (widgetGroup.hasOwnProperty(widgetName)) {
						widgetInstance = widgetGroup[widgetName];

						//Destroy widget
						if (widgetInstance) {
							widgetInstance.destroy();
							widgetInstance.trigger("widgetdestroyed");
						}
					}
				}
			}

			/**
			 * Calls destroy on widget (or widgets) connected with given HTMLElement
			 * Removes child widgets as well.
			 * @method destroyAllWidgets
			 * @param {HTMLElement|string} element
			 * @param {boolean} [childOnly=false] destroy only widgets on children elements
			 * @static
			 * @member ns.engine
			 */
			function destroyAllWidgets(element, childOnly) {
				var childWidgets,
					i;

				if (typeof element === TYPE_STRING) {
					element = document.getElementById(element);
				}

				
				if (!childOnly) {
					// If type is not defined all widgets should be removed
					// this is for backward compatibility
					destroyGroupWidgets(element);
				}

				//Destroy child widgets, if something left.
				childWidgets = slice.call(element.querySelectorAll("[" + DATA_BOUND + "]"));
				for (i = childWidgets.length - 1; i >= 0; i -= 1) {
					if (childWidgets[i]) {
						destroyAllWidgets(childWidgets[i], false);
					}
				}

				removeAllBindings(element);
			}

			/**
			 * Load widgets from data-* definition
			 * @method processHollowWidget
			 * @param {HTMLElement} element base element of widget
			 * @param {Object} definition widget definition
			 * @param {Object} [options] options for create widget
			 * @return {HTMLElement} base element of widget
			 * @private
			 * @static
			 * @member ns.engine
			 */
			function processHollowWidget(element, definition, options) {
				var name = (element && element.getAttribute(DATA_NAME)) ||
					(definition && definition.name);
								definition = definition || (name && widgetDefinitions[name]) || {
					"name": name
				};
				return processWidget(element, definition, options);
			}

			/**
			 * Compare function for nodes on build queue
			 * @param {Object} nodeA
			 * @param {Object} nodeB
			 * @return {number}
			 * @private
			 * @static
			 */
			function compareByDepth(nodeA, nodeB) {
				/*jshint -W016 */
				var mask = Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING;

				if (nodeA.element === nodeB.element) {
					return 0;
				}

				if (nodeA.element.compareDocumentPosition(nodeB.element) & mask) {
					return 1;
				}
				/*jshint +W016 */
				return -1;
			}

			/**
			 * Processes one build queue item. Runs processHollowWidget
			 * underneath
			 * @method processBuildQueueItem
			 * @param {Object|HTMLElement} queueItem
			 * @private
			 * @static
			 */
			function processBuildQueueItem(queueItem) {
				// HTMLElement doesn't have .element property
				// widgetDefinitions will return undefined when called widgetDefinitions[undefined]
				processHollowWidget(queueItem.element || queueItem, widgetDefinitions[queueItem.widgetName]);
			}

			/**
			 * Build widgets on all children of context element
			 * @method createWidgets
			 * @static
			 * @param {HTMLElement} context base html for create children
			 * @member ns.engine
			 */
			function createWidgets(context) {
				// find widget which are built
				var builtWidgetElements = slice.call(context.querySelectorAll(querySelectorWidgets)),
					normal,
					buildQueue = [],
					selectorKeys = Object.keys(widgetDefinitions),
					excludeSelector,
					i,
					j,
					len = selectorKeys.length,
					definition,
					widgetName,
					definitionSelectors;

				
				
				// process built widgets
				builtWidgetElements.forEach(processBuildQueueItem);

				// process widgets didn't build
				for (i = 0; i < len; ++i) {
					widgetName = selectorKeys[i];
					if (widgetName.indexOf(".") === -1) {
						definition = widgetDefinitions[widgetName];
						definitionSelectors = definition.selectors;
						if (definitionSelectors.length) {
							excludeSelector = excludeBuiltAndBound(widgetName);

							normal = slice.call(context.querySelectorAll(definitionSelectors.join(excludeSelector + ",") +
							excludeSelector));
							j = normal.length;

							while (--j >= 0) {
								buildQueue.push({
									element: normal[j],
									widgetName: widgetName
								});
							}
						}
					}
				}

				// Sort queue by depth, on every DOM branch outer most element go first
				buildQueue.sort(compareByDepth);

				// Build all widgets from queue
				buildQueue.forEach(processBuildQueueItem);

				
				eventUtils.trigger(document, "built");
				eventUtils.trigger(document, eventType.BOUND);
							}

			/**
			 * Handler for event create
			 * @method createEventHandler
			 * @param {Event} event
			 * @static
			 * @member ns.engine
			 */
			function createEventHandler(event) {
				createWidgets(event.target);
			}

			function setViewport() {
				/**
				 * Sets viewport tag if not exists
				 */
				var documentHead = document.head,
					metaTagListLength,
					metaTagList,
					metaTag,
					i;

				metaTagList = documentHead.querySelectorAll("[name=\"viewport\"]");
				metaTagListLength = metaTagList.length;

				if (metaTagListLength > 0) {
					// Leave the last viewport tag
					--metaTagListLength;

					// Remove duplicated tags
					for (i = 0; i < metaTagListLength; ++i) {
						// Remove meta tag from DOM
						documentHead.removeChild(metaTagList[i]);
					}
				} else {
					// Create new HTML Element
					metaTag = document.createElement("meta");

					// Set required attributes
					metaTag.setAttribute("name", "viewport");
					metaTag.setAttribute("content", "width=device-width, user-scalable=no");

					// Force that viewport tag will be first child of head
					if (documentHead.firstChild) {
						documentHead.insertBefore(metaTag, documentHead.firstChild);
					} else {
						documentHead.appendChild(metaTag);
					}
				}
			}

			/**
			 * Build first page
			 * @method build
			 * @static
			 * @member ns.engine
			 */
			function build() {
				eventUtils.trigger(document, eventType.READY);
				setViewport();
			}

			/**
			 * Method to remove all listeners bound in run
			 * @method stop
			 * @static
			 * @member ns.engine
			 */
			function stop() {
				eventUtils.trigger(document, eventType.STOP_ROUTING);
			}

			/**
			 * Method to remove all listeners bound in run
			 * @method destroy
			 * @static
			 * @member ns.engine
			 */
			function destroy() {
				stop();
				eventUtils.fastOff(document, "create", createEventHandler);
				destroyAllWidgets(document, true);
				eventUtils.trigger(document, eventType.DESTROY);
			}

			/**
			 * Add to object value at index equal to type of arg.
			 * @method getType
			 * @param {Object} result
			 * @param {*} arg
			 * @return {Object}
			 * @static
			 * @private
			 * @member ns.engine
			 */
			function getType(result, arg) {
				var type = arg instanceof HTMLElement ? "HTMLElement" : typeof arg;

				result[type] = arg;
				return result;
			}

			/**
			 * Convert args array to object with keys being types and arguments mapped by values
			 * @method getArgumentsTypes
			 * @param {Arguments[]} args
			 * @return {Object}
			 * @static
			 * @private
			 * @member ns.engine
			 */
			function getArgumentsTypes(args) {
				return arrayUtils.reduce(args, getType, {});
			}

			ns.widgetDefinitions = {};
			engine = {
				justBuild: location.hash === "#build",
				/**
				 * object with names of engine attributes
				 * @property {Object} dataTau
				 * @property {string} [dataTau.built="data-tau-built"] attribute inform that widget id build
				 * @property {string} [dataTau.name="data-tau-name"] attribute contains widget name
				 * @property {string} [dataTau.bound="data-tau-bound"] attribute inform that widget id bound
				 * @property {string} [dataTau.separator=","] separation string for widget names
				 * @static
				 * @member ns.engine
				 */
				dataTau: {
					built: DATA_BUILT,
					name: DATA_NAME,
					bound: DATA_BOUND,
					separator: NAMES_SEPARATOR
				},
				destroyWidget: destroyWidget,
				destroyAllWidgets: destroyAllWidgets,
				createWidgets: createWidgets,

				/**
				 * Method to get all definitions of widgets
				 * @method getDefinitions
				 * @return {Object}
				 * @static
				 * @member ns.engine
				 */
				getDefinitions: function () {
					return widgetDefinitions;
				},
				/**
				 * Returns definition of widget
				 * @method getWidgetDefinition
				 * @param {string} name
				 * @static
				 * @member ns.engine
				 * @return {Object}
				 */
				getWidgetDefinition: function (name) {
					return widgetDefinitions[name];
				},
				defineWidget: defineWidget,
				getBinding: getBinding,
				getAllBindings: getAllBindings,
				setBinding: setBinding,
				removeBinding: removeBinding,
				removeAllBindings: removeAllBindings,

				/**
				 * Clear bindings of widgets
				 * @method _clearBindings
				 * @static
				 * @member ns.engine
				 */
				_clearBindings: function () {
					//clear and set references to the same object
					widgetBindingMap = {};
				},

				build: build,

				/**
				 * Run engine
				 * @method run
				 * @static
				 * @member ns.engine
				 */
				run: function () {
										// stop the TAU process if exists before
					stop();

					eventUtils.fastOn(document, "create", createEventHandler);

					eventUtils.trigger(document, eventType.INIT, {tau: ns});

					switch (document.readyState) {
						case "interactive":
						case "complete":
							// build widgets and initiate router
							build();
							break;
						default:
							// build widgets and initiate router
							eventUtils.one(document, "DOMContentLoaded", build.bind(engine));
							break;
					}
				},

				/**
				 * Build instance of widget and binding events
				 * Returns error when empty element is passed
				 * @method instanceWidget
				 * @param {HTMLElement|string} [element]
				 * @param {string} name
				 * @param {Object} [options]
				 * @return {?Object}
				 * @static
				 * @member ns.engine
				 */
				instanceWidget: function (element, name, options) {
					var binding,
						definition,
						argumentsTypes = getArgumentsTypes(arguments);

					// Map arguments with specific types to correct variables
					// Only name is required argument
					element = argumentsTypes.HTMLElement;
					name = argumentsTypes.string;
					options = argumentsTypes.object;
					// If element exists try to find existing binding
					if (element) {
						binding = getBinding(element, name);
					}
					// If didn't found binding build new widget
					if (!binding && widgetDefinitions[name]) {
						definition = widgetDefinitions[name];
						if (definition.buildOptions.requireMatchSelector &&
							!ns.util.selectors.matchesSelector(element, definition.selector)) {
							return null;
						}
						element = processHollowWidget(element, definition, options);
						binding = getBinding(element, name);
					} else if (binding) {
						// if widget was built early we should set options delivered to constructor
						binding.option(options);
					}
					return binding;
				},

				stop: stop,

				destroy: destroy,

				/**
				 * Method to change build mode
				 * @method setJustBuild
				 * @param {boolean} newJustBuild
				 * @static
				 * @member ns.engine
				 */
				setJustBuild: function (newJustBuild) {
					// Set location hash to have a consistent behavior
					if (newJustBuild) {
						location.hash = "build";
					} else {
						location.hash = "";
					}

					justBuild = newJustBuild;
				},

				/**
				 * Method to get build mode
				 * @method getJustBuild
				 * @return {boolean}
				 * @static
				 * @member ns.engine
				 */
				getJustBuild: function () {
					return justBuild;
				},
				_createEventHandler: createEventHandler
			};

			engine.eventType = eventType;
			ns.engine = engine;
			}(window, window.document));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping namespace
 * Object maps all methods enabling jQuery Mobile API.
 * @class ns.jqm
 */
(function (ns, window) {
	"use strict";
				var eventType = ns.engine.eventType;

			ns.jqm = {
				/**
				 * jQuery object
				 * @property {Object} jQuery
				 * @member ns.jqm
				 */
				jQuery: ns.getConfig("jQuery") || window.jQuery
			};

			/**
			 * Initialize framework in the same way as it is done in jQueryMobile
			 */
			function init() {
				// Tell the world that JQM is ready to serve Tau
				ns.event.trigger(document, "mobileinit");
			}

			/**
			 * Removes events listeners on framework destroy.
			 */
			function destroy() {
				document.removeEventListener(eventType.INIT, init, false);
				document.removeEventListener(eventType.DESTROY, destroy, false);
			}

			document.addEventListener(eventType.INIT, init, false);
			document.addEventListener(eventType.DESTROY, destroy, false);

			}(ns, window));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/**
 * #jQuery Mobile mapping defaults
 * Object maps default values from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.defaults
 */
(function (window, document) {
	"use strict";
				var engineEventsType = ns.engine.eventType,
				$ = ns.jqm.jQuery,
				eventType = {
					INIT: engineEventsType.INIT,
					DESTROY: engineEventsType.DESTROY
				},
				defaults = {
					/**
					 * Proxy colors library from ns namespace to jQM namespace
					 * @method init
					 * @member ns.jqm.defaults
					 * @static
					 */
					init: function () {
						if ($) {

							$.mobile = $.mobile || {};
							// Version of the jQuery Mobile Framework
							//$.mobile.version: __version__;

							// Namespace used framework-wide for data-attrs. Default is no namespace
							$.mobile.ns = "";

							// Define the url parameter used for referencing widget-generated sub-pages.
							// Translates to to example.html&ui-page=subpageIdentifier
							// hash segment before &ui-page= is used to make Ajax request
							$.mobile.subPageUrlKey = "ui-page";

							// Class assigned to page currently in view; and during transitions
							$.mobile.activePageClass = "ui-page-active";

							// Class used for "active" button state; from CSS framework
							$.mobile.activeBtnClass = "ui-btn-active";

							// Class used for "focus" form element state; from CSS framework
							$.mobile.focusClass = "ui-focus";

							// Automatically handle clicks and form submissions through Ajax; when same-domain
							$.mobile.ajaxEnabled = true;

							// Automatically load and show pages based on location.hash
							$.mobile.hashListeningEnabled = true;

							// disable to prevent jquery from bothering with links
							$.mobile.linkBindingEnabled = true;

							// Set default page transition - 'none' for no transitions
							$.mobile.defaultPageTransition = "fade";

							// Set maximum window width for transitions to apply - 'false' for no limit
							$.mobile.maxTransitionWidth = false;

							// Minimum scroll distance that will be remembered when returning to a page
							$.mobile.minScrollBack = 250;

							// DEPRECATED = the following property is no longer in use; but defined until 2.0 to prevent conflicts
							$.mobile.touchOverflowEnabled = false;

							// Set default dialog transition - 'none' for no transitions
							$.mobile.defaultDialogTransition = "pop";

							// Error response message - appears when an Ajax page request fails
							$.mobile.pageLoadErrorMessage = "Error Loading Page";

							// replace calls to window.history.back with phonegaps navigation helper
							// where it is provided on the window object
							$.mobile.phonegapNavigationEnabled = false;

							//automatically initialize the DOM when it's ready
							$.mobile.autoInitializePage = true;

							$.mobile.pushStateEnabled = true;

							// allows users to opt in to ignoring content by marking a parent element as
							// data-ignored
							$.mobile.ignoreContentEnabled = false;

							// turn of binding to the native orientationchange due to android orientation behavior
							$.mobile.orientationChangeEnabled = true;

							$.mobile.tizen = $.mobile.tizen || {};
							$.mobile.tizen.enableHWKeyHandler = true;
						}
					},
					destroy: function () {
						document.removeEventListener(eventType.INIT, defaults.init, false);
						document.removeEventListener(eventType.DESTROY, defaults.destroy, false);
						$ = null;
					}
				};

			// Listen when framework is ready
			document.addEventListener(eventType.INIT, defaults.init, false);
			document.addEventListener(eventType.DESTROY, defaults.destroy, false);

			}(window, window.document));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global define, ns */
/**
 * #String Utility
 * Utility helps work with strings.
 * @class ns.util.string
 */
(function () {
	"use strict";
				var DASH_TO_UPPER_CASE_REGEXP = /-([a-z])/gi,
				UPPER_TO_DASH_CASE_REGEXP = /([A-Z])/g,
				arrayUtil = ns.util.array;

			/**
			 * Callback method for regexp used in dashesToCamelCase method
			 * @method toUpperCaseFn
			 * @param {string} match
			 * @param {string} value
			 * @return {string}
			 * @member ns.util.string
			 * @static
			 * @private
			 */
			function toUpperCaseFn(match, value) {
				return value.toLocaleUpperCase();
			}

			/**
			 * Callback method for regexp used in camelCaseToDashes method
			 * @method toUpperCaseFn
			 * @param {string} match
			 * @param {string} value
			 * @return {string}
			 * @member ns.util.string
			 * @static
			 * @private
			 */
			function toLowerCaseFn(match, value) {
				return "-" + value.toLowerCase();
			}

			/**
			 * Changes dashes string to camel case string
			 * @method firstToUpperCase
			 * @param {string} str
			 * @return {string}
			 * @member ns.util.string
			 * @static
			 */
			function dashesToCamelCase(str) {
				return str.replace(DASH_TO_UPPER_CASE_REGEXP, toUpperCaseFn);
			}

			/**
			 * Changes camel case string to dashes string
			 * @method camelCaseToDashes
			 * @param {string} str
			 * @return {string}
			 * @member ns.util.string
			 * @static
			 */
			function camelCaseToDashes(str) {
				return str.replace(UPPER_TO_DASH_CASE_REGEXP, toLowerCaseFn);
			}

			/**
			 * Changes the first char in string to uppercase
			 * @method firstToUpperCase
			 * @param {string} str
			 * @return {string}
			 * @member ns.util.string
			 * @static
			 */
			function firstToUpperCase(str) {
				return str.charAt(0).toLocaleUpperCase() + str.substring(1);
			}

			/**
			 * Map different types to number if is possible.
			 * @param {string|*} x
			 * @return {*}
			 */
			function mapToNumber(x) {
				var parsed;

				if (x && (x + "").indexOf("%") === -1) {
					parsed = parseInt(x, 10);
					if (isNaN(parsed)) {
						parsed = null;
					}
					return parsed;
				}
				return x;
			}

			/**
			 * Parses comma separated string to array
			 * @method parseProperty
			 * @param {string} property
			 * @return {Array} containing number or null
			 * @member ns.util.string
			 * @static
			 */
			function parseProperty(property) {
				var arrayProperty;

				if (typeof property === "string") {
					arrayProperty = property.split(",");
				} else {
					arrayProperty = property || [];
				}

				return arrayUtil.map(arrayProperty, mapToNumber);
			}

			ns.util.string = {
				dashesToCamelCase: dashesToCamelCase,
				camelCaseToDashes: camelCaseToDashes,
				firstToUpperCase: firstToUpperCase,
				parseProperty: parseProperty
			};
			}());

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, define, ns */
/**
 * #Utility DOM
 * Utility object with function to DOM manipulation, CSS properties support
 * and DOM attributes support.
 *
 * # How to replace jQuery methods  by ns methods
 * ## append vs appendNodes
 *
 * #### HTML code before manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">And</div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * #### jQuery manipulation
 *
 *     @example
 *     $( "#second" ).append( "<span>Test</span>" );

 * #### ns manipulation
 *
 *     @example
 *     var context = document.getElementById("second"),
 *         element = document.createElement("span");
 *     element.innerHTML = "Test";
 *     ns.util.DOM.appendNodes(context, element);
 *
 * #### HTML code after manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">And
 *             <span>Test</span>
 *         </div>
 *        <div id="third">Goodbye</div>
 *     </div>
 *
 * ## replaceWith vs replaceWithNodes
 *
 * #### HTML code before manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">And</div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * #### jQuery manipulation
 *
 *     @example
 *     $('#second').replaceWith("<span>Test</span>");
 *
 * #### ns manipulation
 *
 *     @example
 *     var context = document.getElementById("second"),
 *         element = document.createElement("span");
 *     element.innerHTML = "Test";
 *     ns.util.DOM.replaceWithNodes(context, element);
 *
 * #### HTML code after manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <span>Test</span>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * ## before vs insertNodesBefore
 *
 * #### HTML code before manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">And</div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * #### jQuery manipulation
 *
 *     @example
 *     $( "#second" ).before( "<span>Test</span>" );
 *
 * #### ns manipulation
 *
 *     @example
 *     var context = document.getElementById("second"),
 *         element = document.createElement("span");
 *     element.innerHTML = "Test";
 *     ns.util.DOM.insertNodesBefore(context, element);
 *
 * #### HTML code after manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <span>Test</span>
 *         <div id="second">And</div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * ## wrapInner vs wrapInHTML
 *
 * #### HTML code before manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">And</div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * #### jQuery manipulation
 *
 *     @example
 *     $( "#second" ).wrapInner( "<span class="new"></span>" );
 *
 * #### ns manipulation
 *
 *     @example
 *     var element = document.getElementById("second");
 *     ns.util.DOM.wrapInHTML(element, "<span class="new"></span>");
 *
 * #### HTML code after manipulation
 *
 *     @example
 *     <div>
 *         <div id="first">Hello</div>
 *         <div id="second">
 *             <span class="new">And</span>
 *         </div>
 *         <div id="third">Goodbye</div>
 *     </div>
 *
 * @class ns.util.DOM
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function () {
	"use strict";
				ns.util.DOM = ns.util.DOM || {};
			}());

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/*
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function () {
	"use strict";
	

			var selectors = ns.util.selectors,
				DOM = ns.util.DOM,
				NAMESPACE = "namespace";

			/**
			 * Returns given attribute from element or the closest parent,
			 * which matches the selector.
			 * @method inheritAttr
			 * @member ns.util.DOM
			 * @param {HTMLElement} element
			 * @param {string} attr
			 * @param {string} selector
			 * @return {?string}
			 * @static
			 */
			DOM.inheritAttr = function (element, attr, selector) {
				var value = element.getAttribute(attr),
					parent;

				if (!value) {
					parent = selectors.getClosestBySelector(element, selector);
					if (parent) {
						return parent.getAttribute(attr);
					}
				}
				return value;
			};

			/**
			 * Returns Number from properties described in html tag
			 * @method getNumberFromAttribute
			 * @member ns.util.DOM
			 * @param {HTMLElement} element
			 * @param {string} attribute
			 * @param {string=} [type] auto type casting
			 * @param {number} [defaultValue] default returned value
			 * @static
			 * @return {number}
			 */
			DOM.getNumberFromAttribute = function (element, attribute, type, defaultValue) {
				var value = element.getAttribute(attribute),
					result = defaultValue;

				if (!isNaN(value)) {
					if (type === "float") {
						value = parseFloat(value);
						if (!isNaN(value)) {
							result = value;
						}
					} else {
						value = parseInt(value, 10);
						if (!isNaN(value)) {
							result = value;
						}
					}
				}
				return result;
			};

			function getDataName(name, skipData) {
				var _namespace = ns.getConfig(NAMESPACE),
					prefix = "";

				if (!skipData) {
					prefix = "data-";
				}
				return prefix + (_namespace ? _namespace + "-" : "") + name;
			}

			/**
			 * Special function to set attribute and property in the same time
			 * @method setAttribute
			 * @param {HTMLElement} element
			 * @param {string} name
			 * @param {Mixed} value
			 * @member ns.util.DOM
			 * @static
			 */
			function setAttribute(element, name, value) {
				element[name] = value;
				element.setAttribute(name, value);
			}

			/**
			 * This function sets value of attribute data-{namespace}-{name} for element.
			 * If the namespace is empty, the attribute data-{name} is used.
			 * @method setNSData
			 * @param {HTMLElement} element Base element
			 * @param {string} name Name of attribute
			 * @param {string|number|boolean} value New value
			 * @member ns.util.DOM
			 * @static
			 */
			DOM.setNSData = function (element, name, value) {
				element.setAttribute(getDataName(name), value);
			};

			/**
			 * This function returns value of attribute data-{namespace}-{name} for element.
			 * If the namespace is empty, the attribute data-{name} is used.
			 * Method may return boolean in case of 'true' or 'false' strings as attribute value.
			 * @method getNSData
			 * @param {HTMLElement} element Base element
			 * @param {string} name Name of attribute
			 * @param {boolean} skipData
			 * @member ns.util.DOM
			 * @return {?string|boolean}
			 * @static
			 */
			DOM.getNSData = function (element, name, skipData) {
				var value = element.getAttribute(getDataName(name, skipData));

				if (value === "true") {
					return true;
				}

				if (value === "false") {
					return false;
				}

				return value;
			};

			/**
			 * This function returns true if attribute data-{namespace}-{name} for element is set
			 * or false in another case. If the namespace is empty, attribute data-{name} is used.
			 * @method hasNSData
			 * @param {HTMLElement} element Base element
			 * @param {string} name Name of attribute
			 * @member ns.util.DOM
			 * @return {boolean}
			 * @static
			 */
			DOM.hasNSData = function (element, name) {
				return element.hasAttribute(getDataName(name));
			};

			/**
			 * Get or set value on data attribute.
			 * @method nsData
			 * @param {HTMLElement} element
			 * @param {string} name
			 * @param {?Mixed} [value]
			 * @static
			 * @member ns.util.DOM
			 */
			DOM.nsData = function (element, name, value) {
				if (value === undefined) {
					return DOM.getNSData(element, name);
				} else {
					return DOM.setNSData(element, name, value);
				}
			};

			/**
			 * This function removes attribute data-{namespace}-{name} from element.
			 * If the namespace is empty, attribute data-{name} is used.
			 * @method removeNSData
			 * @param {HTMLElement} element Base element
			 * @param {string} name Name of attribute
			 * @member ns.util.DOM
			 * @static
			 */
			DOM.removeNSData = function (element, name) {
				element.removeAttribute(getDataName(name));
			};

			/**
			 * Returns object with all data-* attributes of element
			 * @method getData
			 * @param {HTMLElement} element Base element
			 * @member ns.util.DOM
			 * @return {Object}
			 * @static
			 */
			DOM.getData = function (element) {
				var dataPrefix = "data-",
					data = {},
					attributes = element.attributes,
					attribute,
					nodeName,
					value,
					i,
					length = attributes.length,
					lowerCaseValue;

				for (i = 0; i < length; i++) {
					attribute = attributes.item(i);
					nodeName = attribute.nodeName;
					if (nodeName.indexOf(dataPrefix) > -1) {
						value = attribute.value;
						lowerCaseValue = value.toLowerCase();
						if (lowerCaseValue === "true") {
							value = true;
						} else if (lowerCaseValue === "false") {
							value = false;
						}
						data[nodeName.replace(dataPrefix, "")] = value;
					}
				}

				return data;
			};

			/**
			 * Special function to remove attribute and property in the same time
			 * @method removeAttribute
			 * @param {HTMLElement} element
			 * @param {string} name
			 * @member ns.util.DOM
			 * @static
			 */
			DOM.removeAttribute = function (element, name) {
				element.removeAttribute(name);
				element[name] = false;
			};

			DOM.setAttribute = setAttribute;
			/**
			 * Special function to set attributes and properties in the same time
			 * @method setAttribute
			 * @param {HTMLElement} element
			 * @param {Object} values
			 * @member ns.util.DOM
			 * @static
			 */
			DOM.setAttributes = function (element, values) {
				var i,
					names = Object.keys(values),
					name,
					len;

				for (i = 0, len = names.length; i < len; i++) {
					name = names[i];
					setAttribute(element, name, values[name]);
				}
			};
			}());

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/*
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function (window, ns) {
	"use strict";
	
			var DOM = ns.util.DOM,
				stringUtil = ns.util.string,
				appStyleSheet;

			/**
			 * Returns css property for element
			 * @method getCSSProperty
			 * @param {HTMLElement} element
			 * @param {string} property
			 * @param {string|number|null} [def=null] default returned value
			 * @param {"integer"|"float"|null} [type=null] auto type casting
			 * @return {string|number|null}
			 * @member ns.util.DOM
			 * @static
			 */
			function getCSSProperty(element, property, def, type) {
				var style = window.getComputedStyle(element),
					value,
					result = def;

				if (style) {
					value = style.getPropertyValue(property);
					if (value) {
						switch (type) {
							case "integer":
								value = parseInt(value, 10);
								if (!isNaN(value)) {
									result = value;
								}
								break;
							case "float":
								value = parseFloat(value);
								if (!isNaN(value)) {
									result = value;
								}
								break;
							default:
								result = value;
								break;
						}
					}
				}
				return result;
			}

			/**
			 * Convert string to float or integer
			 * @param {string} value
			 * @return {number}
			 */
			function convertToNumber(value) {
				if ((value + "").indexOf(".") > -1) {
					return parseFloat(value);
				}
				return parseInt(value, 10);
			}

			/**
			 * Extracts css properties from computed css for an element.
			 * The properties values are applied to the specified
			 * properties list (dictionary)
			 * @method extractCSSProperties
			 * @param {HTMLElement} element
			 * @param {Object} properties
			 * @param {?string} [pseudoSelector=null]
			 * @param {boolean} [noConversion=false]
			 * @member ns.util.DOM
			 * @static
			 */
			function extractCSSProperties(element, properties, pseudoSelector, noConversion) {
				var style = window.getComputedStyle(element, pseudoSelector),
					property,
					value,
					newValue;

				for (property in properties) {
					if (properties.hasOwnProperty(property)) {
						value = style.getPropertyValue(property);
						newValue = convertToNumber(value);

						if (!isNaN(newValue) || !noConversion) {
							value = newValue;
						}

						properties[property] = value;
					}
				}
			}

			function getOffset(element, props, pseudoSelector, force, offsetProperty) {
				var originalDisplay,
					originalVisibility,
					originalPosition,
					offsetValue,
					style = element.style;

				if (style.display !== "none") {
					extractCSSProperties(element, props, pseudoSelector, true);
					offsetValue = element[offsetProperty];
				} else if (force) {
					originalDisplay = style.display;
					originalVisibility = style.visibility;
					originalPosition = style.position;

					style.display = "block";
					style.visibility = "hidden";
					style.position = "relative";

					extractCSSProperties(element, props, pseudoSelector, true);
					offsetValue = element[offsetProperty];

					style.display = originalDisplay;
					style.visibility = originalVisibility;
					style.position = originalPosition;
				}
				return offsetValue;
			}

			/**
			 * Returns elements height from computed style
			 * @method getElementHeight
			 * @param {HTMLElement} element
			 * if null then the "inner" value is assigned
			 * @param {"outer"|null} [type=null]
			 * @param {boolean} [includeOffset=false]
			 * @param {boolean} [includeMargin=false]
			 * @param {?string} [pseudoSelector=null]
			 * @param {boolean} [force=false] check even if element is hidden
			 * @return {number}
			 * @member ns.util.DOM
			 * @static
			 */
			function getElementHeight(element, type, includeOffset, includeMargin, pseudoSelector, force) {
				var height = 0,
					outer = (type && type === "outer") || false,
					offsetHeight,
					property,
					props = {
						"height": 0,
						"margin-top": 0,
						"margin-bottom": 0,
						"padding-top": 0,
						"padding-bottom": 0,
						"border-top-width": 0,
						"border-bottom-width": 0,
						"box-sizing": ""
					};

				if (element) {
					offsetHeight = getOffset(element, props, pseudoSelector, force, "offsetHeight");

					for (property in props) {
						if (props.hasOwnProperty(property) && property !== "box-sizing") {
							props[property] = convertToNumber(props[property]);
						}
					}

					height += props["height"];

					if (props["box-sizing"] !== "border-box") {
						height += props["padding-top"] + props["padding-bottom"];
					}

					if (includeOffset) {
						height = offsetHeight;
					} else if (outer && props["box-sizing"] !== "border-box") {
						height += props["border-top-width"] + props["border-bottom-width"];
					}

					if (includeMargin) {
						height += Math.max(0, props["margin-top"]) + Math.max(0, props["margin-bottom"]);
					}
				}
				return height;
			}

			/**
			 * Returns elements width from computed style
			 * @method getElementWidth
			 * @param {HTMLElement} element
			 * if null then the "inner" value is assigned
			 * @param {"outer"|null} [type=null]
			 * @param {boolean} [includeOffset=false]
			 * @param {boolean} [includeMargin=false]
			 * @param {?string} [pseudoSelector=null]
			 * @param {boolean} [force=false] check even if element is hidden
			 * @return {number}
			 * @member ns.util.DOM
			 * @static
			 */
			function getElementWidth(element, type, includeOffset, includeMargin, pseudoSelector, force) {
				var width = 0,
					value,
					offsetWidth,
					property,
					outer = (type && type === "outer") || false,
					props = {
						"width": 0,
						"margin-left": 0,
						"margin-right": 0,
						"padding-left": 0,
						"padding-right": 0,
						"border-left-width": 0,
						"border-right-width": 0,
						"box-sizing": ""
					};

				if (element) {
					offsetWidth = getOffset(element, props, pseudoSelector, force, "offsetWidth");

					for (property in props) {
						if (props.hasOwnProperty(property) && property !== "box-sizing") {
							value = parseFloat(props[property]);
							props[property] = value;
						}
					}

					width += props["width"];
					if (props["box-sizing"] !== "border-box") {
						width += props["padding-left"] + props["padding-right"];
					}

					if (includeOffset) {
						width = offsetWidth;
					} else if (outer && props["box-sizing"] !== "border-box") {
						width += props["border-left-width"] + props["border-right-width"];
					}

					if (includeMargin) {
						width += Math.max(0, props["margin-left"]) + Math.max(0, props["margin-right"]);
					}
				}
				return width;
			}

			/**
			 * Returns offset of element
			 * @method getElementOffset
			 * @param {HTMLElement} element
			 * @return {Object}
			 * @member ns.util.DOM
			 * @static
			 */
			function getElementOffset(element) {
				var left = 0,
					top = 0,
					loopElement = element;

				do {
					top += loopElement.offsetTop;
					left += loopElement.offsetLeft;
					loopElement = loopElement.offsetParent;
				} while (loopElement !== null);

				return {
					top: top,
					left: left
				};
			}

			/**
			 * Check if element occupies place at view
			 * @method isOccupiedPlace
			 * @param {HTMLElement} element
			 * @return {boolean}
			 * @member ns.util.DOM
			 * @static
			 */
			function isOccupiedPlace(element) {
				return !(element.offsetWidth <= 0 && element.offsetHeight <= 0);
			}

			/**
			 * Set values for element with prefixes for browsers
			 * @method setPrefixedStyle
			 * @param {HTMLElement | CSSStyleRule} elementOrRule
			 * @param {string} property
			 * @param {string|Object|null} value
			 * @member ns.util.DOM
			 * @static
			 */
			function setPrefixedStyle(elementOrRule, property, value) {
				var style = elementOrRule.style,
					propertyForPrefix = property,
					values = (typeof value !== "object") ? {
						webkit: value,
						moz: value,
						o: value,
						ms: value,
						normal: value
					} : value;

				style.setProperty(property, values.normal);
				style.setProperty("-webkit-" + propertyForPrefix, values.webkit);
				style.setProperty("-moz-" + propertyForPrefix, values.moz);
				style.setProperty("-o-" + propertyForPrefix, values.o);
				style.setProperty("-ms-" + propertyForPrefix, values.ms);
			}

			/**
			 * Get value from element with prefixes for browsers
			 * @method getCSSProperty
			 * @param {string} value
			 * @return {Object}
			 * @member ns.util.DOM
			 * @static
			 */
			function getPrefixedValue(value) {
				return {
					webkit: "-webkit-" + value,
					moz: "-moz-" + value,
					o: "-ms-" + value,
					ms: "-o-" + value,
					normal: value
				};
			}

			/**
			 * Returns style value for css property with browsers prefixes
			 * @method getPrefixedStyle
			 * @param {HTMLStyle} styles
			 * @param {string} property
			 * @return {Object}
			 * @member ns.util.DOM
			 * @static
			 */
			function getPrefixedStyleValue(styles, property) {
				var prefixedProperties = getPrefixedValue(property),
					value,
					key;

				for (key in prefixedProperties) {
					if (prefixedProperties.hasOwnProperty(key)) {
						value = styles[prefixedProperties[key]];
						if (value && value !== "none") {
							return value;
						}
					}
				}
				return value;
			}

			/**
			 * Returns size (width, height) as CSS string
			 * @method toCSSSize
			 * @param {string|Array} size has to be comma separated string (eg. "10,100") or array with 2
			 * elements
			 * @return {string} if not enough arguments the method returns empty string
			 * @member ns.util.DOM
			 * @static
			 */
			function toCSSSize(size) {
				var cssSize = "",
					arraySize = stringUtil.parseProperty(size);

				if (arraySize && arraySize.length === 2) {
					cssSize = "width: " + arraySize[0] + "px; " +
					"height: " + arraySize[1] + "px;";
				}

				return cssSize;
			}

			/**
			 * Set CSS styles for pseudo class selector.
			 * @method setStylesForPseudoClass
			 * @param {string} selector selector of elements
			 * @param {string} pseudoClassName CSS pseudo class name to set, for example after, before
			 * @param {Object} cssValues object with styles to set
			 * @return {number?} return index of inserted rule
			 * @member ns.util.DOM
			 * @static
			 */
			function setStylesForPseudoClass(selector, pseudoClassName, cssValues) {
				var cssValuesArray = [],
					headElement,
					styleElement,
					name;

				// create style element on first use
				if (!appStyleSheet) {
					headElement = document.head || document.getElementsByTagName("head")[0];
					styleElement = document.createElement("style");
					styleElement.type = "text/css";
					headElement.appendChild(styleElement);
					appStyleSheet = styleElement.sheet;
				}

				for (name in cssValues) {
					if (cssValues.hasOwnProperty(name)) {
						cssValuesArray.push(name + ": " + cssValues[name]);
					}
				}

				if (cssValuesArray.length) {
					return appStyleSheet.addRule(selector + "::" + pseudoClassName, cssValuesArray.join("; "));
				}

				return null;
			}

			/**
			 * Remove CSS rule from sheet.
			 * @method removeCSSRule
			 * @param {number} ruleIndex Index of rule to remove
			 * @static
			 */
			function removeCSSRule(ruleIndex) {

				// create style element on first use
				if (appStyleSheet) {
					appStyleSheet.deleteRule(ruleIndex);
				}
			}

			// assign methods to namespace
			DOM.getCSSProperty = getCSSProperty;
			DOM.extractCSSProperties = extractCSSProperties;
			DOM.getElementHeight = getElementHeight;
			DOM.getElementWidth = getElementWidth;
			DOM.getElementOffset = getElementOffset;
			DOM.isOccupiedPlace = isOccupiedPlace;
			DOM.setPrefixedStyle = setPrefixedStyle;
			DOM.getPrefixedValue = getPrefixedValue;
			DOM.getPrefixedStyleValue = getPrefixedStyleValue;
			DOM.toCSSSize = toCSSSize;
			DOM.setStylesForPseudoClass = setStylesForPseudoClass;
			DOM.removeCSSRule = removeCSSRule;

			}(window, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/**
 * #Set Utility
 *
 * Own implementation of ECMAScript Set.
 *
 * @class ns.util.Set
 */
(function (window, ns) {
	"use strict";
				var Set = function () {
				this._data = [];
			};

			Set.prototype = {
				/**
				 * Add one or many arguments to set
				 * @method add
				 * @member ns.util.Set
				 */
				add: function () {
					var data = this._data;

					this._data = data.concat.apply(data, [].slice.call(arguments))
						.filter(function (item, pos, array) {
							return array.indexOf(item) === pos;
						});
				},
				/**
				 * Remove all items from set
				 * @method clear
				 * @member ns.util.Set
				 */
				clear: function () {
					this._data = [];
				},
				/**
				 * delete one item from set
				 * @method delete
				 * @param {*} item
				 * @member ns.util.Set
				 */
				delete: function (item) {
					var data = this._data,
						index = data.indexOf(item);

					if (index > -1) {
						data.splice(index, 1);
					}
				},
				/**
				 * Check that item exists in set
				 * @method has
				 * @param {Object} item
				 * @member ns.util.Set
				 * @return {boolean}
				 */
				has: function (item) {
					return this._data.indexOf(item) > -1;
				},
				/**
				 * Iterate on each set elements
				 * @method forEach
				 * @param {Function} cb
				 * @member ns.util.Set
				 */
				forEach: function (cb) {
					this._data.forEach(cb);
				}
			};

			// for tests
			ns.util._Set = Set;
			ns.util.Set = window.Set || Set;

			}(window, ns));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Namespace For Widgets
 * Namespace For Widgets
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @class ns.widget
 */
(function (document) {
	"use strict";
				var engine = ns.engine,
				eventType = engine.eventType,
				widget = {
					/**
					 * Get bound widget for element
					 * @method getInstance
					 * @static
					 * @param {HTMLElement|string} element
					 * @param {string} type widget name
					 * @return {?Object}
					 * @member ns.widget
					 */
					getInstance: engine.getBinding,
					/**
					 * Returns Get all bound widget for element or id gives as parameter
					 * @method getAllInstances
					 * @param {HTMLElement|string} element
					 * @return {?Object}
					 * @static
					 * @member ns.widget
					 */
					getAllInstances: engine.getAllBindings
				};

			function mapWidgetDefinition(name, element, options) {
				var widgetParams = {
					name: name,
					element: element,
					options: options
				}

				return widgetParams;
			}

			function widgetConstructor(name, element, options) {
				var widgetParams = mapWidgetDefinition(name, element, options);

				return engine.instanceWidget(widgetParams.element, widgetParams.name, widgetParams.options);
			}

			/**
			 * Register simple widget constructor in namespace
			 * @param {Event} event
			 */
			function defineWidget(event) {
				var definition = event.detail,
					name = definition.name;

				ns.widget[name] = widgetConstructor.bind(null, name);
			}

			/**
			 * Remove event listeners on framework destroy
			 */
			function destroy() {
				document.removeEventListener(eventType.WIDGET_DEFINED, defineWidget, true);
				document.removeEventListener(eventType.DESTROY, destroy, false);
			}

			document.addEventListener(eventType.WIDGET_DEFINED, defineWidget, true);
			document.addEventListener(eventType.DESTROY, destroy, false);

			/** @namespace ns.widget */
			ns.widget = widget;
			}(window.document));

/*global window, ns, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #BaseWidget
 * Prototype class of widget
 *
 * ## How to invoke creation of widget from JavaScript
 *
 * To build and initialize widget in JavaScript you have to use method
 * {@link ns.engine#instanceWidget}. First argument for method is HTMLElement, which specifies the
 * element of widget. Second parameter is name of widget to create.
 *
 * If you load jQuery before initializing tau library, you can use standard jQuery UI Widget
 * notation.
 *
 * ### Examples
 * #### Build widget from JavaScript
 *
 *        @example
 *        var element = document.getElementById("id"),
 *            ns.engine.instanceWidget(element, "Button");
 *
 * #### Build widget from jQuery
 *
 *        @example
 *        var element = $("#id").button();
 *
 * ## How to create new widget
 *
 *        @example
 *        (function (ns) {
 *			"use strict";
 *			 *					var BaseWidget = ns.widget.BaseWidget, // create alias to main objects
 *						...
 *						arrayOfElements, // example of private property, common for all instances of widget
 *						Button = function () { // create local object with widget
 *							...
 *						},
 *						prototype = new BaseWidget(); // add ns.widget.BaseWidget as prototype to widget's
 *						object, for better minification this should be assign to local variable and next
 *						variable should be assign to prototype of object.
 *
 *					function closestEnabledButton(element) { // example of private method
 *						...
 *					}
 *					...
 *
 *					prototype.options = { //add default options to be read from data- attributes
 *						theme: "s",
 *						...
 *					};
 *
 *					prototype._build = function (template, element) {
 *						// method called when the widget is being built, should contain all HTML
 *						// manipulation actions
 *						...
 *						return element;
 *					};
 *
 *					prototype._init = function (element) {
 *						// method called during initialization of widget, should contain all actions
 *						// necessary fastOn application start
 *						...
 *						return element;
 *					};
 *
 *					prototype._bindEvents = function (element) {
 *						// method to bind all events, should contain all event bindings
 *						...
 *					};
 *
 *					prototype._enable = function (element) {
 *						// method called during invocation of enable() method
 *						...
 *					};
 *
 *					prototype._disable = function (element) {
 *						// method called during invocation of disable() method
 *						...
 *					};
 *
 *					prototype.refresh = function (element) {
 *						// example of public method
 *						...
 *					};
 *
 *					prototype._refresh = function () {
 *						// example of protected method
 *						...
 *					};
 *
 *					Button.prototype = prototype;
 *
 *					engine.defineWidget( // define widget
 *						"Button", //name of widget
 *						"[data-role='button'],button,[type='button'],[type='submit'],[type='reset']",
 *						//widget's selector
 *						[ // public methods, here should be list all public method
 *							"enable",
 *							"disable",
 *							"refresh"
 *						],
 *						Button, // widget's object
 *						"mobile" // widget's namespace
 *					);
 *					ns.widget.Button = Button;
 *					 *		}(ns));
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author Przemyslaw Ciezkowski <p.ciezkowski@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Michał Szepielak <m.szepielak@samsung.com>
 * @class ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var slice = [].slice,
				/**
				 * Alias to ns.engine
				 * @property {ns.engine} engine
				 * @member ns.widget.BaseWidget
				 * @private
				 * @static
				 */
				engine = ns.engine,
				engineDataTau = engine.dataTau,
				util = ns.util,
				/**
				 * Alias to {@link ns.event}
				 * @property {Object} eventUtils
				 * @member ns.widget.BaseWidget
				 * @private
				 * @static
				 */
				eventUtils = ns.event,
				/**
				 * Alias to {@link ns.util.DOM}
				 * @property {Object} domUtils
				 * @private
				 * @static
				 */
				domUtils = util.DOM,
				utilString = util.string,
				/**
				 * Alias to {@link ns.util.object}
				 * @property {Object} objectUtils
				 * @private
				 * @static
				 */
				objectUtils = util.object,
				Set = util.Set,
				BaseWidget = function () {
					this.flowState = "created";
					return this;
				},
				getNSData = domUtils.getNSData,
				prototype = {},
				/**
				 * Property with string represent function type
				 * (for better minification)
				 * @property {string} [TYPE_FUNCTION="function"]
				 * @private
				 * @static
				 * @readonly
				 */
				TYPE_FUNCTION = "function",
				disableClass = "ui-state-disabled",
				ariaDisabled = "aria-disabled",
				__callbacks;

			BaseWidget.classes = {
				disable: disableClass
			};

			prototype._configureDefinition = function (definition) {
				var self = this,
					definitionName,
					definitionNamespace;

				if (definition) {
					definitionName = definition.name;
					definitionNamespace = definition.namespace;
					/**
					 * Name of the widget
					 * @property {string} name
					 * @member ns.widget.BaseWidget
					 */
					self.name = definitionName;

					/**
					 * Name of the widget (in lower case)
					 * @property {string} widgetName
					 * @member ns.widget.BaseWidget
					 */
					self.widgetName = definitionName;

					/**
					 * Namespace of widget events
					 * @property {string} widgetEventPrefix
					 * @member ns.widget.BaseWidget
					 */
					self.widgetEventPrefix = definitionName.toLowerCase();

					/**
					 * Namespace of the widget
					 * @property {string} namespace
					 * @member ns.widget.BaseWidget
					 */
					self.namespace = definitionNamespace;

					/**
					 * Full name of the widget
					 * @property {string} widgetFullName
					 * @member ns.widget.BaseWidget
					 */
					self.widgetFullName = ((definitionNamespace ? definitionNamespace + "-" : "") +
						definitionName).toLowerCase();
					/**
					 * Id of widget instance
					 * @property {string} id
					 * @member ns.widget.BaseWidget
					 */
					self.id = ns.getUniqueId();

					/**
					 * Widget's selector
					 * @property {string} selector
					 * @member ns.widget.BaseWidget
					 */
					self.selector = definition.selector;
				}
			};

			/**
			 * Protected method configuring the widget
			 * @method _configure
			 * @member ns.widget.BaseWidget
			 * @protected
			 * @template
			 * @ignore
			 */
			/**
			 * Configures widget object from definition.
			 *
			 * It calls such methods as #\_getCreateOptions and #\_configure.
			 * @method configure
			 * @param {Object} definition
			 * @param {string} definition.name Name of the widget
			 * @param {string} definition.selector Selector of the widget
			 * @param {HTMLElement} element Element of widget
			 * @param {Object} options Configure options
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 * @ignore
			 */
			prototype.configure = function (definition, element, options) {
				var self = this;
				/**
				 * Object with options for widget
				 * @property {Object} [options={}]
				 * @member ns.widget.BaseWidget
				 */

				self.flowState = "configuring";

				self.options = self.options || {};
				/**
				 * Base element of widget
				 * @property {?HTMLElement} [element=null]
				 * @member ns.widget.BaseWidget
				 */
				self.element = self.element || null;

				self._configureDefinition(definition);

				if (typeof self._configure === TYPE_FUNCTION) {
					self._configure(element);
				}

				self.isCustomElement = !!element.createdCallback;

				self._getCreateOptions(element);

				objectUtils.fastMerge(self.options, options);

				self.flowState = "configured";
			};

			/**
			 * Reads data-* attributes and save to options object.
			 * @method _getCreateOptions
			 * @param {HTMLElement} element Base element of the widget
			 * @return {Object}
			 * @member ns.widget.BaseWidget
			 * @protected
			 */
			prototype._getCreateOptions = function (element) {
				var self = this,
					options = self.options,
					tag = element.localName.toLowerCase();

				if (options) {
					Object.keys(options).forEach(function (option) {
						var attributeName = utilString.camelCaseToDashes(option),
							baseValue = getNSData(element, attributeName, true),
							prefixedValue = getNSData(element, attributeName);

						if (prefixedValue !== null) {
							options[option] = prefixedValue;
						} else {
							if (typeof options[option] === "boolean") {
								self._readBooleanOptionFromElement(element, option);
							}
						}

						if (option === "type" && tag === "input") { // don't set conflicting props
							return;
						}

						if (baseValue !== null) {
							options[option] = baseValue;
						}
					});
				}
				return options;
			};

			/**
			 * Protected method building the widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement} widget's element
			 * @member ns.widget.BaseWidget
			 * @protected
			 * @template
			 */
			/**
			 * Builds widget.
			 *
			 * It calls method #\_build.
			 *
			 * Before starting building process, the event beforecreate with
			 * proper prefix defined in variable widgetEventPrefix is triggered.
			 * @method build
			 * @param {HTMLElement} element Element of widget before building process
			 * @return {HTMLElement} Element of widget after building process
			 * @member ns.widget.BaseWidget
			 * @ignore
			 */
			prototype.build = function (element) {
				var self = this,
					id,
					node,
					dataBuilt = element.getAttribute(engineDataTau.built),
					dataName = element.getAttribute(engineDataTau.name);

				eventUtils.trigger(element, self.widgetEventPrefix + "beforecreate");

				self.flowState = "building";

				id = element.id;
				if (id) {
					self.id = id;
				} else {
					element.id = self.id;
				}

				if (typeof self._build === TYPE_FUNCTION) {
					node = self._build(element);
				} else {
					node = element;
				}

				self._setBooleanOptions(element);

				// Append current widget name to data-tau-built and data-tau-name attributes
				dataBuilt = !dataBuilt ? self.name : dataBuilt + engineDataTau.separator + self.name;
				dataName = !dataName ? self.name : dataName + engineDataTau.separator + self.name;

				element.setAttribute(engineDataTau.built, dataBuilt);
				element.setAttribute(engineDataTau.name, dataName);

				self.flowState = "built";
				return node;
			};

			/**
			 * Protected method initializing the widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @member ns.widget.BaseWidget
			 * @template
			 * @protected
			 */
			/**
			 * Initializes widget.
			 *
			 * It calls method #\_init.
			 * @method init
			 * @param {HTMLElement} element Element of widget before initialization
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 * @ignore
			 */
			prototype.init = function (element) {
				var self = this;

				self.id = element.id;

				self.flowState = "initiating";

				if (typeof self._init === TYPE_FUNCTION) {
					self._init(element);
				}

				if (element.getAttribute("disabled") || self.options.disabled === true) {
					self.disable();
				} else {
					self.enable();
				}
				self.flowState = "initiated";
				return self;
			};

			/**
			 * Returns base element widget
			 * @member ns.widget.BaseWidget
			 * @return {HTMLElement|null}
			 * @instance
			 */
			prototype.getContainer = function () {
				var self = this;

				if (typeof self._getContainer === TYPE_FUNCTION) {
					return self._getContainer();
				}
				return self.element;
			};

			/**
			 * Bind widget events attached in init mode
			 * @method _bindEvents
			 * @param {HTMLElement} element Base element of widget
			 * @member ns.widget.BaseWidget
			 * @template
			 * @protected
			 */
			/**
			 * Binds widget events.
			 *
			 * It calls such methods as #\_buildBindEvents and #\_bindEvents.
			 * At the end of binding process, the event "create" with proper
			 * prefix defined in variable widgetEventPrefix is triggered.
			 * @method bindEvents
			 * @param {HTMLElement} element Base element of the widget
			 * @param {boolean} onlyBuild Inform about the type of bindings: build/init
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 * @ignore
			 */
			prototype.bindEvents = function (element, onlyBuild) {
				var self = this,
					dataBound = element.getAttribute(engineDataTau.bound);

				if (!onlyBuild) {
					dataBound = !dataBound ? self.name : dataBound + engineDataTau.separator + self.name;
					element.setAttribute(engineDataTau.bound, dataBound);
				}
				if (typeof self._buildBindEvents === TYPE_FUNCTION) {
					self._buildBindEvents(element);
				}
				if (!onlyBuild && typeof self._bindEvents === TYPE_FUNCTION) {
					self._bindEvents(element);
				}

				self.trigger(self.widgetEventPrefix + "create", self);

				return self;
			};

			/**
			 * Event triggered when method focus is called
			 * @event taufocus
			 * @member ns.widget.BaseWidget
			 */

			/**
			 * Focus widget's element.
			 *
			 * This function calls function focus on element and if it is known
			 * the direction of event, the proper css classes are added/removed.
			 * @method focus
			 * @param {Object} options The options of event.
			 * @param {HTMLElement} options.previousElement Element to blur
			 * @param {HTMLElement} options.element Element to focus
			 * @member ns.widget.BaseWidget
			 */
			prototype.focus = function (options) {
				var self = this,
					element = self.element,
					blurElement,
					blurWidget;

				options = options || {};

				blurElement = options.previousElement;
				// we try to blur element, which has focus previously
				if (blurElement) {
					blurWidget = engine.getBinding(blurElement);
					// call blur function on widget
					if (blurWidget) {
						options = objectUtils.merge({}, options, {element: blurElement});
						blurWidget.blur(options);
					} else {
						// or on element, if widget does not exist
						blurElement.blur();
					}
				}

				options = objectUtils.merge({}, options, {element: element});

				// set focus on element
				eventUtils.trigger(document, "taufocus", options);
				element.focus();

				return true;
			};

			/**
			 * Event triggered then method blur is called.
			 * @event taublur
			 * @member ns.widget.BaseWidget
			 */

			/**
			 * Blur widget's element.
			 *
			 * This function calls function blur on element and if it is known
			 * the direction of event, the proper css classes are added/removed.
			 * @method blur
			 * @param {Object} options The options of event.
			 * @param {HTMLElement} options.element Element to blur
			 * @member ns.widget.BaseWidget
			 */
			prototype.blur = function (options) {
				var self = this,
					element = self.element;

				options = objectUtils.merge({}, options, {element: element});

				// blur element
				eventUtils.trigger(document, "taublur", options);
				element.blur();
				return true;
			};

			/**
			 * Protected method destroying the widget
			 * @method _destroy
			 * @template
			 * @protected
			 * @member ns.widget.BaseWidget
			 */
			/**
			 * Destroys widget.
			 *
			 * It calls method #\_destroy.
			 *
			 * At the end of destroying process, the event "destroy" with proper
			 * prefix defined in variable widgetEventPrefix is triggered and
			 * the binding set in engine is removed.
			 * @method destroy
			 * @param {HTMLElement} element Base element of the widget
			 * @member ns.widget.BaseWidget
			 */
			prototype.destroy = function (element) {
				var self = this;

				element = element || self.element;

				// the widget is in during destroy process
				self.flowState = "destroying";

				if (typeof self._destroy === TYPE_FUNCTION) {
					self._destroy(element);
				}
				if (self.element) {
					self.trigger(self.widgetEventPrefix + "destroy");
				}
				if (element) {
					engine.removeBinding(element, self.name);
				}
				// the widget was destroyed
				self.flowState = "destroyed";
			};

			/**
			 * Protected method disabling the widget
			 * @method _disable
			 * @protected
			 * @member ns.widget.BaseWidget
			 * @template
			 */
			/**
			 * Disables widget.
			 *
			 * It calls method #\_disable.
			 * @method disable
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 */
			prototype.disable = function () {
				var self = this,
					args = slice.call(arguments),
					element = self.element;

				element.classList.add(disableClass);
				element.setAttribute(ariaDisabled, true);

				if (typeof self._disable === TYPE_FUNCTION) {
					args.unshift(element);
					self._disable.apply(self, args);
				}
				return this;
			};

			/**
			 * Check if widget is disabled.
			 * @method isDisabled
			 * @member ns.widget.BaseWidget
			 * @return {boolean} Returns true if widget is disabled
			 */
			prototype.isDisabled = function () {
				var self = this;

				return self.element.getAttribute("disabled") || self.options.disabled === true;
			};

			/**
			 * Protected method enabling the widget
			 * @method _enable
			 * @protected
			 * @member ns.widget.BaseWidget
			 * @template
			 */
			/**
			 * Enables widget.
			 *
			 * It calls method #\_enable.
			 * @method enable
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 */
			prototype.enable = function () {
				var self = this,
					args = slice.call(arguments),
					element = self.element;

				element.classList.remove(disableClass);
				element.setAttribute(ariaDisabled, false);

				if (typeof self._enable === TYPE_FUNCTION) {
					args.unshift(element);
					self._enable.apply(self, args);
				}
				return this;
			};

			/**
			 * Protected method causing the widget to refresh
			 * @method _refresh
			 * @protected
			 * @member ns.widget.BaseWidget
			 * @template
			 */
			/**
			 * Refreshes widget.
			 *
			 * It calls method #\_refresh.
			 * @method refresh
			 * @member ns.widget.BaseWidget
			 * @return {ns.widget.BaseWidget}
			 */
			prototype.refresh = function () {
				var self = this;

				if (typeof self._refresh === TYPE_FUNCTION) {
					self._refresh.apply(self, arguments);
				}
				return self;
			};

			/**
			 * Reads class based on name conversion option value, for all options which have boolean value
			 * we can read option value by check that exists classname connected with option name. To
			 * correct use this method is required define in widget property _classesPrefix.
			 *
			 * For example for option middle in Button widget we will check existing of class
			 * ui-btn-middle.
			 *
			 * @method _readBooleanOptionFromElement
			 * @param {HTMLElement} element Main element of widget
			 * @param {string} name Name of option which should be used
			 * @return {boolean}
			 * @member ns.widget.BaseWidget
			 * @protected
			 */
			prototype._readBooleanOptionFromElement = function (element, name) {
				var classesPrefix = this._classesPrefix,
					className;

				if (classesPrefix) {
					className = classesPrefix + utilString.camelCaseToDashes(name);
					this.options[name] = element.classList.contains(className);
				}
			};

			/**
			 * Sets or removes class based on name conversion option, for all options which have boolean
			 * value we can just set classname which is converted from camel case to dash style.
			 * To correct use this method is required define in widget property _classesPrefix.
			 *
			 * For example for option middle in Button widget we will set or remove class ui-btn-middle.
			 *
			 * @method _setBooleanOption
			 * @param {HTMLElement} element Main element of widget
			 * @param {string} name Name of option which should be used
			 * @param {boolean} value New value of option to set
			 * @member ns.widget.BaseWidget
			 * @protected
			 * @return {false} always return false to block refreshing
			 */
			prototype._setBooleanOption = function (element, name, value) {
				var classesPrefix = this._classesPrefix,
					className;

				if (classesPrefix) {
					className = classesPrefix + utilString.camelCaseToDashes(name);
					element.classList.toggle(className, value);
				}

				// we don't need refresh, always can return false
				return false;
			};

			/**
			 * For each options which has boolean value set or remove connected class.
			 *
			 * @method _setBooleanOptions
			 * @param {HTMLElement} element Base element of the widget
			 * @return {Object}
			 * @member ns.widget.BaseWidget
			 * @protected
			 */
			prototype._setBooleanOptions = function (element) {
				var self = this,
					classesPrefix = self._classesPrefix,
					options = self.options;

				if (classesPrefix && options !== undefined) {
					Object.keys(options).forEach(function (option) {
						if (typeof options[option] === "boolean") {
							options[option] = self._setBooleanOption(element, option, options[option]);
						}
					});
				}
				return options;
			};

			prototype._processOptionObject = function (firstArgument) {
				var self = this,
					key,
					partResult,
					refresh = false;

				for (key in firstArgument) {
					if (firstArgument.hasOwnProperty(key)) {
						partResult = self._oneOption(key, firstArgument[key]);
						if (key !== undefined && firstArgument[key] !== undefined) {
							refresh = refresh || partResult;
						}
					}
				}
				return refresh;
			};

			/**
			 * Gets or sets options of the widget.
			 *
			 * This method can work in many context.
			 *
			 * If first argument is type of object them, method set values for options given in object.
			 * Keys of object are names of options and values from object are values to set.
			 *
			 * If you give only one string argument then method return value for given option.
			 *
			 * If you give two arguments and first argument will be a string then second argument will be
			 * intemperate as value to set.
			 *
			 * @method option
			 * @param {string|Object} [name] name of option
			 * @param {*} [value] value to set
			 * @member ns.widget.BaseWidget
			 * @return {*} return value of option or null if method is called in setter context
			 */
			prototype.option = function (name, value) {
				var self = this,
					firstArgument = name,
					secondArgument = value,
					result = null,
					refresh = false;

				if (typeof firstArgument === "string") {
					result = self._oneOption(firstArgument, secondArgument);
					if (secondArgument !== undefined) {
						refresh = result;
						result = null;
					}
				} else if (typeof firstArgument === "object") {
					refresh = self._processOptionObject(firstArgument);
				}
				if (refresh) {
					self.refresh();
				}
				return result;
			};

			/**
			 * Gets or sets one option of the widget.
			 *
			 * @method _oneOption
			 * @param {string} field
			 * @param {*} value
			 * @member ns.widget.BaseWidget
			 * @return {*}
			 * @protected
			 */
			prototype._oneOption = function (field, value) {
				var self = this,
					methodName,
					refresh = false;

				if (value === undefined) {
					methodName = "_get" + (field[0].toUpperCase() + field.slice(1));
					if (typeof self[methodName] === TYPE_FUNCTION) {
						return self[methodName]();
					}
					return self.options[field];
				}
				methodName = "_set" + (field[0].toUpperCase() + field.slice(1));
				if (typeof self[methodName] === TYPE_FUNCTION) {
					refresh = self[methodName](self.element, value);
				} else if (typeof value === "boolean") {
					refresh = self._setBooleanOption(self.element, field, value);
				} else {
					self.options[field] = value;
					if (self.element) {
						self.element.setAttribute("data-" + (field.replace(/[A-Z]/g, function (c) {
							return "-" + c.toLowerCase();
						})), value);
						refresh = true;
					}
				}
				return refresh;
			};

			/**
			 * Returns true if widget has bounded events.
			 *
			 * This methods enables to check if the widget has bounded
			 * events through the {@link ns.widget.BaseWidget#bindEvents} method.
			 * @method isBound
			 * @param {string} [type] Type of widget
			 * @member ns.widget.BaseWidget
			 * @ignore
			 * @return {boolean} true if events are bounded
			 */
			prototype.isBound = function (type) {
				var element = this.element;

				type = type || this.name;
				return element && element.hasAttribute(engineDataTau.bound) &&
					element.getAttribute(engineDataTau.bound).indexOf(type) > -1;
			};

			/**
			 * Returns true if widget is built.
			 *
			 * This methods enables to check if the widget was built
			 * through the {@link ns.widget.BaseWidget#build} method.
			 * @method isBuilt
			 * @param {string} [type] Type of widget
			 * @member ns.widget.BaseWidget
			 * @ignore
			 * @return {boolean} true if the widget was built
			 */
			prototype.isBuilt = function (type) {
				var element = this.element;

				type = type || this.name;
				return element && element.hasAttribute(engineDataTau.built) &&
					element.getAttribute(engineDataTau.built).indexOf(type) > -1;
			};

			/**
			 * Protected method getting the value of widget
			 * @method _getValue
			 * @return {*}
			 * @member ns.widget.BaseWidget
			 * @template
			 * @protected
			 */
			/**
			 * Protected method setting the value of widget
			 * @method _setValue
			 * @param {*} value
			 * @return {*}
			 * @member ns.widget.BaseWidget
			 * @template
			 * @protected
			 */
			/**
			 * Gets or sets value of the widget.
			 *
			 * @method value
			 * @param {*} [value] New value of widget
			 * @member ns.widget.BaseWidget
			 * @return {*}
			 */
			prototype.value = function (value) {
				var self = this;

				if (value !== undefined) {
					if (typeof self._setValue === TYPE_FUNCTION) {
						return self._setValue(value);
					}
					return self;
				}
				if (typeof self._getValue === TYPE_FUNCTION) {
					return self._getValue();
				}
				return self;
			};

			/**
			 * Triggers an event on widget's element.
			 *
			 * @method trigger
			 * @param {string} eventName The name of event to trigger
			 * @param {?*} [data] additional Object to be carried with the event
			 * @param {boolean} [bubbles=true] Indicating whether the event
			 * bubbles up through the DOM or not
			 * @param {boolean} [cancelable=true] Indicating whether
			 * the event is cancelable
			 * @member ns.widget.BaseWidget
			 * @return {boolean} False, if any callback invoked preventDefault on event object
			 */
			prototype.trigger = function (eventName, data, bubbles, cancelable) {
				if (this.element) {
					return eventUtils.trigger(this.element, eventName, data, bubbles, cancelable);
				}
			};

			/**
			 * Adds event listener to widget's element.
			 * @method on
			 * @param {string} eventName The name of event
			 * @param {Function} listener Function called after event will be trigger
			 * @param {boolean} [useCapture=false] useCapture Parameter of addEventListener
			 * @member ns.widget.BaseWidget
			 */
			prototype.on = function (eventName, listener, useCapture) {
				eventUtils.on(this.element, eventName, listener, useCapture);
			};

			/**
			 * Removes event listener from  widget's element.
			 * @method off
			 * @param {string} eventName The name of event
			 * @param {Function} listener Function call after event will be trigger
			 * @param {boolean} [useCapture=false] useCapture Parameter of addEventListener
			 * @member ns.widget.BaseWidget
			 */
			prototype.off = function (eventName, listener, useCapture) {
				eventUtils.off(this.element, eventName, listener, useCapture);
			};

			prototype._framesFlow = function () {
				var self = this,
					args = slice.call(arguments),
					func = args.shift();

				if (typeof func === "function") {
					func();
				}
				if (func !== undefined) {
					util.requestAnimationFrame(function frameFlowCallback() {
						self._framesFlow.apply(self, args);
					});
				}
			};

			function callbacksFilter(item) {
				return !item.toRemove;
			}

			function callbacksForEach(item) {
				if (item.object[item.property] === item.value) {
					util.requestAnimationFrame(item.callback.bind(item.object));
					item.toRemove = true;
				}
			}

			function _controlWaitFor() {
				__callbacks.forEach(callbacksForEach);
				__callbacks = __callbacks.filter(callbacksFilter);
				if (__callbacks.length) {
					util.requestAnimationFrame(_controlWaitFor);
				}
			}

			prototype._waitFor = function (property, value, callback) {
				var self = this;

				if (self[property] === value) {
					callback.call(self);
				} else {
					__callbacks = __callbacks || [];
					__callbacks.push({
						object: self,
						property: property,
						value: value,
						callback: callback
					});
				}
				_controlWaitFor();
			};

			function readDOMElementStateClassList(element, stateObject) {
				var classList = stateObject.classList;

				if (classList !== undefined) {
					if (classList instanceof Set) {
						classList.clear();
					} else {
						classList = new Set();
						stateObject.classList = classList;
					}
					if (element.classList.length) {
						classList.add.apply(classList, slice.call(element.classList));
					}
				}
			}

			function readDOMElementState(element, stateObject) {
				readDOMElementStateClassList(element, stateObject);
				if (stateObject.offsetWidth !== undefined) {
					stateObject.offsetWidth = element.offsetWidth;
				}
				if (stateObject.style !== undefined) {
					domUtils.extractCSSProperties(element, stateObject.style, null, true);
				}
				if (stateObject.children !== undefined) {
					stateObject.children.forEach(function (child, index) {
						readDOMElementState(element.children[index], child);
					});
				}
			}

			function render(stateObject, element, isChild, options) {
				var recalculate = false,
					animation = (options) ? options.animation : null;

				if (animation && !animation.active) {
					// Animation has stopped before render
					return false;
				}

				if (stateObject.classList !== undefined) {
					slice.call(element.classList).forEach(function renderRemoveClassList(className) {
						if (!stateObject.classList.has(className)) {
							element.classList.remove(className);
							recalculate = true;
						}
					});
					stateObject.classList.forEach(function renderAddClassList(className) {
						if (!element.classList.contains(className)) {
							element.classList.add(className);
							recalculate = true;
						}
					});
				}
				if (stateObject.style !== undefined) {
					Object.keys(stateObject.style).forEach(function renderUpdateStyle(styleName) {
						element.style[styleName] = stateObject.style[styleName];
					});
				}
				if (stateObject.children !== undefined) {
					stateObject.children.forEach(function renderChildren(child, index) {
						render(child, element.children[index], true);
					});
				}
				if (recalculate && !isChild) {
					util.requestAnimationFrame(readDOMElementState.bind(null, element, stateObject));
				}
			}

			prototype._render = function (now) {
				var self = this,
					stateDOM = self._stateDOM,
					element = self.element,
					animation = self._animation;

				if (now) {
					render(stateDOM, element, false, {animation: animation});
				} else {
					util.requestAnimationFrame(render.bind(null, stateDOM, element, false, {animation: animation}));
				}
			};

			prototype._initDOMstate = function () {
				readDOMElementState(this.element, this._stateDOM);
			};

			prototype._togglePrefixedClass = function (stateDOM, prefix, name) {
				var requireRefresh = false,
					prefixedClassName = prefix + name;

				stateDOM.classList.forEach(function (className) {
					if (className.indexOf(prefix) === 0 && prefixedClassName !== className) {
						stateDOM.classList.delete(className);
						requireRefresh = true;
					}
				});
				if (!stateDOM.classList.has(prefixedClassName)) {
					stateDOM.classList.add(prefixedClassName);
					requireRefresh = true;
				}
				return requireRefresh;
			};

			BaseWidget.prototype = prototype;

			// definition
			ns.widget.BaseWidget = BaseWidget;

			}(window.document, ns));

/*global window, ns, define */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping widget
 * Object maps widgets from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.widget
 */
(function (window, document) {
	"use strict";
				/**
			 * Alias to Array.slice function
			 * @method slice
			 * @member ns.jqm.widget
			 * @private
			 * @static
			 */
			var slice = [].slice,
				$ = ns.jqm.jQuery,
				/**
				 * Wrap function in closure and wrap first argument in jquery object
				 * @param {Function} fn
				 */
				wrapFn = function (fn) {
					return function (el) {
						return fn($(el));
					};
				},
				/**
				 * Alias to ns.engine
				 * @member ns.jqm
				 * @private
				 * @static
				 */
				engine = ns.engine,
				object = ns.util.object,
				jqmWidget = {
					/**
					 * bind widget to jqm
					 * @method init
					 * @param {Object} engine ns.engine class
					 * @param {Object} definition widget definition
					 * @member ns.jqm.widget
					 * @static
					 */
					init: function (engine, definition) {
						var name = (definition.widgetNameToLowercase) ?
							definition.name.toLowerCase() :
							definition.name;

						if ($) {
							document.addEventListener(name + "create", function (event) {
								var element = event.target,
									instance = event.detail,
									data = $(element).data(name);

								if (instance) {
									instance.bindings = {};
									instance.hoverable = {};
									instance.focusable = {};
									instance.document = $(element.style ? element.ownerDocument : element.document || element);
									instance.window = $(instance.document[0].defaultView || instance.document[0].parentWindow);
									object.merge(instance, data);
									$(element).data(name, instance);
								}
							}, true);
							this.processDefinition(definition, engine);
						}
					},

					/**
					 * bind widget to jqm
					 * @method processDefinition
					 * @param {Object} definition widget definition
					 * @param {Object} engine ns.engine class
					 * @member ns.jqm.widget
					 * @static
					 */
					processDefinition: function (definition, engine) {
						/*
						 * name of widget
						 * type string
						 */
						var name = (definition.widgetNameToLowercase) ?
								definition.name.toLowerCase() :
								definition.name,
							/*
							 * list of public methods
							 * type Array
							 */
							methods = definition.methods;

						$.fn[name] = widgetConstructor(engine, name, methods, definition.name);
						if (definition.namespace) {
							$[definition.namespace] = $[definition.namespace] || {};
							$[definition.namespace][definition.name.toLowerCase()] = definition.widgetClass;
						}
						definition = null;
					}
				},
				eventType = engine.eventType;

			function widgetConstructor(engine, name, methods, instanceWidgetName) {
				/*
				 * widget instance
				 * type Object
				 */
				var instance = null;

				return function () {
					/*
					 * function arguments
					 * type Array
					 */
					var args = slice.call(arguments),
						/*
						 * element of jQuery collection
						 * type HTMLElement
						 */
						element,
						/*
						 * is built?
						 * type Boolean
						 */
						built,
						/*
						 * name of method
						 * type string
						 */
						method,
						/*
						 * result value
						 * type mixed
						 */
						resultValue,
						/*
						 * first argument of function
						 * type mixed
						 */
						firstArg,
						i,
						options = {};

					/*
					 * NOTE:
					 * The loop below contains some fixes/hacks for TizenSlider, Listview with FastScroll and AutoDividers
					 * and also Popup, please be aware while refactoring.
					 */
					for (i = 0; i < this.length; i++) {
						element = this.get(i);
						switch (name) {
							case "slider":
								instance = engine.getBinding(element, "Slider") || engine.getBinding(element, "TizenSlider");
								break;
							default:
								instance = engine.getBinding(element, instanceWidgetName);
						}

						built = instance && instance.isBuilt();
						firstArg = args.shift();
						if (firstArg === undefined || typeof firstArg === "object") {
							if (typeof firstArg === "object") {
								options = firstArg;
							}
							if (!instance || !built) {
								engine.instanceWidget(element, instanceWidgetName, options);
							} else {
								instance.option(options);
							}
						} else {
							if (instance === null) {
								return this;
							}
							method = firstArg;
							if (method === "destroy") {
								instance.destroy();
								return this;
							}
							if (methods.indexOf(method) < 0) {
								throw "Method " + method + " does not exist!";
							}
							if (name === "listview" &&
								method === "option" &&
								args[0] === "autodividersSelector" &&
								typeof args[1] === "function") {
								// wrap first argument of callback method in JQuery object
								args[1] = wrapFn(args[1]);
							}
							if (name === "popup" && method === "open") {
								// window.event is used because in Winset we open context popup by
								// $("#pop_text_only").popup("open") after clicking on input
								args[1] = window.event;
							}

							resultValue = instance[method].apply(instance, args);
							if (resultValue !== undefined) {
								if (resultValue !== instance) {
									return resultValue;
								}
							}
						}
					}
					return this;
				};
			}

			/**
			 * Callback for event widgetdefined, register widget in jqm namespace
			 * @param {Event} event
			 */
			function defineWidget(event) {
				jqmWidget.init(engine, event.detail);
			}

			/**
			 * Removes event listeners on framework destroy.
			 */
			function destroy() {
				document.removeEventListener(eventType.WIDGET_DEFINED, defineWidget, false);
				document.removeEventListener(eventType.INIT, defineOldWidgets, false);
				document.removeEventListener(eventType.DESTROY, destroy, false);
			}

			/**
			 * Define widgets which names was changed for backward capability.
			 */
			function defineOldWidgets() {
				engine.defineWidget(
					"FixedToolbar",
					"",
					[],
					ns.widget.Page,
					"mobile"
				);
				engine.defineWidget(
					"pagelayout",
					"",
					[],
					ns.widget.Page,
					"mobile"
				);
				engine.defineWidget(
					"popupwindow",
					"",
					[],
					ns.widget.Popup,
					"tizen"
				);
				engine.defineWidget(
					"ctxpopup",
					"",
					[],
					ns.widget.Popup,
					"tizen"
				);
			}

			document.addEventListener(eventType.WIDGET_DEFINED, defineWidget, false);
			document.addEventListener(eventType.INIT, defineOldWidgets, false);
			document.addEventListener(eventType.DESTROY, destroy, false);

			ns.jqm.widget = jqmWidget;
			}(window, window.document));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Callback Utility
 * Class creates a callback list
 *
 * Create a callback list using the following parameters:
 *  options: an optional list of space-separated options that will change how
 *            the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *    once:            will ensure the callback list can only be fired once (like a Deferred)
 *
 *    memory:            will keep track of previous values and will call any callback added
 *                    after the list has been fired right away with the latest "memorized"
 *                    values (like a Deferred)
 *
 *    unique:            will ensure a callback can only be added once (no duplicate in the list)
 *
 *    stopOnFalse:    interrupt callings when a callback returns false
 * @class ns.util.callbacks
 */
(function (window, document, ns) {
	"use strict";
				ns.util.callbacks = function (orgOptions) {

				var object = ns.util.object,
					options = object.copy(orgOptions),
					/**
					 * Alias to Array.slice function
					 * @method slice
					 * @member ns.util.callbacks
					 * @private
					 */
					slice = [].slice,
					/**
					 * Last fire value (for non-forgettable lists)
					 * @property {Object} memory
					 * @member ns.util.callbacks
					 * @private
					 */
					memory,
					/**
					 * Flag to know if list was already fired
					 * @property {boolean} fired
					 * @member ns.util.callbacks
					 * @private
					 */
					fired,
					/**
					 * Flag to know if list is currently firing
					 * @property {boolean} firing
					 * @member ns.util.callbacks
					 * @private
					 */
					firing,
					/**
					 * First callback to fire (used internally by add and fireWith)
					 * @property {number} [firingStart=0]
					 * @member ns.util.callbacks
					 * @private
					 */
					firingStart,
					/**
					 * End of the loop when firing
					 * @property {number} firingLength
					 * @member ns.util.callbacks
					 * @private
					 */
					firingLength,
					/**
					 * Index of currently firing callback (modified by remove if needed)
					 * @property {number} firingIndex
					 * @member ns.util.callbacks
					 * @private
					 */
					firingIndex,
					/**
					 * Actual callback list
					 * @property {Array} list
					 * @member ns.util.callbacks
					 * @private
					 */
					list = [],
					/**
					 * Stack of fire calls for repeatable lists
					 * @property {Array} stack
					 * @member ns.util.callbacks
					 * @private
					 */
					stack = !options.once && [],
					fire,
					add,
					self = {
						/**
						 * Add a callback or a collection of callbacks to the list
						 * @method add
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						add: function () {
							var start;

							if (list) {
								// First, we save the current length
								start = list.length;

								add(arguments);
								// Do we need to add the callbacks to the
								// current firing batch?
								if (firing) {
									firingLength = list.length;
									// With memory, if we're not firing then
									// we should call right away
								} else if (memory) {
									firingStart = start;
									fire(memory);
								}
							}
							return this;
						},
						/**
						 * Remove a callback from the list
						 * @method remove
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						remove: function () {
							if (list) {
								slice.call(arguments).forEach(function (arg) {
									var index = list.indexOf(arg);

									while (index > -1) {
										list.splice(index, 1);
										// Handle firing indexes
										if (firing) {
											if (index <= firingLength) {
												firingLength--;
											}
											if (index <= firingIndex) {
												firingIndex--;
											}
										}
										index = list.indexOf(arg, index);
									}
								});
							}
							return this;
						},
						/**
						 * Check if a given callback is in the list.
						 * If no argument is given,
						 * return whether or not list has callbacks attached.
						 * @method has
						 * @param {Function} fn
						 * @return {boolean}
						 * @member ns.util.callbacks
						 */
						has: function (fn) {
							return fn ? !!list && list.indexOf(fn) > -1 : !!(list && list.length);
						},
						/**
						 * Remove all callbacks from the list
						 * @method empty
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						empty: function () {
							list = [];
							firingLength = 0;
							return this;
						},
						/**
						 * Have the list do nothing anymore
						 * @method disable
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						disable: function () {
							list = stack = memory = undefined;
							return this;
						},
						/**
						 * Is it disabled?
						 * @method disabled
						 * @return {boolean}
						 * @member ns.util.callbacks
						 */
						disabled: function () {
							return !list;
						},
						/**
						 * Lock the list in its current state
						 * @method lock
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						lock: function () {
							stack = undefined;
							if (!memory) {
								self.disable();
							}
							return this;
						},
						/**
						 * Is it locked?
						 * @method locked
						 * @return {boolean} stack
						 * @member ns.util.callbacks
						 */
						locked: function () {
							return !stack;
						},
						/**
						 * Call all callbacks with the given context and
						 * arguments
						 * @method fireWith
						 * @param {Object} context
						 * @param {Array} args
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						fireWith: function (context, args) {
							if (list && (!fired || stack)) {
								args = args || [];
								args = [context, args.slice ? args.slice() : args];
								if (firing) {
									stack.push(args);
								} else {
									fire(args);
								}
							}
							return this;
						},
						/**
						 * Call all the callbacks with the given arguments
						 * @method fire
						 * @return {ns.util.callbacks} self
						 * @member ns.util.callbacks
						 */
						fire: function () {
							self.fireWith(this, arguments);
							return this;
						},
						/**
						 * To know if the callbacks have already been called at
						 * least once
						 * @method fired
						 * @return {boolean}
						 * @member ns.util.callbacks
						 */
						fired: function () {
							return !!fired;
						}
					};
				/**
				 * Adds functions to the callback list
				 * @method add
				 * @param {...*} argument
				 * @member ns.util.bezierCurve
				 * @private
				 */

				add = function (args) {
					slice.call(args).forEach(function (arg) {
						var type = typeof arg;

						if (type === "function") {
							if (!options.unique || !self.has(arg)) {
								list.push(arg);
							}
						} else if (arg && arg.length && type !== "string") {
							// Inspect recursively
							add(arg);
						}
					});
				};
				/**
				 * Fire callbacks
				 * @method fire
				 * @param {Array} data
				 * @member ns.util.bezierCurve
				 * @private
				 */
				fire = function (data) {
					memory = options.memory && data;
					fired = true;
					firingIndex = firingStart || 0;
					firingStart = 0;
					firingLength = list.length;
					firing = true;
					while (list && firingIndex < firingLength) {
						if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
							memory = false; // To prevent further calls using add
							break;
						}
						firingIndex++;
					}
					firing = false;
					if (list) {
						if (stack) {
							if (stack.length) {
								fire(stack.shift());
							}
						} else if (memory) {
							list = [];
						} else {
							self.disable();
						}
					}
				};

				return self;
			};

			}(window, window.document, ns));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Deferred Utility
 * Class creates object which can call registered callback depend from
 * state of object..
 * @class ns.util.deferred
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
*/(function (window, document, ns) {
	"use strict";
	
			var Deferred = function (callback) {
				var callbacks = ns.util.callbacks,
					object = ns.util.object,
					/**
					 * Register additional action for deferred object
					 * @property {Array} tuples
					 * @member ns.util.deferred
					 * @private
					 */
					tuples = [
						// action, add listener, listener list, final state
						["resolve", "done", callbacks({once: true, memory: true}), "resolved"],
						["reject", "fail", callbacks({once: true, memory: true}), "rejected"],
						["notify", "progress", callbacks({memory: true})]
					],
					state = "pending",
					deferred = {},
					promise = {
						/**
						 * Determine the current state of a Deferred object.
						 * @method state
						 * @return {"pending" | "resolved" | "rejected"} representing the current state
						 * @member ns.util.deferred
						 */
						state: function () {
							return state;
						},
						/**
						 * Add handlers to be called when the Deferred object
						 * is either resolved or rejected.
						 * @method always
						 * @return {ns.util.deferred} self
						 * @member ns.util.deferred
						 */
						always: function () {
							deferred.done(arguments).fail(arguments);
							return this;
						},
						/**
						 * Add handlers to be called when the Deferred object
						 * is resolved, rejected, or still in progress.
						 * @method then
						 * @return {Object} returns a new promise
						 * @member ns.util.deferred
						 */
						then: function () { /* fnDone, fnFail, fnProgress */
							var functions = arguments;

							return new Deferred(function (newDefer) {
								tuples.forEach(function (tuple, i) {
									var fn = (typeof functions[i] === "function") && functions[i];
									// deferred[ done | fail | progress ] for forwarding actions to newDefer

									deferred[tuple[1]](function () {
										var returned = fn && fn.apply(this, arguments);

										if (returned && (typeof returned.promise === "function")) {
											returned.promise()
												.done(newDefer.resolve)
												.fail(newDefer.reject)
												.progress(newDefer.notify);
										} else {
											newDefer[tuple[0] + "With"](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments);
										}
									});
								});
								functions = null;
							}).promise();
						},
						/**
						 * Get a promise for this deferred. If obj is provided,
						 * the promise aspect is added to the object
						 * @method promise
						 * @param {Object} obj
						 * @return {Object} return a Promise object
						 * @member ns.util.deferred
						 */
						promise: function (obj) {
							if (obj) {
								return object.merge(obj, promise);
							}
							return promise;
						}
					};

				/**
				 * alias for promise.then, Keep pipe for back-compat
				 * @method pipe
				 * @member ns.util.deferred
				 */
				promise.pipe = promise.then;

				// Add list-specific methods

				tuples.forEach(function (tuple, i) {
					var list = tuple[2],
						stateString = tuple[3];

					// promise[ done | fail | progress ] = list.add
					promise[tuple[1]] = list.add;

					// Handle state
					if (stateString) {
						list.add(function () {
							// state = [ resolved | rejected ]
							state = stateString;

							// mapping of values [ reject_list | resolve_list ].disable; progress_list.lock
						}, tuples[i ^ 1][2].disable, tuples[2][2].lock);
					}

					// deferred[ resolve | reject | notify ]
					deferred[tuple[0]] = function () {
						deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
						return this;
					};
					deferred[tuple[0] + "With"] = list.fireWith;
				});

				// Make the deferred a promise
				promise.promise(deferred);

				// Call given func if any
				if (callback) {
					callback.call(deferred, deferred);
				}

				// All done!
				return deferred;
			};

			ns.util.deferred = Deferred;
			}(window, window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Globalize Utility
 * Object supports globalize options.
 * @class ns.util.globalize
 */

(function (window, document, ns, Globalize) {
	"use strict";
				var isGlobalizeInit = false,
				cldrDataCategory = {main: "main", supplemental: "supplemental"},
				cldrDataCache = {
					main: {},
					supplemental: {}
				},
				customDataCache = {},
				cldrJsonNames = {
					main: [ //has language dependency
						"currencies",
						"ca-gregorian",
						"numbers"
					],
					supplemental: [
						"scriptMetaData",
						"likelySubtags",
						"currencyData",
						"plurals",
						"timeData",
						"weekData",
						"numberingSystems" // this is for arab locale
					]
				},
				UtilDeferred = ns.util.deferred,
				globalizeInstance = null,
				rtlClassName = "ui-script-direction-rtl",
				extension = ".json",
				cldrDataName = "cldr-data",
				libPath = "lib",
				customLocalePathName = "locale";

			/**
			 * Get filtered path from array
			 * @method pathFilter
			 * @param {Array} path
			 * @return {string}
			 * @private
			 */
			function pathFilter(path) {
				return path.filter(function (item) {
					return item;
				}).join("/");
			}

			/**
			 * Get Language code
			 * @method getLang
			 * @param {string} language
			 * @return {string}
			 * @private
			 */
			function getLang(language) {
				var lang = language ||
						document.getElementsByTagName("html")[0].getAttribute("lang") ||
						window.navigator.language.split(".")[0] || // Webkit, Safari + workaround for Tizen
						"en",
					countryCode,
					countryCodeIdx = lang.lastIndexOf("-"),
					ignoreCodes = ["Cyrl", "Latn", "Mong"];	// Not country code!

				if (countryCodeIdx !== -1) {	// Found country code!
					countryCode = lang.substr(countryCodeIdx + 1);
					if (ignoreCodes.join("-").indexOf(countryCode) < 0) {
						// countryCode is not found from ignoreCodes.
						// Make countryCode to uppercase.
						lang = [lang.substr(0, countryCodeIdx), countryCode.toUpperCase()].join("-");
					}
				}
				// NOTE: "en-US"" to "en" because we do not use CLDR full data TODO:Make Guide document for CLDR full data
				lang = getNeutralLang(lang);
				return lang;
			}

			/**
			 * Get neutral language
			 * @method getNeutralLang
			 * @param {string} lang
			 * @return {string}
			 * @private
			 */
			function getNeutralLang(lang) {
				var neutralLangIdx = lang.lastIndexOf("-"),
					neutralLang;

				if (neutralLangIdx !== -1) {
					neutralLang = lang.substr(0, neutralLangIdx);
				} else {
					neutralLang = lang;
				}
				return neutralLang;
			}

			/**
			 * Get path of CLDR data
			 * @method getCldrFilesPath
			 * @param {string} subPath
			 * @param {string} lang
			 * @param {string} jsonName
			 * @return {string}
			 * @private
			 */
			function getCldrFilesPath(subPath, lang, jsonName) {
				var path;

				lang = (subPath === "supplemental") ? null : lang;

				// Default Globalize culture file path
				path = [
					libPath,
					cldrDataName,
					subPath,
					lang,
					jsonName + extension //TODO:Use gregorian
				];

				return pathFilter(path);
			}

			/**
			 * Get path of Custom data which is matched with language
			 * @method getCustomFilesPath
			 * @param {string} lang
			 * @return {string}
			 * @private
			 */
			function getCustomFilesPath(lang) {
				return pathFilter([
					customLocalePathName,
					lang + extension
				]);
			}

			/**
			 * Loads json file
			 * @method loadJSON
			 * @param {string} path
			 * @return {Deferred}
			 * @private
			 */
			function loadJSON(path) {
				var xhrObj,
					jsonObj,
					info,
					deferred = new UtilDeferred();

				if (path) {	// Invalid path -> Regard it as "404 Not Found" error.
					try {
						xhrObj = new XMLHttpRequest();
						xhrObj.onreadystatechange = function () {
							if (xhrObj.readyState === 4) {
								switch (xhrObj.status) {
									case 0:
									case 200:
										jsonObj = JSON.parse(xhrObj.responseText);
										info = {"state": xhrObj.status, "path": path, "data": jsonObj};
										deferred.resolve(info);
										break;
									case 404:
										info = {"state": xhrObj.status, "path": path, "data": null};
										deferred.reject(info);
										break;
									default:
										jsonObj = JSON.parse(xhrObj.responseText);
										info = {"state": xhrObj.status, "path": path, "data": jsonObj};
										deferred.reject(info);
										break;
								}
							}
						};
						xhrObj.open("GET", path, true);
						xhrObj.send("");
					} catch (e) {
						info = {"state": -1, "path": path, "data": null};
						deferred.reject(info);
					}
				} else {
					info = {"state": -2, "path": path, "data": null};
					deferred.reject(info);

				}
				return deferred;
			}

			/**
			 * Loads CLDR data
			 * @method loadCldrData
			 * @param {string|null} language
			 * @param {string} category
			 * @return {Deferred}
			 * @private
			 */
			function loadCldrData(language, category) {
				var path,
					cldrDataTotal = cldrJsonNames[category].length,
					cache = null,
					deferred = new UtilDeferred();

				if (language) { // when category is "main" , language must have value like "en" , "ko" .etc
					if (!cldrDataCache[category].hasOwnProperty(language)) {
						cache = cldrDataCache[category][language] = {};
					} else {
						cache = cldrDataCache[category][language];
					}
				} else { // when category is "supplement" language is empty
					cache = cldrDataCache[category];
				}

				cldrJsonNames[category].forEach(function (fileName) {

					path = getCldrFilesPath(category, language, fileName);

					if (!cache[path]) {
						loadJSON(path).then(function (info) {
							var jsonObj = info.data,
								key = info.path;

							cache[key] = jsonObj; //cache likelySubtags for Globalize
							Globalize.load(jsonObj); //load likelySubtags json
							if (Object.keys(cache).length === cldrDataTotal) {
								deferred.resolve(language);
							}

						}, deferred.reject);

					} else {
						//Globalize.load(cache[path]);
						deferred.resolve(language);
					}
				});

				return deferred;
			}

			/**
			 * Loads custom data
			 * @method loadCustomData
			 * @param {string} localeId
			 * @return {Deferred}
			 * @private
			 */
			function loadCustomData(localeId) {
				var path = null,
					deferred = new UtilDeferred(),
					cache = customDataCache;

				path = getCustomFilesPath(localeId);
				if (!cache[path]) {
					loadJSON(path).then(function (info) {
						cache[path] = info;
						deferred.resolve(info);
					},
						deferred.reject);
				} else {
					deferred.resolve(cache[path]);
				}
				return deferred;
			}

			/**
			 * Init Globalize
			 * @method initGlobalize
			 * @return {Deferred}
			 * @private
			 */
			function initGlobalize() {
				var deferred = new UtilDeferred();

				isGlobalizeInit = true;
				loadCldrData(null, cldrDataCategory.supplemental).then(deferred.resolve, deferred.reject);
				return deferred;
			}

			/**
			 * Check script direction of locale
			 * @method isRTL
			 * @param {string} locale
			 * @private
			 */
			function isRTL(locale) {
				var path = getCldrFilesPath(cldrDataCategory.supplemental, locale, cldrJsonNames.supplemental[0]),
					scriptMetaData = cldrDataCache.supplemental[path] || null,
					result = null;

				locale = Globalize.locale().attributes.script;

				if (scriptMetaData) {
					scriptMetaData.some(function (item) {
						if (item.IDENTIFIER === locale) {
							switch (item.RTL) {
								case "YES":
									result = true;
									break;
								case "NO":
									result = false;
									break;
								case "UNKNOWN":
									result = true;
									break;
							}
							return true;
						}
						return false;
					});
					return result;
				} else {
					throw new Error("Globalize is not initialized");
				}
			}


			/**
			 * Load Basic Locale files for "locale id" in cldr-data directory
			 * @method loadLocaleData
			 * Language code. ex) en-US, en, ko-KR, ko, If language is not
			 * given,
			 * first. Check window.tizen.systeminfo to get locale information
			 * second. Check language from html "lang" attribute.
			 * @param {string} localeId
			 * @return {Deferred}
			 * @private
			 */
			function loadLocaleData(localeId) {
				var deferred = new UtilDeferred();

				if (!isGlobalizeInit) {
					initGlobalize().then(function () {
						loadLocaleData(localeId).then(function (locale) {
							deferred.resolve(locale);
						}, deferred.reject);
					});
				} else {
					if (window.tizen && !localeId) {
						window.tizen.systeminfo.getPropertyValue("LOCALE", function (locale) {
							var countryLang = locale.country;

							if (countryLang) {
								countryLang = getNeutralLang(countryLang.replace("_", "-")); //TODO: Need to fix local id type
							}
							loadCldrData(countryLang, cldrDataCategory.main).then(function (locale) {
								deferred.resolve(locale);
							}, deferred.reject);
						});
					} else {
						//first  find "lang" attribute in html
						//second find "locale" in navigator in window.navigator
						loadCldrData(localeId, cldrDataCategory.main).then(function (locale) {
							deferred.resolve(locale);
						}, deferred.reject);

					}
				}
				return deferred;
			}

			/**
			 * Update class of body to indicate right-to-left language
			 * @method updateScriptDirectionClass
			 * if give language is rtl type than add class ("ui-script-direction-rtl") in body element
			 * @private
			 */
			function updateScriptDirectionClass() {
				var rtl = isRTL(Globalize.locale().locale),
					body = document.body,
					classList = body.classList;

				if (rtl) {
					if (!classList.contains(rtlClassName)) {
						classList.add(rtlClassName);
					}
					Globalize.prototype.rtl = true;
				} else {
					if (classList.contains(rtlClassName)) {
						classList.remove(rtlClassName);
					}
					Globalize.prototype.rtl = false;
				}
			}

			/**
			 * Update Globalize prototype
			 * @method updateGlobalize
			 * @private
			 */
			function updateGlobalize() {
				Globalize.prototype.getLocale = ns.util.globalize.getLocale;
				Globalize.prototype.getCalendar = ns.util.globalize.getCalendar;
			}

			/**
			 * Check Globalize and Cldr object in window object to use core/util/globalize.js
			 * @method checkDependency
			 * @private
			 */
			function checkDependency() {
				return (window.Globalize && window.Cldr);
			}

			ns.util.globalize = {

				/**
				 * Put the module into module array of core.util.globalize
				 * @method importModule
				 * @param {string} fileName
				 * @member ns.util.globalize
				 * @static
				 */
				importModule: function (fileName) {
					var module = fileName.split("/"),
						path = module.shift(),
						moduleMain = cldrJsonNames.main,
						moduleSupplemental = cldrJsonNames.supplemental,
						i = 0,
						j;

					fileName = module.shift();

					switch (path) {
						case "main":
							for (j = moduleMain.length; i < j; i++) {
								if (moduleMain[i] === fileName) {
									return;
								}
							}
							moduleMain.push(fileName);
							break;
						case "supplemental":
							for (j = moduleSupplemental.length; i < j; i++) {
								if (moduleSupplemental[i] === fileName) {
									return;
								}
							}
							moduleSupplemental.push(fileName);
							break;
					}
				},

				/**
				 * Set Locale. This API is Async API.
				 * Please use deferred callback functions which are returned( .done(), .then() .etc)
				 * @method setLocale
				 * @param {string} localeId
				 * @member ns.util.globalize
				 * @return {Deferred}
				 * @static
				 */
				setLocale: function (localeId) {
					var deferred = new UtilDeferred();

					localeId = getLang(localeId);
					if (checkDependency()) {
						loadLocaleData(localeId)
							.then(function (locale) {
								Globalize.locale(locale);
								globalizeInstance = new Globalize(locale);
								return locale;
							}, deferred.reject)
							.done(function (locale) {
								loadCustomData(locale)
									.then(function (info) {
										Globalize.loadMessages(info.data);
										globalizeInstance = new Globalize(locale);
										deferred.resolve(globalizeInstance);
									}, function () {
										globalizeInstance = new Globalize(locale);
										deferred.resolve(globalizeInstance); //we do not care of failure of "loadCustomData on purpose"
									});
							})
							.done(updateScriptDirectionClass)
							.done(updateGlobalize);

						return deferred;
					} else {
						throw new Error("Globalize is not loaded");
					}

				},

				/**
				 * Get Locale.
				 * @method getLocale
				 * @return {string} Current locale
				 * @member ns.util.globalize
				 * @static
				 */
				getLocale: function () {
					if (checkDependency()) {
						return Globalize.locale().locale;
					} else {
						throw new Error("Globalize is not loaded");
					}

				},

				/**
				 * Get gregorian calendar.
				 * @method getCalendar
				 * @return {Object} gregorian calendar data given locale.
				 * @member ns.util.globalize
				 * @static
				 */
				getCalendar: function () {
					//default is gregorian calendar
					//TODO: Need to implementation in jquery/globalize
					//TODO: Need to implement validation
					if (checkDependency() && globalizeInstance) {
						return globalizeInstance.cldr.main("dates/calendars/gregorian");
					} else {
						throw new Error("Globalize is not initialized");
					}

				}

			};

			}(window, window.document, ns, window.Globalize));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Zoom Utility
 * Object supports enabling and disabling zoom.
 * @class ns.util.zoom
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var meta = document.querySelector("meta[name=viewport]"),
				initialContent = meta && meta.getAttribute("content"),
				disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
				enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
				disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(initialContent),
				zoom = {
					/**
					 * Status of zoom
					 * @property {boolean} enabled
					 * @static
					 * @member ns.util.zoom
					 */
					enabled: !disabledInitially,
					/**
					 * Flag shows actual locked/unlocked status
					 * @property {boolean} [locked=false]
					 * @static
					 * @member ns.util.zoom
					 */
					locked: false,
					/**
					 * Disable zoom
					 * @method disable
					 * @param {boolean} lock
					 * @static
					 * @member ns.util.zoom
					 */
					disable: function (lock) {
						if (!disabledInitially && !zoom.locked) {
							if (meta) {
								meta.setAttribute("content", disabledZoom);
							}
							zoom.enabled = false;
							zoom.locked = lock || false;
						}
					},
					/**
					 * Enable zoom
					 * @method enable
					 * @param {boolean} unlock
					 * @static
					 * @member ns.util.zoom
					 */
					enable: function (unlock) {
						if (!disabledInitially && (!zoom.locked || unlock === true)) {
							if (meta) {
								meta.setAttribute("content", enabledZoom);
							}
							zoom.enabled = true;
							zoom.locked = false;
						}
					},
					/**
					 * Restore zoom
					 * @method restore
					 * @static
					 * @member ns.util.zoom
					 */
					restore: function () {
						if (!disabledInitially) {
							if (meta) {
								meta.setAttribute("content", initialContent);
							}
							zoom.enabled = true;
						}
					}
				};

			ns.util.zoom = zoom;
			}(window, window.document, ns));

/*global window, ns, define, XMLHttpRequest, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Load Utility
 * Object contains function to load external resources.
 * @class ns.util.load
 */
(function (document, ns) {
	"use strict";
	
			/**
			 * Local alias for document HEAD element
			 * @property {HTMLHeadElement} head
			 * @static
			 * @private
			 * @member ns.util.load
			 */
			var head = document.head,
				/**
				 * Local alias for document styleSheets element
				 * @property {HTMLStyleElement} styleSheets
				 * @static
				 * @private
				 * @member ns.util.load
				 */
				styleSheets = document.styleSheets,
				/**
				 * Local alias for ns.util.DOM
				 * @property {Object} utilsDOM Alias for {@link ns.util.DOM}
				 * @member ns.util.load
				 * @static
				 * @private
				 */
				utilDOM = ns.util.DOM,
				getNSData = utilDOM.getNSData,
				setNSData = utilDOM.setNSData,
				load = ns.util.load || {},
				/**
				 * Regular expression for extracting path to the image
				 * @property {RegExp} IMAGE_PATH_REGEXP
				 * @static
				 * @private
				 * @member ns.util.load
				 */
				IMAGE_PATH_REGEXP = /url\((\.\/)?images/gm,
				/**
				 * Regular expression for extracting path to the css
				 * @property {RegExp} CSS_FILE_REGEXP
				 * @static
				 * @private
				 * @member ns.util.load
				 */
				CSS_FILE_REGEXP = /[^/]+\.css$/;

			/**
			 * Load file
			 * (synchronous loading)
			 * @method loadFileSync
			 * @param {string} scriptPath
			 * @param {?Function} successCB
			 * @param {?Function} errorCB
			 * @static
			 * @private
			 * @member ns.util.load
			 */
			function loadFileSync(scriptPath, successCB, errorCB) {
				var xhrObj = new XMLHttpRequest();

				// open and send a synchronous request
				xhrObj.open("GET", scriptPath, false);
				xhrObj.send();
				// add the returned content to a newly created script tag
				if (xhrObj.status === 200 || xhrObj.status === 0) {
					if (typeof successCB === "function") {
						successCB(xhrObj, xhrObj.status);
					}
				} else {
					if (typeof errorCB === "function") {
						errorCB(xhrObj, xhrObj.status, new Error(xhrObj.statusText));
					}
				}
			}

			/**
			 * Callback function on javascript load success
			 * @method scriptSyncSuccess
			 * @private
			 * @static
			 * @param {?Function} successCB
			 * @param {?Function} xhrObj
			 * @param {?string} status
			 * @member ns.util.load
			 */
			function scriptSyncSuccess(successCB, xhrObj, status) {
				var script = document.createElement("script");

				script.type = "text/javascript";
				script.text = xhrObj.responseText;
				document.body.appendChild(script);
				if (typeof successCB === "function") {
					successCB(xhrObj, status);
				}
			}


			/**
			 * Add script to document
			 * (synchronous loading)
			 * @method scriptSync
			 * @param {string} scriptPath
			 * @param {?Function} successCB
			 * @param {?Function} errorCB
			 * @static
			 * @member ns.util.load
			 */
			function scriptSync(scriptPath, successCB, errorCB) {
				loadFileSync(scriptPath, scriptSyncSuccess.bind(null, successCB), errorCB);
			}

			/**
			 * Callback function on css load success
			 * @method cssSyncSuccess
			 * @param {string} cssPath
			 * @param {?Function} successCB
			 * @param {?Function} xhrObj
			 * @member ns.util.load
			 * @static
			 * @private
			 */
			function cssSyncSuccess(cssPath, successCB, xhrObj) {
				var css = document.createElement("style");

				css.type = "text/css";
				css.textContent = xhrObj.responseText.replace(
					IMAGE_PATH_REGEXP,
					"url(" + cssPath.replace(CSS_FILE_REGEXP, "images")
				);
				if (typeof successCB === "function") {
					successCB(css);
				}
			}

			/**
			 * Add css to document
			 * (synchronous loading)
			 * @method cssSync
			 * @param {string} cssPath
			 * @param {?Function} successCB
			 * @param {?Function} errorCB
			 * @static
			 * @private
			 * @member ns.util.load
			 */
			function cssSync(cssPath, successCB, errorCB) {
				loadFileSync(cssPath, cssSyncSuccess.bind(null, cssPath, successCB), errorCB);
			}

			/**
			 * Add element to head tag
			 * @method addElementToHead
			 * @param {HTMLElement} element
			 * @param {boolean} [asFirstChildElement=false]
			 * @member ns.util.load
			 * @static
			 */
			function addElementToHead(element, asFirstChildElement) {
				var firstElement;

				if (head) {
					if (asFirstChildElement) {
						firstElement = head.firstElementChild;
						if (firstElement) {
							head.insertBefore(element, firstElement);
							return;
						}
					}
					head.appendChild(element);
				}
			}

			/**
			 * Create HTML link element with href
			 * @method makeLink
			 * @param {string} href
			 * @return {HTMLLinkElement}
			 * @member ns.util.load
			 * @static
			 */
			function makeLink(href) {
				var cssLink = document.createElement("link");

				cssLink.setAttribute("rel", "stylesheet");
				cssLink.setAttribute("href", href);
				cssLink.setAttribute("name", "tizen-theme");
				return cssLink;
			}

			/**
			 * Adds the given node to document head or replaces given 'replaceElement'.
			 * Additionally adds 'name' and 'theme-name' attribute
			 * @param {HTMLElement} node Element to be placed as theme link
			 * @param {string} themeName Theme name passed to the element
			 * @param {HTMLElement} [replaceElement=null] If replaceElement is given it gets replaced by node
			 */
			function addNodeAsTheme(node, themeName, replaceElement) {
				setNSData(node, "name", "tizen-theme");
				setNSData(node, "theme-name", themeName);

				if (replaceElement) {
					replaceElement.parentNode.replaceChild(node, replaceElement);
				} else {
					addElementToHead(node, true);
				}
			}

			/**
			 * Add css link element to head if not exists
			 * @method themeCSS
			 * @param {string} path
			 * @param {string} themeName
			 * @param {boolean} [embed=false] Embeds the CSS content to the document
			 * @member ns.util.load
			 * @static
			 */
			function themeCSS(path, themeName, embed) {
				var i,
					styleSheetsLength = styleSheets.length,
					ownerNode,
					previousElement = null,
					linkElement;
				// Find css link or style elements

				for (i = 0; i < styleSheetsLength; i++) {
					ownerNode = styleSheets[i].ownerNode;

					// We try to find a style / link node that matches current style or is linked to
					// the proper theme. We cannot use ownerNode.href because this returns the absolute path
					if (getNSData(ownerNode, "name") === "tizen-theme" || ownerNode.getAttribute("href") === path) {
						if (getNSData(ownerNode, "theme-name") === themeName) {
							// Nothing to change
							return;
						}
						previousElement = ownerNode;
						break;
					}
				}

				if (embed) {
					// Load and replace old styles or append new styles
					cssSync(path, function onSuccess(styleElement) {
						addNodeAsTheme(styleElement, themeName, previousElement);
					}, function onFailure(xhrObj, xhrStatus) {
						ns.warn("There was a problem when loading '" + themeName + "', status: " + xhrStatus);
					});
				} else {
					linkElement = makeLink(path);
					addNodeAsTheme(linkElement, themeName, previousElement);
				}
			}

			/**
			 * In debug mode add time to url to disable cache
			 * @property {string} cacheBust
			 * @member ns.util.load
			 * @static
			 */
			load.cacheBust = (document.location.href.match(/debug=true/)) ? "?cacheBust=" + (new Date()).getTime() : "";
			// the binding a local methods with the namespace
			load.scriptSync = scriptSync;
			load.addElementToHead = addElementToHead;
			load.makeLink = makeLink;
			load.themeCSS = themeCSS;

			ns.util.load = load;
			}(window.document, ns));

/*global window, define, ns*/
/*jslint bitwise: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Framework Data Object
 * Object contains properties describing run time environment.
 * @class ns.frameworkData
 */
(function (document, ns) {
	"use strict";
				var slice = Array.prototype.slice,
				FRAMEWORK_WEBUI = "tizen-web-ui-fw",
				FRAMEWORK_TAU = "tau",
				IS_TAU_REGEXP = /(^|[\\\/])(tau(\.full|\.mvc)?(\.min)?\.js)$/,
				// Regexp detect framework js file
				LIB_FILENAME_REGEXP = /(^|[\\\/])(tau|tizen-web-ui-fw)(\.full|\.mvc|\.custom)?(\.min)?\.js$/,
				// Regexp detect framework css file
				CSS_FILENAME_REGEXP = /(^|[\\\/])(tau|tizen-web-ui-fw)(\.full|\.mvc|\.custom)?(\.min)?\.css$/,
				// Regexp detect correct theme name
				TIZEN_THEMES_REGEXP = /^(changeable|white|black|default)$/i,
				MINIFIED_REGEXP = /\.min\.js$/,
				frameworkData = {
					/**
					 * The name of framework
					 * @property {string} frameworkName="tizen-web-ui-fw"
					 * @member ns.frameworkData
					 * @static
					 */
					frameworkName: FRAMEWORK_WEBUI,
					/**
					 * The root directory of framework on current device
					 * @property {string} rootDir="/usr/share/tizen-web-ui-fw"
					 * @member ns.frameworkData
					 * @static
					 */
					rootDir: "/usr/share/" + FRAMEWORK_WEBUI,
					/**
					 * The version of framework
					 * @property {string} version="latest"
					 * @member ns.frameworkData
					 * @static
					 */
					version: "latest",
					/**
					 * The theme of framework
					 * @property {string} theme="default"
					 * @member ns.frameworkData
					 * @static
					 */
					theme: "default",
					/**
					 * Tells if the theme that is set was already loaded
					 * @property {boolean} themeLoaded=false
					 * @member ns.frameworkData
					 * @static
					 */
					themeLoaded: false,
					/**
					 * The default width of viewport in framework.
					 * @property {number} defaultViewportWidth=360
					 * @member ns.frameworkData
					 * @static
					 */
					defaultViewportWidth: 360,
					/**
					 * The type of width of viewport in framework.
					 * @property {string} viewportWidth="device-width"
					 * @member ns.frameworkData
					 * @static
					 */
					viewportWidth: "device-width",
					/**
					 * Determines whether the viewport should be scaled
					 * @property {boolean} isMinified=false
					 * @member ns.frameworkData
					 * @static
					 */
					viewportScale: false,
					/**
					 * The default font size in framework.
					 * @property {number} defaultFontSize=22
					 * @member ns.frameworkData
					 * @static
					 */
					defaultFontSize: 22,
					/**
					 * Determines whether the framework is minified
					 * @property {boolean} minified=false
					 * @member ns.frameworkData
					 * @static
					 */
					minified: false,
					/**
					 * Determines the capability of device
					 * @property {Object} deviceCapa
					 * @property {boolean} deviceCapa.inputKeyBack=true
					 * Determines whether the back key is supported.
					 * @property {boolean} deviceCapa.inputKeyMenu=true
					 *  Determines whether the menu key is supported.
					 * @member ns.frameworkData
					 * @static
					 */
					deviceCapa: {inputKeyBack: true, inputKeyMenu: true},
					/**
					 * Determines whether the framework is loaded in debug profile.
					 * @property {boolean} debug=false
					 * @member ns.frameworkData
					 * @static
					 */
					debug: false,
					/**
					 * The version of framework's package
					 * @property {string} pkgVersion="0.2.83"
					 * @member ns.frameworkData
					 * @static
					 */
					pkgVersion: "0.2.83",
					/**
					 * The prefix of data used in framework
					 * @property {string} dataPrefix="data-framework-"
					 * @member ns.frameworkData
					 * @static
					 */
					dataPrefix: "data-framework-",
					/**
					 * The profile of framework
					 * @property {string} profile=""
					 * @member ns.frameworkData
					 * @static
					 */
					profile: ""
				};

			/**
			 * Get data-* params from <script> tag, and set tizen.frameworkData.* values
			 * Returns true if proper <script> tag is found, or false if not.
			 * @method getParams
			 * @return {boolean} Returns true if proper <script> tag is found, or false if not.
			 * @member ns.frameworkData
			 * @static
			 */
			frameworkData.getParams = function () {
				var self = this,
					dataPrefix = self.dataPrefix,
					scriptElements = slice.call(document.querySelectorAll("script[src]")),
					cssElements = slice.call(document.styleSheets),
					themeLoaded = false,
					theme;

				/**
				 * Following cases should be covered here (by recognizing on-page css files).
				 * The final theme and themePath values are determined after going through all script elements
				 *
				 *
				 * none                                       -> theme: null
				 * <link href="theme.css" />                  -> theme: null
				 * <link href="default/theme.css" />          -> theme: null
				 * <link href="tau.css" />                    -> theme: null
				 * <link href="white/tau.min.css" />          -> theme: "white"
				 * <link href="other/path/black/tau.css" />   -> theme: "black"
				 * <link href="other/path/black/tau.css" />   -> theme: "black"
				 * <link href="other/path/black/other.css" /> -> theme: null
				 * <link href="other/path/black/other.css" data-theme-name="white" />     -> theme: "white"
				 * @method findThemeInLinks
				 * @param {CSSStyleSheet} styleSheet
				 */
				// @TODO write unit tests for covering those cases
				function findThemeInLinks(styleSheet) {
					var cssElement = styleSheet.ownerNode,
						dataThemeName = cssElement.getAttribute("data-theme-name"),
						// Attribute value is taken because href property gives different output
						href = cssElement.getAttribute("href"),
						hrefFragments = href && href.split("/"),
						hrefDirPart;

					// If we have the theme name defined we can use it right away
					// without thinking about the naming convention
					if (dataThemeName) {
						if (TIZEN_THEMES_REGEXP.test(dataThemeName)) {
							theme = dataThemeName;
						}
					} else if (href && CSS_FILENAME_REGEXP.test(href)) {
						// We try to find file matching library theme CSS
						// If we have the theme name defined we can use it right away
						// We can only determine the current theme using path based approach when the .css file
						// is located in at least one directory
						if (hrefFragments.length >= 2) {
							// When the second to last element matches known themes set the theme to that name
							hrefDirPart = hrefFragments.slice(-2)[0].match(TIZEN_THEMES_REGEXP);
							theme = hrefDirPart && hrefDirPart[0];
						}
					}

					// In case a theme was found (here or in a previous stylesheet) this will be true
					themeLoaded = themeLoaded || !!theme;
				}

				/**
				 * Sets framework data based on found framework library
				 * @param {HTMLElement} scriptElement
				 */
				// @TODO write unit cases
				function findFrameworkDataInScripts(scriptElement) {
					var src = scriptElement.getAttribute("src"),
						profileName = "",
						frameworkName,
						themePath,
						jsPath;

					// Check if checked file is a known framework
					// no need to check if src exists because of the query selector
					if (LIB_FILENAME_REGEXP.test(src)) {

						// Priority:
						// 1. theme loaded with css
						// 2. theme from attribute
						// 3. default theme
						theme = theme || scriptElement.getAttribute(dataPrefix + "theme") || self.theme;

						theme = theme.toLowerCase();

						if (IS_TAU_REGEXP.test(src)) {
							frameworkName = FRAMEWORK_TAU;
							// Get profile name.
							// Profile may be defined from framework script or
							// it can be assumed, that profile name is second up directory name
							// e.g. pathToLib/profileName/js/tau.js
							profileName = scriptElement.getAttribute(dataPrefix + "profile") || src.split("/").slice(-3)[0];
							themePath = "/" + profileName + "/theme/" + theme;

							// TAU framework library link
							jsPath = "/" + profileName + "/js";
						} else {
							// tizen-web-ui framework
							frameworkName = FRAMEWORK_WEBUI;
							themePath = "/latest/themes/" + theme;
							jsPath = "/latest/js";
						}

						self.rootDir = scriptElement.getAttribute(dataPrefix + "root") ||
							// remove from src path jsPath and "/" sign
							src.substring(0, src.lastIndexOf(frameworkName) - jsPath.length - 1) ||
							self.rootDir;

						self.themePath = self.rootDir + themePath;
						self.jsPath = self.rootDir + jsPath;
						self.version = scriptElement.getAttribute(dataPrefix + "version") || self.version;
						self.theme = theme;
						self.themeLoaded = themeLoaded;
						self.frameworkName = frameworkName;
						self.minified = src.search(MINIFIED_REGEXP) > -1;
						self.profile = profileName;
					}
				}

				cssElements.forEach(findThemeInLinks);
				scriptElements.forEach(findFrameworkDataInScripts);
			};

			ns.frameworkData = frameworkData;
			// self init
			ns.frameworkData.getParams();
			}(window.document, ns));

/*global window, ns, define */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping engine
 * Object maps engine object from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.engine
 */
(function (window, document) {
	"use strict";
				/**
			 * Alias to Array.slice function
			 * @method slice
			 * @member ns.jqm.engine
			 * @private
			 * @static
			 */
			var slice = [].slice,
				/**
				 * @property {Object} nsNormalizeDict directory of data-* attributes normalized name
				 * @member ns.jqm.engine
				 * @private
				 * @static
				 */
				nsNormalizeDict = {},
				$ = ns.jqm.jQuery,
				util = ns.util,
				zoom = util.zoom,
				events = ns.event,
				load = util.load,
				utilsObject = util.object,
				/*
				 * Convert jQuery object to HTMLElement or Array of HTMLElements
				 * @param {jQuery|HTMLElement|Array} item
			   * @return {HTMLElement|Array}
				 */
				mapItem = function (item) {
					if (typeof item === "object" && item.selector && item.get) {
						return item.length === 1 ? item.get(0) : item.toArray();
					}

					return item;
				},
				engine = ns.engine,
				eventType = engine.eventType,

			/**
			 * append ns functions to jQuery Mobile namespace
			 * @method init
			 * @member ns.jqm.engine
			 * @static
			 */
				init = function () {
					var keys = Object.keys(engine),
						i,
						len,
						name,
						/*
						 * original jQuery find function
						 * type function
						 */
						oldFind,
						/*
						 * regular expression to find data-{namespace}-attribute
						 */
						jqmDataRE = /:jqmData\(([^)]*)\)/g, // @TODO fix, insecure (jslint)
						/*
						 * string to detect exists jqmData selector
						 */
						jqmDataStr = ":jqmData",


						tizen;

					if ($) {

						for (i = 0, len = keys.length; i < len; ++i) {
							name = keys[i];
							$[name] = widgetFunction.bind(null, arguments, mapItem, engine, name);
						}

						utilsObject.merge($.mobile, {
							/*
							 * jQuery Mobile namespace
							 */
							ns: "",
							/**
							 *
							 * @param {string} prop
							 * @return {?string}
							 */
							nsNormalize: function (prop) {
								if (!prop) {
									return null;
								}
								nsNormalizeDict[prop] = nsNormalizeDict[prop] || $.camelCase($.mobile.ns + prop);
								return nsNormalizeDict[prop];
							},
							activeBtnClass: ns.widget.core.Button.classes.uiBtnActive,
							activePageClass: ns.widget.core.Page.classes.uiPageActive,
							focusClass: ns.widget.core.Button.classes.uiFocus,
							version: "1.2.0",
							getAttrFixed: function (element, key) {
								var value = element.getAttribute(key);

								return value === "true" ? true :
									value === "false" ? false :
										value === null ? undefined :
											value;
							},
							path: ns.util.path,
							back: window.history.back.bind(window.history),
							silentScroll: function (yPos) {
								if (yPos === undefined) {
									yPos = $.mobile.defaultHomeScroll;
								}

								// prevent scrollstart and scrollstop events
								// @TODO enable event control
								//ns.event.special.scrollstart.enabled = false;

								setTimeout(function () {
									window.scrollTo(0, yPos);
									events.trigger(document, "silentscroll", {x: 0, y: yPos});
								}, 20);

								setTimeout(function () {
									// @TODO enable event control
									//$.event.special.scrollstart.enabled = true;
								}, 150);
							},
							nsNormalizeDict: nsNormalizeDict,
							closestPageData: function (target) {
								var page = ns.util.selectors.getClosestBySelector($(target)[0],
									"[data-" + ($.mobile.ns || "") + "role='page'], [data-" + ($.mobile.ns || "") + "role='dialog']");

								return ns.engine.instanceWidget(page, "Page");
							},
							enhanceable: function ($set) {
								return this.haveParents($set, "enhance");
							},
							hijackable: function ($set) {
								return this.haveParents($set, "ajax");
							},
							haveParents: function ($set, attr) {
								var count = 0,
									$newSet = null,
									e,
									$element,
									excluded,
									i,
									c;

								if (!$.mobile.ignoreContentEnabled) {
									return $set;
								}

								count = $set.length;
								$newSet = $();

								for (i = 0; i < count; i++) {
									$element = $set.eq(i);
									excluded = false;
									e = $set[i];

									while (e) {
										c = e.getAttribute ? e.getAttribute("data-" + $.mobile.ns + attr) : "";

										if (c === "false") {
											excluded = true;
											break;
										}

										e = e.parentNode;
									}

									if (!excluded) {
										$newSet = $newSet.add($element);
									}
								}

								return $newSet;
							},
							getScreenHeight: function () {
								// Native innerHeight returns more accurate value for this across platforms,
								// jQuery version is here as a normalized fallback for platforms like Symbian
								return window.innerHeight;
							},
							widget: function () {
								// @todo fill data
								return null;
							},
							media: ns.support.media,
							browser: {},
							gradeA: function () {
								// @todo fill data
								return null;
							},
							zoom: zoom,
							popupwindow: {}
						});
						$.mobile.buttonMarkup = $.mobile.buttonMarkup || ns.widget.mobile.Button;
						$.mobile.$window = $(window);
						$.mobile.$document = $(document);
						$.mobile.keyCode = {
							ALT: 18,
							BACKSPACE: 8,
							CAPS_LOCK: 20,
							COMMA: 188,
							COMMAND: 91,
							COMMAND_LEFT: 91, // COMMAND
							COMMAND_RIGHT: 93,
							CONTROL: 17,
							DELETE: 46,
							DOWN: 40,
							END: 35,
							ENTER: 13,
							ESCAPE: 27,
							HOME: 36,
							INSERT: 45,
							LEFT: 37,
							MENU: 93, // COMMAND_RIGHT
							NUMPAD_ADD: 107,
							NUMPAD_DECIMAL: 110,
							NUMPAD_DIVIDE: 111,
							NUMPAD_ENTER: 108,
							NUMPAD_MULTIPLY: 106,
							NUMPAD_SUBTRACT: 109,
							PAGE_DOWN: 34,
							PAGE_UP: 33,
							PERIOD: 190,
							RIGHT: 39,
							SHIFT: 16,
							SPACE: 32,
							TAB: 9,
							UP: 38,
							WINDOWS: 91 // COMMAND
						};
						$.tizen = $.tizen || {};
						tizen = $.tizen;
						tizen.globalize = ns.util.globalize;
						$.mobile.tizen = utilsObject.merge($.mobile.tizen, {
							_widgetPrototypes: {},
							disableSelection: function () {
								ns.warn("Function $.mobile.tizen.disableSelection is deprecated");
							},
							enableSelection: function () {
								ns.warn("Function $.mobile.tizen.enableSelection is deprecated");
							},
							enableContextMenu: function () {
								ns.warn("Function $.mobile.tizen.enableContextMenu is deprecated");
							},
							disableContextMenu: function () {
								ns.warn("Function $.mobile.tizen.disableContextMenu is deprecated");
							}
						});
						$.mobile.tizen.loadPrototype = null;

						/*
						 * jqmData function from jQuery Mobile
						 */
						$.fn.jqmData = function (prop, value) {
							var result;

							if (prop !== undefined) {
								if (prop) {
									prop = $.mobile.nsNormalize(prop);
								}
								if (arguments.length < 2 || value === undefined) {
									result = this.data(prop);
								} else {
									result = this.data(prop, value);
								}
							}
							return result;
						};

						$.fn.jqmRemoveData = function (prop) {
							if (prop !== undefined) {
								if (prop) {
									prop = $.mobile.nsNormalize(prop);
								}
								this.removeData(prop);
							}
							return this;
						};

						$.jqmData = function (context, prop, value) {
							var result = $(context).jqmData(prop, value);

							return value || result;
						};

						$.jqmRemoveData = function (context, prop) {
							$(context).jqmRemoveData(prop);
						};

						$.fn.removeWithDependents = function () {
							$.removeWithDependents(this);
						};

						$.removeWithDependents = function (elem) {
							var $elem = $(elem);

							($elem.jqmData("dependents") || $()).remove();
							$elem.remove();
						};

						$.fn.addDependents = function (newDependents) {
							$.addDependents($(this), newDependents);
						};

						$.addDependents = function (elem, newDependents) {
							var dependents = $(elem).jqmData("dependents") || $();

							$(elem).jqmData("dependents", $.merge(dependents, newDependents));
						};

						$.fn.getEncodedText = function () {
							return $("<div/>").text($(this).text()).html();
						};

						// fluent helper function for the mobile namespaced equivalent
						$.fn.jqmEnhanceable = function () {
							return $.mobile.enhanceable(this);
						};

						$.fn.jqmHijackable = function () {
							return $.mobile.hijackable(this);
						};

						/*
						 * Add support of jqmData() in jQuery find
						 */
						oldFind = $.find;

						$.find = function (selector, context, ret, extra) {
							if (selector.indexOf(jqmDataStr) > -1) {
								selector = selector.replace(jqmDataRE, "[data-" + ($.mobile.ns || "") + "$1]");
							}
							return oldFind.call(this, selector, context, ret, extra);
						};

						$.extend($.find, oldFind);

						$.find.matches = function (expr, set) {
							return $.find(expr, null, null, set);
						};

						$.find.matchesSelector = function (node, expr) {
							return $.find(expr, null, null, [node]).length > 0;
						};

						/* support for global object $.mobile
						 * @TODO this is temporary fix, we have to think about this function
						 */
						$(document).bind("create", ns.engine._createEventHandler);
						// support creating widgets by triggering pagecreate
						$(document).bind("pagecreate", function (event) {
							var originalEvent = event.originalEvent || event,
								isPage = originalEvent.detail instanceof ns.widget.core.Page,
								pageWidget;

							if (!isPage) { // trigger create when the pagecreate trigger is from outside
								pageWidget = engine.instanceWidget(originalEvent.target, "Page");
								pageWidget.refresh();
								ns.engine._createEventHandler(originalEvent);
							}
						});
						$(document).bind("activePopup", function (event) {
							$.mobile.popup.active = $.mobile.popupwindow.active = event.originalEvent.detail;
						});

						// @TODO fill this object proper data
						$.tizen.frameworkData = ns.frameworkData;

						$.tizen.__tizen__ = tizen;
						tizen.libFileName = "tizen-web-ui-fw(.custom|.full)?(.min)?.js";
						tizen.log = {
							debug: function (msg) {
								if ($.tizen.frameworkData.debug) {
									ns.log(msg);
								}
							},
							warn: ns.warn.bind(ns),
							error: ns.error.bind(ns),
							alert: window.alert.bind(window)
						};
						tizen.util = {
							loadScriptSync: load.scriptSync,
							isMobileBrowser: function () {
								ns.warn("Function $.tizen.__tizen__.util.isMobileBrowser is deprecated");
							}
						};
						tizen.css = {
							cacheBust: load.cacheBust,
							addElementToHead: load.addElementToHead.bind(load),
							makeLink: load.makeLink.bind(load),
							load: load.themeCSS
						};
						tizen.loadTheme = function () {
							ns.warn("Function $.tizen.__tizen__.loadTheme is deprecated");
						};
						//tizen.loadGlobalizeCulture = ns.util.globalize.loadGlobalizeCulture.bind(ns.util.globalize);
						tizen.setLocale = util.globalize.setLocale;
						tizen.setViewport = function () {
							ns.warn("Function $.tizen.__tizen__.setViewport is deprecated");
						};
						tizen.scaleBaseFontSize = function () {
							ns.warn("Function $.tizen.__tizen__.scaleBaseFontSize is deprecated");
						};
						tizen.setScaling = function () {
							ns.warn("Function $.tizen.__tizen__.setScaling is deprecated");
						};
						tizen.getParams = ns.frameworkData.getParams.bind(ns.frameworkData);

						ns.setConfig("enableHWKeyHandler", $.mobile.tizen.enableHWKeyHandler);
					}
				},
				/**
				 * Removes events listeners on destroy of framework.
				 */
				destroy = function () {
					document.removeEventListener(eventType.INIT, init, false);
					document.removeEventListener(eventType.DESTROY, destroy, false);
				};

			/**
			 * Function which is used as jQuery mapping engine method
			 * @param {Arguments} parentArguments
			 * @param {Function} mapItem
			 * @param {Object} engine
			 * @param {string} name
			 */
			function widgetFunction(parentArguments, mapItem, engine, name) {
				var args = slice.call(parentArguments).map(mapItem);

				engine[name].apply(engine, args);
			}

			// Listen when framework is ready
			document.addEventListener(eventType.INIT, init, false);
			document.addEventListener(eventType.DESTROY, destroy, false);

			}(window, window.document));

/*global CustomEvent, define, window, ns */
/*jslint plusplus: true, nomen: true, bitwise: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Virtual Mouse Events
 * Reimplementation of jQuery Mobile virtual mouse events.
 *
 * ##Purpose
 * It will let for users to register callbacks to the standard events like bellow,
 * without knowing if device support touch or mouse events
 * @class ns.event.vmouse
 */
/**
 * Triggered after mouse-down or touch-started.
 * @event vmousedown
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-click or touch-end when touch-move didn't occur
 * @event vclick
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-up or touch-end
 * @event vmouseup
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-move or touch-move
 * @event vmousemove
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-over or touch-start if went over coordinates
 * @event vmouseover
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-out or touch-end
 * @event vmouseout
 * @member ns.event.vmouse
 */
/**
 * Triggered when mouse-cancel or touch-cancel and when scroll occur during touchmove
 * @event vmousecancel
 * @member ns.event.vmouse
 */
(function (window, document, ns) {
	"use strict";
				/**
			 * Object with default options
			 * @property {Object} vmouse
			 * @member ns.event.vmouse
			 * @static
			 * @private
			 **/
			var vmouse,
				/**
				 * @property {Object} eventProps Contains the properties which are copied from the original
				 * event to custom v-events
				 * @member ns.event.vmouse
				 * @static
				 * @private
				 **/
				eventProps,
				/**
				 * Indicates if the browser support touch events
				 * @property {boolean} touchSupport
				 * @member ns.event.vmouse
				 * @static
				 **/
				touchSupport = window.hasOwnProperty("ontouchstart"),
				/**
				 * @property {boolean} didScroll The flag tell us if the scroll event was triggered
				 * @member ns.event.vmouse
				 * @static
				 * @private
				 **/
				didScroll,
				/** @property {HTMLElement} lastOver holds reference to last element that touch was over
				 * @member ns.event.vmouse
				 * @private
				 */
				lastOver = null,
				/**
				 * @property {number} [startX=0] Initial data for touchstart event
				 * @member ns.event.vmouse
				 * @static
				 * @private
				 **/
				startX = 0,
				/**
				 * @property {number} [startY=0] Initial data for touchstart event
				 * @member ns.event.vmouse
				 * @private
				 * @static
				 **/
				startY = 0,
				touchEventProps = ["clientX", "clientY", "pageX", "pageY", "screenX", "screenY"],
				KEY_CODES = {
					enter: 13
				};

			/**
			 * Extends objects with other objects
			 * @method copyProps
			 * @param {Object} from Sets the original event
			 * @param {Object} to Sets the new event
			 * @param {Object} properties Sets the special properties for position
			 * @param {Object} propertiesNames Describe parameters which will be copied from Original to
			 * event
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function copyProps(from, to, properties, propertiesNames) {
				var i,
					length,
					descriptor,
					property;

				for (i = 0, length = propertiesNames.length; i < length; ++i) {
					property = propertiesNames[i];
					if (isNaN(properties[property]) === false || isNaN(from[property]) === false) {
						descriptor = Object.getOwnPropertyDescriptor(to, property);
						if (property !== "detail" && (!descriptor || descriptor.writable)) {
							to[property] = properties[property] || from[property];
						}
					}
				}
			}

			/**
			 * Create custom event
			 * @method createEvent
			 * @param {string} newType gives a name for the new Type of event
			 * @param {Event} original Event which trigger the new event
			 * @param {Object} properties Sets the special properties for position
			 * @return {Event}
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function createEvent(newType, original, properties) {
				var evt = new CustomEvent(newType, {
						"bubbles": original.bubbles,
						"cancelable": original.cancelable,
						"detail": original.detail
					}),
					originalType = original.type,
					changeTouches,
					touch,
					j = 0,
					len,
					prop;

				copyProps(original, evt, properties, eventProps);
				evt._originalEvent = original;

				if (originalType.indexOf("touch") !== -1) {
					originalType = original.touches;
					changeTouches = original.changedTouches;

					if (originalType && originalType.length) {
						touch = originalType[0];
					} else {
						touch = (changeTouches && changeTouches.length) ? changeTouches[0] : null;
					}

					if (touch) {
						for (len = touchEventProps.length; j < len; j++) {
							prop = touchEventProps[j];
							evt[prop] = touch[prop];
						}
					}
				}

				return evt;
			}

			/**
			 * Dispatch Events
			 * @method fireEvent
			 * @param {string} eventName event name
			 * @param {Event} evt original event
			 * @param {Object} [properties] Sets the special properties for position
			 * @return {boolean}
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function fireEvent(eventName, evt, properties) {
				return evt.target.dispatchEvent(createEvent(eventName, evt, properties || {}));
			}

			eventProps = [
				"currentTarget",
				"detail",
				"button",
				"buttons",
				"clientX",
				"clientY",
				"offsetX",
				"offsetY",
				"pageX",
				"pageY",
				"screenX",
				"screenY",
				"toElement",
				"which"
			];

			vmouse = {
				/**
				 * Sets the distance of pixels after which the scroll event will be successful
				 * @property {number} [eventDistanceThreshold=10]
				 * @member ns.event.vmouse
				 * @static
				 */
				eventDistanceThreshold: 10,

				touchSupport: touchSupport
			};

			/**
			 * Handle click down
			 * @method handleDown
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleDown(evt) {
				fireEvent("vmousedown", evt);
			}

			/**
			 * Prepare position of event for keyboard events.
			 * @method preparePositionForClick
			 * @param {Event} event
			 * @return {?Object} options
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function preparePositionForClick(event) {
				var x = event.clientX,
					y = event.clientY;
				// event comes from keyboard

				if (!x && !y) {
					return preparePositionForEvent(event);
				}
			}

			/**
			 * Handle click
			 * @method handleClick
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleClick(evt) {
				fireEvent("vclick", evt, preparePositionForClick(evt));
			}

			/**
			 * Handle click up
			 * @method handleUp
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleUp(evt) {
				fireEvent("vmouseup", evt);
			}

			/**
			 * Handle click move
			 * @method handleMove
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleMove(evt) {
				fireEvent("vmousemove", evt);
			}

			/**
			 * Handle click over
			 * @method handleOver
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleOver(evt) {
				fireEvent("vmouseover", evt);
			}

			/**
			 * Handle click out
			 * @method handleOut
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleOut(evt) {
				fireEvent("vmouseout", evt);
			}

			/**
			 * Handle touch start
			 * @method handleTouchStart
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleTouchStart(evt) {
				var touches = evt.touches,
					firstTouch;

				//if touches are registered and we have only one touch
				if (touches && touches.length === 1) {
					didScroll = false;
					firstTouch = touches[0];
					startX = firstTouch.pageX;
					startY = firstTouch.pageY;

					// Check if we have touched something on our page
					// @TODO refactor for multi touch
					/*
					 over = document.elementFromPoint(startX, startY);
					 if (over) {
					 lastOver = over;
					 fireEvent("vmouseover", evt);
					 }
					 */
					fireEvent("vmousedown", evt);
				}

			}

			/**
			 * Handle touch end
			 * @method handleTouchEnd
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleTouchEnd(evt) {
				var touches = evt.touches;

				if (touches && touches.length === 0) {
					fireEvent("vmouseup", evt);
					fireEvent("vmouseout", evt);
					// Reset flag for last over element
					lastOver = null;
				}
			}

			/**
			 * Handle touch move
			 * @method handleTouchMove
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleTouchMove(evt) {
				var over,
					firstTouch = evt.touches && evt.touches[0],
					didCancel = didScroll,
					//sets the threshold, based on which we consider if it was the touch-move event
					moveThreshold = vmouse.eventDistanceThreshold;

				/**
				 * Ignore the touch which has identifier other than 0.
				 * Only first touch has control others are ignored.
				 * Patch for webkit behavior where touchmove event
				 * is triggered between touchend events
				 * if there is multi touch.
				 */

				if ((firstTouch === undefined) || firstTouch.identifier > 0) {
					//evt.preventDefault(); // cant preventDefault passive events!!!
					evt.stopPropagation();
					return;
				}

				didScroll = didScroll ||
					//check in both axes X,Y if the touch-move event occur
					(Math.abs(firstTouch.pageX - startX) > moveThreshold ||
					Math.abs(firstTouch.pageY - startY) > moveThreshold);

				// detect over event
				// for compatibility with mouseover because "touchenter" fires only once
				// @TODO Handle many touches
				over = document.elementFromPoint(firstTouch.pageX, firstTouch.pageY);
				if (over && lastOver !== over) {
					lastOver = over;
					fireEvent("vmouseover", evt);
				}

				//if didScroll occur and wasn't canceled then trigger touchend otherwise just touchmove
				if (didScroll && !didCancel) {
					fireEvent("vmousecancel", evt);
					lastOver = null;
				}
				fireEvent("vmousemove", evt);
			}

			/**
			 * Handle Scroll
			 * @method handleScroll
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleScroll(evt) {
				if (!didScroll) {
					fireEvent("vmousecancel", evt);
				}
				didScroll = true;
			}

			/**
			 * Handle touch cancel
			 * @method handleTouchCancel
			 * @param {Event} evt
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleTouchCancel(evt) {

				fireEvent("vmousecancel", evt);
				lastOver = null;
			}

			/**
			 * Prepare position of event for keyboard events.
			 * @method preparePositionForEvent
			 * @param {Event} event
			 * @return {Object} properties
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function preparePositionForEvent(event) {
				var targetRect = event.target && event.target.getBoundingClientRect(),
					properties = {};

				if (targetRect) {
					properties = {
						"clientX": targetRect.left + targetRect.width / 2,
						"clientY": targetRect.top + targetRect.height / 2,
						"which": 1
					};
				}
				return properties;
			}

			/**
			 * Handle key up
			 * @method handleKeyUp
			 * @param {Event} event
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleKeyUp(event) {
				var properties;

				if (event.keyCode === KEY_CODES.enter) {
					properties = preparePositionForEvent(event);
					fireEvent("vmouseup", event, properties);
					fireEvent("vclick", event, properties);
				}
			}

			/**
			 * Handle key down
			 * @method handleKeyDown
			 * @param {Event} event
			 * @private
			 * @static
			 * @member ns.event.vmouse
			 */
			function handleKeyDown(event) {
				if (event.keyCode === KEY_CODES.enter) {
					fireEvent("vmousedown", event, preparePositionForEvent(event));
				}
			}

			/**
			 * Binds events common to mouse and touch to support virtual mouse.
			 * @method bindCommonEvents
			 * @static
			 * @member ns.event.vmouse
			 */
			vmouse.bindCommonEvents = function () {
				document.addEventListener("keyup", handleKeyUp, true);
				document.addEventListener("keydown", handleKeyDown, true);
				document.addEventListener("scroll", handleScroll, true);
				document.addEventListener("click", handleClick, true);
			};

			// @TODO delete touchSupport flag and attach touch and mouse listeners,
			// @TODO check if v-events are not duplicated if so then called only once

			/**
			 * Binds touch events to support virtual mouse.
			 * @method bindTouch
			 * @static
			 * @member ns.event.vmouse
			 */
			vmouse.bindTouch = function () {
				document.addEventListener("touchstart", handleTouchStart, true);
				document.addEventListener("touchend", handleTouchEnd, true);
				document.addEventListener("touchmove", handleTouchMove, true);
				document.addEventListener("touchcancel", handleTouchCancel, true);
			};

			/**
			 * Binds mouse events to support virtual mouse.
			 * @method bindMouse
			 * @static
			 * @member ns.event.vmouse
			 */
			vmouse.bindMouse = function () {
				document.addEventListener("mousedown", handleDown, true);
				document.addEventListener("mouseup", handleUp, true);
				document.addEventListener("mousemove", handleMove, true);
				document.addEventListener("mouseover", handleOver, true);
				document.addEventListener("mouseout", handleOut, true);
			};

			/**
			 * Unbinds touch events to support virtual mouse.
			 * @method unbindTouch
			 * @static
			 * @member ns.event.vmouse
			 */
			vmouse.unbindTouch = function () {
				document.removeEventListener("touchstart", handleTouchStart, true);
				document.removeEventListener("touchend", handleTouchEnd, true);
				document.removeEventListener("touchmove", handleTouchMove, true);

				document.removeEventListener("touchcancel", handleTouchCancel, true);

				document.removeEventListener("click", handleClick, true);
			};

			/**
			 * Unbinds mouse events to support virtual mouse.
			 * @method unbindMouse
			 * @static
			 * @member ns.event.vmouse
			 */
			vmouse.unbindMouse = function () {
				document.removeEventListener("mousedown", handleDown, true);

				document.removeEventListener("mouseup", handleUp, true);
				document.removeEventListener("mousemove", handleMove, true);
				document.removeEventListener("mouseover", handleOver, true);
				document.removeEventListener("mouseout", handleOut, true);

				document.removeEventListener("keyup", handleKeyUp, true);
				document.removeEventListener("keydown", handleKeyDown, true);
				document.removeEventListener("scroll", handleScroll, true);
				document.removeEventListener("click", handleClick, true);
			};

			ns.event.vmouse = vmouse;

			if (touchSupport) {
				vmouse.bindTouch();
			} else {
				vmouse.bindMouse();
			}
			vmouse.bindCommonEvents();

			}(window, window.document, ns));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Event orientationchange
 * Namespace to support orientationchange event
 * @class ns.event.orientationchange
 */
/**
 * Event orientationchange
 * @event orientationchange
 * @member ns.event.orientationchange
 */
(function (window, document, ns) {
	"use strict";
				var eventUtils = ns.event,
				eventType = ns.engine.eventType,
				orientationchange = {
					/**
					 * Window alias
					 * @property {Window} window
					 * @member ns.event.orientationchange
					 * @static
					 * @protected
					 */
					_window: window,
					/**
					 * Informs about support orientation change event.
					 * @property {boolean} supported
					 * @member ns.event.orientationchange
					 */
					supported: (window.orientation !== undefined) && (window.onorientationchange !== undefined),
					/**
					 * List of properties copied to event details object
					 * @property {Array} properties
					 * @member ns.event.orientationchange
					 * @static
					 */
					properties: ["orientation"],

					/**
					 * Chosen orientation
					 * @property {Window} [orientation=portrait]
					 * @member ns.event.orientationchange
					 * @protected
					 * @static
					 */
					_orientation: "portrait"
				},
				detectOrientationByDimensions = function (omitCustomEvent) {
					var win = orientationchange._window,
						width = win.innerWidth,
						height = win.innerHeight;

					if (win.screen) {
						width = win.screen.availWidth;
						height = win.screen.availHeight;
					}

					if (width > height) {
						orientationchange._orientation = "landscape";
					} else {
						orientationchange._orientation = "portrait";
					}

					if (!omitCustomEvent) {
						eventUtils.trigger(window, "orientationchange", {"orientation": orientationchange._orientation});
					}
				},
				checkReportedOrientation = function () {
					if (orientationchange._window.orientation) {
						switch (orientationchange._window.orientation) {
							case 90:
							case -90:
								orientationchange._orientation = "portrait";
								break;
							default:
								orientationchange._orientation = "landscape";
								break;
						}
					} else {
						detectOrientationByDimensions(true);
					}
				},
				matchMediaHandler = function (mediaQueryList, omitEvent) {
					if (mediaQueryList.matches) {
						orientationchange._orientation = "portrait";
					} else {
						orientationchange._orientation = "landscape";
					}

					if (!omitEvent) { // this was added explicit for testing
						eventUtils.trigger(window, "orientationchange", {"orientation": orientationchange._orientation});
					}
				},
				portraitMatchMediaQueryList = null;

			/**
			* Returns current orientation.
			* @method getOrientation
			* @return {"landscape"|"portrait"}
			* @member ns.event.orientationchange
			* @static
			*/
			orientationchange.getOrientation = function () {
				return orientationchange._orientation;
			};

			/**
			* Triggers event orientationchange on element
			* @method trigger
			* @param {HTMLElement} element
			* @member ns.event.orientationchange
			* @static
			*/
			orientationchange.trigger = function (element) {
				eventUtils.trigger(element, "orientationchange", {"orientation": orientationchange._orientation});
			};

			/**
			* Unbinds events
			* @member ns.event.orientationchange
			* @static
			*/
			orientationchange.unbind = function () {
				window.removeEventListener("orientationchange", checkReportedOrientation, false);
				document.removeEventListener("throttledresize", detectOrientationByDimensions, true);
				document.removeEventListener(eventType.DESTROY, orientationchange.unbind, false);
			};

			/**
			* Performs orientation detection
			* @member ns.event.orientationchange
			* @static
			*/
			orientationchange.detect = function () {
				if (orientationchange.supported) {
					window.addEventListener("orientationchange", checkReportedOrientation, false);
					checkReportedOrientation();
					// try media queries
				} else {
					if (orientationchange._window.matchMedia) {
						portraitMatchMediaQueryList = orientationchange._window.matchMedia("(orientation: portrait)");
						if (portraitMatchMediaQueryList.matches) {
							orientationchange._orientation = "portrait";
						} else {
							orientationchange._orientation = "landscape";
						}
						portraitMatchMediaQueryList.addListener(matchMediaHandler);
					} else {
						document.addEventListener("throttledresize", detectOrientationByDimensions, true);
						detectOrientationByDimensions();
					}
				}
			};

			document.addEventListener(eventType.DESTROY, orientationchange.unbind, false);

			orientationchange.detect();

			ns.event.orientationchange = orientationchange;

			}(window, window.document, ns));

/*global window, ns, define */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping events
 * Object maps events from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.event
 */
(function (window, document) {
	"use strict";
	
			var eventUtils = ns.event,
				$ = ns.jqm.jQuery,
				originalTrigger,
				originalDispatch,
				eventType = {
					CLICK: "click",
					SUBMIT: "submit",
					KEYUP: "keyup",
					TOUCHSTART: "touchstart",
					TOUCHEND: "touchend",
					VCLICK: "vclick",
					MOUSEDOWN: "mousedown",
					MOUSEUP: "mouseup",
					BEFOREROUTERINIT: "beforerouterinit",
					DESTROY: "taudestroy"
				},
				registerEventNames = ["touchstart", "touchmove", "touchend", "tap", "taphold", "swipeleft", "swiperight", "scrollstart", "scrollstop"],

				event = {
					/**
					 * Create method on jQuery object with name represent event.
					 * @method proxyEventTriggerMethod
					 * @param {string} name Name of event and new method
					 * @param {Function} trigger Function called after invoke method
					 * @member ns.jqm.event
					 * @static
					 */
					proxyEventTriggerMethod: function (name, trigger) {
						$.fn[name] = function () {
							var $elements = this,
								elementsLength = $elements.length,
								i;

							for (i = 0; i < elementsLength; i++) {
								trigger($elements.get(i));
							}
						};
					},

					/**
					 * Adds proxy to jquery.trigger method
					 * @method proxyTrigger
					 * @param {string} type event type
					 * @param {Mixed} data event data
					 * @return {jQuery}
					 * @member ns.jqm.event
					 */
					proxyTrigger: function (type, data) {
						var $elements = this,
							elementsLength = $elements.length,
							i;

						if (!eventType[type.toUpperCase()]) {
							originalTrigger.call($elements, type, data);
						}

						for (i = 0; i < elementsLength; i++) {
							eventUtils.trigger($elements.get(i), type);
						}

						return this;
					},

					/**
					 * Method read additional data from event.detail and move these data as additional argument to jQuery.event.dispatch
					 * @method proxyDispatch
					 * @param {jQuery.Event} event event type
					 * @return {jQuery}
					 * @member ns.jqm.event
					 */
					proxyDispatch: function (event) {
						var data = (event.originalEvent && event.originalEvent.detail) || event.detail,
							args;

						args = [].slice.call(arguments);
						if (data) {
							args.push(data);
						}
						return originalDispatch.apply(this, args);
					},

					/**
					 * Copy properties from originalEvent.detail.* to event Object.
					 * @method copyEventProperties
					 * @param {HTMLElement} root root element to catch all events window/document
					 * @param {string} name Name of event
					 * @param {Array.<string>} properties Array of properties to copy from originalEvent to jQuery Event
					 * @member ns.jqm.event
					 * @static
					 */
					copyEventProperties: function (root, name, properties) {
						$(root).on(name, function (event) {
							var i,
								property;

							for (i = 0; i < properties.length; i++) {
								property = properties[i];
								if (!event[property]) {
									if (root instanceof window.screen.constructor) {
										// In case of orientation change event the properties are set to window.screen object
										// that's why we check if root is Screen in the first place
										event[property] = event.originalEvent.detail &&
											event.originalEvent.detail[property] ||
											event.target[property];
									} else {
										event[property] = event.originalEvent.detail && event.originalEvent.detail[property];
									}
								}
							}
						});
					},

					/**
					 * Proxy events from ns namespace to jQM namespace
					 * @method init
					 * @member ns.jqm.event
					 * @static
					 */
					init: function () {
						var removeEvents = function (event) {
								event.stopPropagation();
								event.preventDefault();
								return false;
							},
							blockedEvents = [eventType.TOUCHSTART, eventType.TOUCHEND, eventType.VCLICK, eventType.MOUSEDOWN, eventType.MOUSEUP, eventType.CLICK],
							blockedEventsLength = blockedEvents.length,
							html = document.body.parentNode;

						if ($) {
							// setup new event shortcuts
							registerEventNames.forEach(function (name) {
								$.fn[name] = function (fn) {
									return fn ? this.bind(name, fn) : this.trigger(name);
								};
								// jQuery < 1.8
								if ($.attrFn) {
									$.attrFn[name] = true;
								}
							});

							event.copyEventProperties(window.screen, "orientationchange", eventUtils.orientationchange.properties);
							event.proxyEventTriggerMethod("orientationchange", eventUtils.orientationchange.trigger);

							// Proxies jQuery's trigger method to fire swipe event
							if (originalTrigger === undefined) {
								originalTrigger = $.fn.trigger;
								$.fn.trigger = event.proxyTrigger;
							}

							if (!originalDispatch) {
								originalDispatch = $.event.dispatch;
								$.event.dispatch = event.proxyDispatch;
							}

							$.mobile = $.mobile || {};
							$.mobile.tizen = $.mobile.tizen || {};
							$.mobile.tizen.documentRelativeCoordsFromEvent = null;
							$.mobile.tizen.targetRelativeCoordsFromEvent = null;
							$.mobile.addEventBlocker = function () {
								var i;

								html.classList.add("ui-blocker");
								for (i = 0; i < blockedEventsLength; i++) {
									html.addEventListener(blockedEvents[i], removeEvents, true);
								}
							};
							$.mobile.removeEventBlocker = function () {
								var i;

								html.classList.remove("ui-blocker");
								for (i = 0; i < blockedEventsLength; i++) {
									html.removeEventListener(blockedEvents[i], removeEvents, true);
								}
							};
							$.mobile.tizen.documentRelativeCoordsFromEvent = eventUtils.documentRelativeCoordsFromEvent.bind(eventUtils);
							$.mobile.tizen.targetRelativeCoordsFromEvent = eventUtils.targetRelativeCoordsFromEvent.bind(eventUtils);
						}
					},
					/**
					 * Removes events listeners on destroy of framework.
					 */
					destroy: function () {
						document.removeEventListener(eventType.BEFOREROUTERINIT, event.init, false);
						document.removeEventListener(eventType.DESTROY, event.destroy, false);
					}
				};

			// Listen when framework is ready
			document.addEventListener(eventType.BEFOREROUTERINIT, event.init, false);
			document.addEventListener(eventType.DESTROY, event.destroy, false);

			ns.jqm.event = event;
			}(window, window.document));

/*global window, ns, define, RegExp */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Path Utility
 * Object helps work with paths.
 * @class ns.util.path
 * @static
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				/**
				 * Local alias for ns.util.selectors
				 * @property {Object} utilsSelectors Alias for {@link ns.util.selectors}
				 * @member ns.util.path
				 * @static
				 * @private
				 */
				utilsSelectors = ns.util.selectors,
				/**
				 * Local alias for ns.util.DOM
				 * @property {Object} utilsDOM Alias for {@link ns.util.DOM}
				 * @member ns.util.path
				 * @static
				 * @private
				 */
				utilsDOM = ns.util.DOM,
				/**
				 * Cache for document base element
				 * @member ns.util.path
				 * @property {HTMLBaseElement} base
				 * @static
				 * @private
				 */
				base,
				/**
				 * location object
				 * @property {Object} location
				 * @static
				 * @private
				 * @member ns.util.path
				 */
				location = {},
				path = {
					/**
					 * href part for mark state
					 * @property {string} [uiStateKey="&ui-state"]
					 * @static
					 * @member ns.util.path
					 */
					uiStateKey: "&ui-state",

					// This scary looking regular expression parses an absolute URL or its relative
					// variants (protocol, site, document, query, and hash), into the various
					// components (protocol, host, path, query, fragment, etc that make up the
					// URL as well as some other commonly used sub-parts. When used with RegExp.exec()
					// or String.match, it parses the URL into a results array that looks like this:
					//
					//	[0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#
					//       msg-content?param1=true&param2=123
					//	[1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
					//	[2]: http://jblas:password@mycompany.com:8080/mail/inbox
					//	[3]: http://jblas:password@mycompany.com:8080
					//	[4]: http:
					//	[5]: //
					//	[6]: jblas:password@mycompany.com:8080
					//	[7]: jblas:password
					//	[8]: jblas
					//	[9]: password
					//	[10]: mycompany.com:8080
					//	[11]: mycompany.com
					//	[12]: 8080
					//	[13]: /mail/inbox
					//	[14]: /mail/
					//	[15]: inbox
					//	[16]: ?msg=1234&type=unread
					//	[17]: #msg-content?param1=true&param2=123
					//	[18]: #msg-content
					//	[19]: ?param1=true&param2=123
					//
					/**
					 * @property {RegExp} urlParseRE Regular expression for parse URL
					 * @member ns.util.path
					 * @static
					 */
					urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)((#[^\?]*)(\?.*)?)?/,

					/**
					 * Abstraction to address xss (Issue #4787) by removing the authority in
					 * browsers that auto decode it. All references to location.href should be
					 * replaced with a call to this method so that it can be dealt with properly here
					 * @method getLocation
					 * @param {string|Object} [url]
					 * @return {string}
					 * @member ns.util.path
					 */
					getLocation: function (url) {
						var uri = this.parseUrl(url || window.location.href),
							hash = uri.hash,
							search = uri.hashSearch;
						// mimic the browser with an empty string when the hash and hashSearch are empty

						hash = hash === "#" && !search ? "" : hash;
						location = uri;
						// Make sure to parse the url or the location object for the hash because using
						// location.hash is automatically decoded in firefox, the rest of the url should be from the
						// object (location unless we're testing) to avoid the inclusion of the authority
						return uri.protocol + "//" + uri.host + uri.pathname + uri.search + hash + search;
					},

					/**
					 * Return the original document url
					 * @method getDocumentUrl
					 * @member ns.util.path
					 * @param {boolean} [asParsedObject=false]
					 * @return {string|Object}
					 * @static
					 */
					getDocumentUrl: function (asParsedObject) {
						return asParsedObject ? utilsObject.copy(path.documentUrl) : path.documentUrl.href;
					},

					/**
					 * Parse a location into a structure
					 * @method parseLocation
					 * @return {Object}
					 * @member ns.util.path
					 */
					parseLocation: function () {
						return this.parseUrl(this.getLocation());
					},

					/**
					 * Parse a URL into a structure that allows easy access to
					 * all of the URL components by name.
					 * If we're passed an object, we'll assume that it is
					 * a parsed url object and just return it back to the caller.
					 * @method parseUrl
					 * @member ns.util.path
					 * @param {string|Object} url
					 * @return {Object} uri record
					 * @return {string} return.href
					 * @return {string} return.hrefNoHash
					 * @return {string} return.hrefNoSearch
					 * @return {string} return.domain
					 * @return {string} return.protocol
					 * @return {string} return.doubleSlash
					 * @return {string} return.authority
					 * @return {string} return.username
					 * @return {string} return.password
					 * @return {string} return.host
					 * @return {string} return.hostname
					 * @return {string} return.port
					 * @return {string} return.pathname
					 * @return {string} return.directory
					 * @return {string} return.filename
					 * @return {string} return.search
					 * @return {string} return.hash
					 * @return {string} return.hashSearch
					 * @static
					 */
					parseUrl: function (url) {
						var matches;

						if (typeof url === "object") {
							return url;
						}
						matches = path.urlParseRE.exec(url || "") || [];

						// Create an object that allows the caller to access the sub-matches
						// by name. Note that IE returns an empty string instead of undefined,
						// like all other browsers do, so we normalize everything so its consistent
						// no matter what browser we're running on.
						return {
							href: matches[0] || "",
							hrefNoHash: matches[1] || "",
							hrefNoSearch: matches[2] || "",
							domain: matches[3] || "",
							protocol: matches[4] || "",
							doubleSlash: matches[5] || "",
							authority: matches[6] || "",
							username: matches[8] || "",
							password: matches[9] || "",
							host: matches[10] || "",
							hostname: matches[11] || "",
							port: matches[12] || "",
							pathname: matches[13] || "",
							directory: matches[14] || "",
							filename: matches[15] || "",
							search: matches[16] || "",
							hash: matches[18] || "",
							hashSearch: matches[19] || ""
						};
					},

					/**
					 * Turn relPath into an absolute path. absPath is
					 * an optional absolute path which describes what
					 * relPath is relative to.
					 * @method makePathAbsolute
					 * @member ns.util.path
					 * @param {string} relPath
					 * @param {string} [absPath=""]
					 * @return {string}
					 * @static
					 */
					makePathAbsolute: function (relPath, absPath) {
						var absStack,
							relStack,
							directory,
							i;

						if (relPath && relPath.charAt(0) === "/") {
							return relPath;
						}

						relPath = relPath || "";
						absPath = absPath ? absPath.replace(/^\/|(\/[^\/]*|[^\/]+)$/g, "") : "";

						absStack = absPath ? absPath.split("/") : [];
						relStack = relPath.split("/");
						for (i = 0; i < relStack.length; i++) {
							directory = relStack[i];
							switch (directory) {
								case ".":
									break;
								case "..":
									if (absStack.length) {
										absStack.pop();
									}
									break;
								default:
									absStack.push(directory);
									break;
							}
						}
						return "/" + absStack.join("/");
					},

					/**
					 * Returns true if both urls have the same domain.
					 * @method isSameDomain
					 * @member ns.util.path
					 * @param {string|Object} absUrl1
					 * @param {string|Object} absUrl2
					 * @return {boolean}
					 * @static
					 */
					isSameDomain: function (absUrl1, absUrl2) {
						return path.parseUrl(absUrl1).domain === path.parseUrl(absUrl2).domain;
					},

					/**
					 * Returns true for any relative variant.
					 * @method isRelativeUrl
					 * @member ns.util.path
					 * @param {string|Object} url
					 * @return {boolean}
					 * @static
					 */
					isRelativeUrl: function (url) {
						// All relative Url variants have one thing in common, no protocol.
						return path.parseUrl(url).protocol === "";
					},

					/**
					 * Returns true for an absolute url.
					 * @method isAbsoluteUrl
					 * @member ns.util.path
					 * @param {string} url
					 * @return {boolean}
					 * @static
					 */
					isAbsoluteUrl: function (url) {
						return path.parseUrl(url).protocol !== "";
					},

					/**
					 * Turn the specified relative URL into an absolute one. This function
					 * can handle all relative variants (protocol, site, document, query, fragment).
					 * @method makeUrlAbsolute
					 * @member ns.util.path
					 * @param {string} relUrl
					 * @param {string} absUrl
					 * @return {string}
					 * @static
					 */
					makeUrlAbsolute: function (relUrl, absUrl) {
						var relObj,
							absObj,
							protocol,
							doubleSlash,
							authority,
							hasPath,
							pathname,
							search,
							hash;

						if (!path.isRelativeUrl(relUrl)) {
							return relUrl;
						}

						relObj = path.parseUrl(relUrl);
						absObj = path.parseUrl(absUrl);
						protocol = relObj.protocol || absObj.protocol;
						doubleSlash = relObj.protocol ? relObj.doubleSlash : (relObj.doubleSlash || absObj.doubleSlash);
						authority = relObj.authority || absObj.authority;
						hasPath = relObj.pathname !== "";
						pathname = path.makePathAbsolute(relObj.pathname || absObj.filename, absObj.pathname);
						search = relObj.search || (!hasPath && absObj.search) || "";
						hash = relObj.hash;

						return protocol + doubleSlash + authority + pathname + search + hash;
					},

					/**
					 * Add search (aka query) params to the specified url.
					 * If page is embedded page, search query will be added after
					 * hash tag. It's allowed to add query content for both external
					 * pages and embedded pages.
					 * Examples:
					 * http://domain/path/index.html#embedded?search=test
					 * http://domain/path/external.html?s=query#embedded?s=test
					 * @method addSearchParams
					 * @member ns.util.path
					 * @param {string|Object} url
					 * @param {Object|string} params
					 * @return {string}
					 */
					addSearchParams: function (url, params) {
						var urlObject = path.parseUrl(url),
							paramsString = (typeof params === "object") ? this.getAsURIParameters(params) : params,
							searchChar,
							urlObjectHash = urlObject.hash;

						if (path.isEmbedded(url) && paramsString.length > 0) {
							searchChar = urlObject.hashSearch || "?";
							return urlObject.hrefNoHash + (urlObjectHash || "") + searchChar + (searchChar.charAt(searchChar.length - 1) === "?" ? "" : "&") + paramsString;
						}

						searchChar = urlObject.search || "?";
						return urlObject.hrefNoSearch + searchChar + (searchChar.charAt(searchChar.length - 1) === "?" ? "" : "&") + paramsString + (urlObjectHash || "");
					},

					/**
					 * Add search params to the specified url with hash
					 * @method addHashSearchParams
					 * @member ns.util.path
					 * @param {string|Object} url
					 * @param {Object|string} params
					 * @return {string}
					 */
					addHashSearchParams: function (url, params) {
						var urlObject = path.parseUrl(url),
							paramsString = (typeof params === "object") ? path.getAsURIParameters(params) : params,
							hash = urlObject.hash,
							searchChar = hash ? (hash.indexOf("?") < 0 ? hash + "?" : hash + "&") : "#?";

						return urlObject.hrefNoHash + searchChar + (searchChar.charAt(searchChar.length - 1) === "?" ? "" : "&") + paramsString;
					},

					/**
					 * Convert absolute Url to data Url
					 * - for embedded pages strips parameters
					 * - for the same domain as document base remove domain
					 * otherwise returns decoded absolute Url
					 * @method convertUrlToDataUrl
					 * @member ns.util.path
					 * @param {string} absUrl
					 * @param {boolean} dialogHashKey
					 * @param {Object} documentBase uri structure
					 * @return {string}
					 * @static
					 */
					convertUrlToDataUrl: function (absUrl, dialogHashKey, documentBase) {
						var urlObject = path.parseUrl(absUrl);

						if (path.isEmbeddedPage(urlObject, !!dialogHashKey)) {
							// Keep hash and search data for embedded page
							return path.getFilePath(urlObject.hash + urlObject.hashSearch, dialogHashKey);
						}
						documentBase = documentBase || path.documentBase;
						if (path.isSameDomain(urlObject, documentBase)) {
							return urlObject.hrefNoHash.replace(documentBase.domain, "");
						}

						return window.decodeURIComponent(absUrl);
					},

					/**
					 * Get path from current hash, or from a file path
					 * @method get
					 * @member ns.util.path
					 * @param {string} newPath
					 * @return {string}
					 */
					get: function (newPath) {
						if (newPath === undefined) {
							newPath = this.parseLocation().hash;
						}
						return this.stripHash(newPath).replace(/[^\/]*\.[^\/*]+$/, "");
					},

					/**
					 * Test if a given url (string) is a path
					 * NOTE might be exceptionally naive
					 * @method isPath
					 * @member ns.util.path
					 * @param {string} url
					 * @return {boolean}
					 * @static
					 */
					isPath: function (url) {
						return (/\//).test(url);
					},

					/**
					 * Return a url path with the window's location protocol/hostname/pathname removed
					 * @method clean
					 * @member ns.util.path
					 * @param {string} url
					 * @param {Object} documentBase  uri structure
					 * @return {string}
					 * @static
					 */
					clean: function (url, documentBase) {
						return url.replace(documentBase.domain, "");
					},

					/**
					 * Just return the url without an initial #
					 * @method stripHash
					 * @member ns.util.path
					 * @param {string} url
					 * @return {string}
					 * @static
					 */
					stripHash: function (url) {
						return url.replace(/^#/, "");
					},

					/**
					 * Return the url without an query params
					 * @method stripQueryParams
					 * @member ns.util.path
					 * @param {string} url
					 * @return {string}
					 * @static
					 */
					stripQueryParams: function (url) {
						return url.replace(/\?.*$/, "");
					},

					/**
					 * Validation proper hash
					 * @method isHashValid
					 * @member ns.util.path
					 * @param {string} hash
					 * @static
					 */
					isHashValid: function (hash) {
						return (/^#[^#]+$/).test(hash);
					},

					/**
					 * Check whether a url is referencing the same domain, or an external domain or different
					 * protocol could be mailto, etc
					 * @method isExternal
					 * @member ns.util.path
					 * @param {string|Object} url
					 * @param {Object} documentUrl uri object
					 * @return {boolean}
					 * @static
					 */
					isExternal: function (url, documentUrl) {
						var urlObject = path.parseUrl(url);

						return urlObject.protocol && urlObject.domain !== documentUrl.domain ? true : false;
					},

					/**
					 * Check if the url has protocol
					 * @method hasProtocol
					 * @member ns.util.path
					 * @param {string} url
					 * @return {boolean}
					 * @static
					 */
					hasProtocol: function (url) {
						return (/^(:?\w+:)/).test(url);
					},

					/**
					 * Check if the url refers to embedded content
					 * @method isEmbedded
					 * @member ns.util.path
					 * @param {string} url
					 * @return {boolean}
					 * @static
					 */
					isEmbedded: function (url) {
						var urlObject = path.parseUrl(url);

						if (urlObject.protocol !== "") {
							return (!path.isPath(urlObject.hash) && !!urlObject.hash && (urlObject.hrefNoHash === path.parseLocation().hrefNoHash));
						}
						return (/\?.*#|^#/).test(urlObject.href);
					},

					/**
					 * Get the url as it would look squashed on to the current resolution url
					 * @method squash
					 * @member ns.util.path
					 * @param {string} url
					 * @param {string} [resolutionUrl=undefined]
					 * @return {string}
					 */
					squash: function (url, resolutionUrl) {
						var href,
							cleanedUrl,
							search,
							stateIndex,
							isPath = this.isPath(url),
							uri = this.parseUrl(url),
							preservedHash = uri.hash,
							uiState = "";

						// produce a url against which we can resole the provided path
						resolutionUrl = resolutionUrl || (path.isPath(url) ? path.getLocation() : path.getDocumentUrl());

						// If the url is anything but a simple string, remove any preceding hash
						// eg #foo/bar -> foo/bar
						//	#foo -> #foo
						cleanedUrl = isPath ? path.stripHash(url) : url;

						// If the url is a full url with a hash check if the parsed hash is a path
						// if it is, strip the #, and use it otherwise continue without change
						cleanedUrl = path.isPath(uri.hash) ? path.stripHash(uri.hash) : cleanedUrl;

						// Split the UI State keys off the href
						stateIndex = cleanedUrl.indexOf(this.uiStateKey);

						// store the ui state keys for use
						if (stateIndex > -1) {
							uiState = cleanedUrl.slice(stateIndex);
							cleanedUrl = cleanedUrl.slice(0, stateIndex);
						}

						// make the cleanedUrl absolute relative to the resolution url
						href = path.makeUrlAbsolute(cleanedUrl, resolutionUrl);

						// grab the search from the resolved url since parsing from
						// the passed url may not yield the correct result
						search = this.parseUrl(href).search;

						// @TODO all this crap is terrible, clean it up
						if (isPath) {
							// reject the hash if it's a path or it's just a dialog key
							if (path.isPath(preservedHash) || preservedHash.replace("#", "").indexOf(this.uiStateKey) === 0) {
								preservedHash = "";
							}

							// Append the UI State keys where it exists and it's been removed
							// from the url
							if (uiState && preservedHash.indexOf(this.uiStateKey) === -1) {
								preservedHash += uiState;
							}

							// make sure that pound is on the front of the hash
							if (preservedHash.indexOf("#") === -1 && preservedHash !== "") {
								preservedHash = "#" + preservedHash;
							}

							// reconstruct each of the pieces with the new search string and hash
							href = path.parseUrl(href);
							href = href.protocol + "//" + href.host + href.pathname + search + preservedHash;
						} else {
							href += href.indexOf("#") > -1 ? uiState : "#" + uiState;
						}

						return href;
					},

					/**
					 * Check if the hash is preservable
					 * @method isPreservableHash
					 * @member ns.util.path
					 * @param {string} hash
					 * @return {boolean}
					 */
					isPreservableHash: function (hash) {
						return hash.replace("#", "").indexOf(this.uiStateKey) === 0;
					},

					/**
					 * Escape weird characters in the hash if it is to be used as a selector
					 * @method hashToSelector
					 * @member ns.util.path
					 * @param {string} hash
					 * @return {string}
					 * @static
					 */
					hashToSelector: function (hash) {
						var hasHash = (hash.substring(0, 1) === "#");

						if (hasHash) {
							hash = hash.substring(1);
						}
						return (hasHash ? "#" : "") + hash.replace(new RegExp("([!\"#$%&'()*+,./:;<=>?@[\\]^`{|}~])", "g"), "\\$1");
					},

					/**
					 * Check if the specified url refers to the first page in the main application document.
					 * @method isFirstPageUrl
					 * @member ns.util.path
					 * @param {string} url
					 * @param {HTMLElement} firstPageElement first page element
					 * @param {string} documentBase uri structure
					 * @param {boolean} documentBaseDiffers
					 * @param {Object} documentUrl uri structure
					 * @return {boolean}
					 * @static
					 */
					isFirstPageUrl: function (url, firstPageElement, documentBase, documentBaseDiffers, documentUrl) {
						var urlStructure,
							samePath,
							firstPageId,
							hash;

						documentBase = documentBase === undefined ? path.documentBase : documentBase;
						documentBaseDiffers = documentBaseDiffers === undefined ? path.documentBaseDiffers : documentBaseDiffers;
						documentUrl = documentUrl === undefined ? path.documentUrl : documentUrl;

						// We only deal with absolute paths.
						urlStructure = path.parseUrl(path.makeUrlAbsolute(url, documentBase));

						// Does the url have the same path as the document?
						samePath = urlStructure.hrefNoHash === documentUrl.hrefNoHash || (documentBaseDiffers && urlStructure.hrefNoHash === documentBase.hrefNoHash);

						// Get the id of the first page element if it has one.
						firstPageId = firstPageElement && firstPageElement.id || false;
						hash = urlStructure.hash;

						// The url refers to the first page if the path matches the document and
						// it either has no hash value, or the hash is exactly equal to the id of the
						// first page element.
						return samePath && (!hash || hash === "#" || (firstPageId && hash.replace(/^#/, "") === firstPageId));
					},

					/**
					 * Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
					 * requests if the document doing the request was loaded via the file:// protocol.
					 * This is usually to allow the application to "phone home" and fetch app specific
					 * data. We normally let the browser handle external/cross-domain urls, but if the
					 * allowCrossDomainPages option is true, we will allow cross-domain http/https
					 * requests to go through our page loading logic.
					 * @method isPermittedCrossDomainRequest
					 * @member ns.util.path
					 * @param {Object} docUrl
					 * @param {string} reqUrl
					 * @return {boolean}
					 * @static
					 */
					isPermittedCrossDomainRequest: function (docUrl, reqUrl) {
						return ns.getConfig("allowCrossDomainPages", false) &&
							docUrl.protocol === "file:" &&
							reqUrl.search(/^https?:/) !== -1;
					},

					/**
					 * Convert a object data to URI parameters
					 * @method getAsURIParameters
					 * @member ns.util.path
					 * @param {Object} data
					 * @return {string}
					 * @static
					 */
					getAsURIParameters: function (data) {
						var url = "",
							key;

						for (key in data) {
							if (data.hasOwnProperty(key)) {
								url += encodeURIComponent(key) + "=" + encodeURIComponent(data[key]) + "&";
							}
						}
						return url.substring(0, url.length - 1);
					},

					/**
					 * Document Url
					 * @member ns.util.path
					 * @property {string|null} documentUrl
					 */
					documentUrl: null,

					/**
					 * The document base differs
					 * @member ns.util.path
					 * @property {boolean} documentBaseDiffers
					 */
					documentBaseDiffers: false,

					/**
					 * Set location hash to path
					 * @method set
					 * @member ns.util.path
					 * @param {string} path
					 * @static
					 */
					set: function (path) {
						location.hash = path;
					},

					/**
					 * Return the substring of a file path before the sub-page key,
					 * for making a server request
					 * @method getFilePath
					 * @member ns.util.path
					 * @param {string} path
					 * @param {string} dialogHashKey
					 * @return {string}
					 * @static
					 */
					getFilePath: function (path, dialogHashKey) {
						var splitKey = "&" + ns.getConfig("subPageUrlKey", "");

						return path && path.split(splitKey)[0].split(dialogHashKey)[0];
					},

					/**
					 * Remove the preceding hash, any query params, and dialog notations
					 * @method cleanHash
					 * @member ns.util.path
					 * @param {string} hash
					 * @param {string} dialogHashKey
					 * @return {string}
					 * @static
					 */
					cleanHash: function (hash, dialogHashKey) {
						return path.stripHash(hash.replace(/\?.*$/, "").replace(dialogHashKey, ""));
					},

					/**
					 * Check if url refers to the embedded page
					 * @method isEmbeddedPage
					 * @member ns.util.path
					 * @param {string} url
					 * @param {boolean} allowEmbeddedOnlyBaseDoc
					 * @return {boolean}
					 * @static
					 */
					isEmbeddedPage: function (url, allowEmbeddedOnlyBaseDoc) {
						var urlObject = path.parseUrl(url);

						//if the path is absolute, then we need to compare the url against
						//both the documentUrl and the documentBase. The main reason for this
						//is that links embedded within external documents will refer to the
						//application document, whereas links embedded within the application
						//document will be resolved against the document base.
						if (urlObject.protocol !== "") {
							return (urlObject.hash &&
							(allowEmbeddedOnlyBaseDoc ?
								urlObject.hrefNoHash === path.documentUrl.hrefNoHash :
								urlObject.hrefNoHash === path.parseLocation().hrefNoHash));
						}
						return (/^#/).test(urlObject.href);
					}
				};

			path.documentUrl = path.parseLocation();

			base = document.querySelector("base");

			/**
			 * The document base URL for the purposes of resolving relative URLs,
			 * and the name of the default browsing context for the purposes of
			 * following hyperlinks
			 * @member ns.util.path
			 * @property {Object} documentBase uri structure
			 * @static
			 */
			path.documentBase = base ? path.parseUrl(path.makeUrlAbsolute(base.getAttribute("href"),
				path.documentUrl.href)) : path.documentUrl;

			path.documentBaseDiffers = (path.documentUrl.hrefNoHash !== path.documentBase.hrefNoHash);

			/**
			 * Get document base
			 * @method getDocumentBase
			 * @member ns.util.path
			 * @param {boolean} [asParsedObject=false]
			 * @return {string|Object}
			 * @static
			 */
			path.getDocumentBase = function (asParsedObject) {
				return asParsedObject ? utilsObject.copy(path.documentBase) : path.documentBase.href;
			};

			/**
			 * Find the closest page and extract out its url
			 * @method getClosestBaseUrl
			 * @member ns.util.path
			 * @param {HTMLElement} element
			 * @param {string} selector
			 * @return {string}
			 * @static
			 */
			path.getClosestBaseUrl = function (element, selector) {
				// Find the closest page and extract out its url.
				var url = utilsDOM.getNSData(utilsSelectors.getClosestBySelector(element, selector), "url"),
					baseUrl = path.documentBase.hrefNoHash;

				if (!ns.getConfig("dynamicBaseEnabled", true) || !url || !path.isPath(url)) {
					url = baseUrl;
				}

				return path.makeUrlAbsolute(url, baseUrl);
			};

			ns.util.path = path;
			}(window, window.document, ns));

/**
 *
 */
(function (window) {
	"use strict";
				var isarray = Array.isArray;

			pathToRegexp.parse = parse
			pathToRegexp.compile = compile
			pathToRegexp.tokensToFunction = tokensToFunction
			pathToRegexp.tokensToRegExp = tokensToRegExp

/**
 * Start original file
 * Licence MIT
 * https://github.com/pillarjs/path-to-regexp
 */
/**
 * The main path matching regexp utility.
 *
 * @type {RegExp}
 */
var PATH_REGEXP = new RegExp([
	// Match escaped characters that would otherwise appear in future matches.
	// This allows the user to escape special characters that won't transform.
	'(\\\\.)',
	// Match Express-style parameters and un-named parameters with a prefix
	// and optional suffixes. Matches appear as:
	//
	// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
	// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
	'([\\/.])?(?:\\:(\\w+)(?:\\(((?:\\\\.|[^)])*)\\))?|\\(((?:\\\\.|[^)])*)\\))([+*?])?'
].join('|'), 'g')

/**
 * Parse a string for the raw tokens.
 *
 * @param  {String} str
 * @return {Array}
 */
function parse (str) {
	var tokens = []
	var key = 0
	var index = 0
	var path = ''
	var res

	while ((res = PATH_REGEXP.exec(str)) != null) {
		var m = res[0]
		var escaped = res[1]
		var offset = res.index
		path += str.slice(index, offset)
		index = offset + m.length

		// Ignore already escaped sequences.
		if (escaped) {
			path += escaped[1]
			continue
		}

		// Push the current path onto the tokens.
		if (path) {
			tokens.push(path)
			path = ''
		}

		var prefix = res[2]
		var name = res[3]
		var capture = res[4]
		var group = res[5]
		var suffix = res[6]

		var repeat = suffix === '+' || suffix === '*'
		var optional = suffix === '?' || suffix === '*'
		var delimiter = prefix || '/'

		tokens.push({
			name: name || key++,
			prefix: prefix || '',
			delimiter: delimiter,
			optional: optional,
			repeat: repeat,
			pattern: escapeGroup(capture || group || '[^' + delimiter + ']+?')
		})
	}

	// Match any characters still remaining.
	if (index < str.length) {
		path += str.substr(index)
	}

	// If the path exists, push it onto the end.
	if (path) {
		tokens.push(path)
	}

	return tokens
}

/**
 * Compile a string to a template function for the path.
 *
 * @param  {String}   str
 * @return {Function}
 */
function compile (str) {
	return tokensToFunction(parse(str))
}

/**
 * Expose a method for transforming tokens into the path function.
 */
function tokensToFunction (tokens) {
	// Compile all the tokens into regexps.
	var matches = new Array(tokens.length)

	// Compile all the patterns before compilation.
	for (var i = 0; i < tokens.length; i++) {
		if (typeof tokens[i] === 'object') {
			matches[i] = new RegExp('^' + tokens[i].pattern + '$')
		}
	}

	return function (obj) {
		var path = ''

		obj = obj || {}

		for (var i = 0; i < tokens.length; i++) {
			var key = tokens[i]

			if (typeof key === 'string') {
				path += key

				continue
			}

			var value = obj[key.name]

			if (value == null) {
				if (key.optional) {
					continue
				} else {
					throw new TypeError('Expected "' + key.name + '" to be defined')
				}
			}

			if (isarray(value)) {
				if (!key.repeat) {
					throw new TypeError('Expected "' + key.name + '" to not repeat')
				}

				if (value.length === 0) {
					if (key.optional) {
						continue
					} else {
						throw new TypeError('Expected "' + key.name + '" to not be empty')
					}
				}

				for (var j = 0; j < value.length; j++) {
					if (!matches[i].test(value[j])) {
						throw new TypeError('Expected all "' + key.name + '" to match "' + key.pattern + '"')
					}

					path += (j === 0 ? key.prefix : key.delimiter) + encodeURIComponent(value[j])
				}

				continue
			}

			if (!matches[i].test(value)) {
				throw new TypeError('Expected "' + key.name + '" to match "' + key.pattern + '"')
			}

			path += key.prefix + encodeURIComponent(value)
		}

		return path
	}
}

/**
 * Escape a regular expression string.
 *
 * @param  {String} str
 * @return {String}
 */
function escapeString (str) {
	return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1')
}

/**
 * Escape the capturing group by escaping special characters and meaning.
 *
 * @param  {String} group
 * @return {String}
 */
function escapeGroup (group) {
	return group.replace(/([=!:$\/()])/g, '\\$1')
}

/**
 * Attach the keys as a property of the regexp.
 *
 * @param  {RegExp} re
 * @param  {Array}  keys
 * @return {RegExp}
 */
function attachKeys (re, keys) {
	re.keys = keys
	return re
}

/**
 * Get the flags for a regexp from the options.
 *
 * @param  {Object} options
 * @return {String}
 */
function flags (options) {
	return options.sensitive ? '' : 'i'
}

/**
 * Pull out keys from a regexp.
 *
 * @param  {RegExp} path
 * @param  {Array}  keys
 * @return {RegExp}
 */
function regexpToRegexp (path, keys) {
	// Use a negative lookahead to match only capturing groups.
	var groups = path.source.match(/\((?!\?)/g)

	if (groups) {
		for (var i = 0; i < groups.length; i++) {
			keys.push({
				name: i,
				prefix: null,
				delimiter: null,
				optional: false,
				repeat: false,
				pattern: null
			})
		}
	}

	return attachKeys(path, keys)
}

/**
 * Transform an array into a regexp.
 *
 * @param  {Array}  path
 * @param  {Array}  keys
 * @param  {Object} options
 * @return {RegExp}
 */
function arrayToRegexp (path, keys, options) {
	var parts = []

	for (var i = 0; i < path.length; i++) {
		parts.push(pathToRegexp(path[i], keys, options).source)
	}

	var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options))

	return attachKeys(regexp, keys)
}

/**
 * Create a path regexp from string input.
 *
 * @param  {String} path
 * @param  {Array}  keys
 * @param  {Object} options
 * @return {RegExp}
 */
function stringToRegexp (path, keys, options) {
	var tokens = parse(path)
	var re = tokensToRegExp(tokens, options)

	// Attach keys back to the regexp.
	for (var i = 0; i < tokens.length; i++) {
		if (typeof tokens[i] !== 'string') {
			keys.push(tokens[i])
		}
	}

	return attachKeys(re, keys)
}

/**
 * Expose a function for taking tokens and returning a RegExp.
 *
 * @param  {Array}  tokens
 * @param  {Array}  keys
 * @param  {Object} options
 * @return {RegExp}
 */
function tokensToRegExp (tokens, options) {
	options = options || {}

	var strict = options.strict
	var end = options.end !== false
	var route = ''
	var lastToken = tokens[tokens.length - 1]
	var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken)

	// Iterate over the tokens and create our regexp string.
	for (var i = 0; i < tokens.length; i++) {
		var token = tokens[i]

		if (typeof token === 'string') {
			route += escapeString(token)
		} else {
			var prefix = escapeString(token.prefix)
			var capture = token.pattern

			if (token.repeat) {
				capture += '(?:' + prefix + capture + ')*'
			}

			if (token.optional) {
				if (prefix) {
					capture = '(?:' + prefix + '(' + capture + '))?'
				} else {
					capture = '(' + capture + ')?'
				}
			} else {
				capture = prefix + '(' + capture + ')'
			}

			route += capture
		}
	}

	// In non-strict mode we allow a slash at the end of match. If the path to
	// match already ends with a slash, we remove it for consistency. The slash
	// is valid at the end of a path match, not in the middle. This is important
	// in non-ending mode, where "/test/" shouldn't match "/test//route".
	if (!strict) {
		route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?'
	}

	if (end) {
		route += '$'
	} else {
		// In non-ending mode, we need the capturing groups to match as much as
		// possible by using a positive lookahead to the end or next path segment.
		route += strict && endsWithSlash ? '' : '(?=\\/|$)'
	}

	return new RegExp('^' + route, flags(options))
}

/**
 * Normalize the given path string, returning a regular expression.
 *
 * An empty array can be passed in for the keys, which will hold the
 * placeholder key descriptions. For example, using `/user/:id`, `keys` will
 * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
 *
 * @param  {(String|RegExp|Array)} path
 * @param  {Array}                 [keys]
 * @param  {Object}                [
 *
 * options]
 * @return {RegExp}
 */
function pathToRegexp (path, keys, options) {
	keys = keys || []

	if (!isarray(keys)) {
		options = keys
		keys = []
	} else if (!options) {
		options = {}
	}

	if (path instanceof RegExp) {
		return regexpToRegexp(path, keys, options)
	}

	if (isarray(path)) {
		return arrayToRegexp(path, keys, options)
	}

	return stringToRegexp(path, keys, options)
}

/**
 * End original file
 */
			window.pathToRegexp = pathToRegexp;
			}(window));
/*global define, ns */
/*
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Path to Regexp Utility
 * Convert string to regexp and can match path string to one defined regex path
 *
 * Syntax of paths is the same as in Express for nodejs
 *
 * Library based on https://github.com/pillarjs/path-to-regexp
 * @class ns.util.pathToRegexp
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 *
 */
(function (window) {
	"use strict";
	
			ns.util.pathToRegexp = window.pathToRegexp;

			}(window));

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Router
 * Namespace for routers
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @class ns.router
 */
(function () {
	"use strict";
				ns.router = ns.router || {};
			}());

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Route Namespace
 * Object contains rules for router.
 *
 * @class ns.router.route
 */
/*
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function () {
	"use strict";
				ns.router.route = ns.router.route || {};
			}());

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * #History
 * Object controls history changes.
 *
 * @class ns.history
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window) {
	"use strict";
				var historyVolatileMode,
				object = ns.util.object,
				historyUid = 0,
				historyActiveIndex = 0,
				windowHistory = window.history,
				history = {
					/**
					 * Property contains active state in history.
					 * @property {Object} activeState
					 * @static
					 * @member ns.history
					 */
					activeState: null,

					/**
					 * Property contains starting url for tau instance
					 * @property {Object} startURL
					 * @static
					 * @member ns.history
					 */
					startURL: null,

					/**
					 * This method replaces or pushes state to history.
					 * @method replace
					 * @param {Object} state The state object
					 * @param {string} stateTitle The title of state
					 * @param {string} url The new history entry's URL
					 * @static
					 * @member ns.history
					 */
					replace: function (state, stateTitle, url) {
						var newState = object.merge({}, state, {
							uid: historyVolatileMode ? historyActiveIndex : ++historyUid,
							stateUrl: url,
							stateTitle: stateTitle
						});

						if (!this.startURL && url && url.length) {
							this.startURL = url;
						}
						windowHistory[historyVolatileMode ? "replaceState" : "pushState"](newState, stateTitle, url);
						history.setActive(newState);
					},

					/**
					 * This method moves backward through history.
					 * @method back
					 * @static
					 * @member ns.history
					 */
					back: function () {
						// In case of running tau app in web browser
						// don't allow to go back outside tau history
						if (this.startURL !== window.location.href) {
							windowHistory.back();
						}
					},

					/**
					 * This method sets active state.
					 * @method setActive
					 * @param {Object} state Activated state
					 * @static
					 * @member ns.history
					 */
					setActive: function (state) {
						if (state) {
							history.activeState = state;
							historyActiveIndex = state.uid;

							if (state.volatileRecord) {
								history.enableVolatileMode();
								return;
							}
						}

						history.disableVolatileMode();
					},

					/**
					 * This method returns "back" if state is in history or "forward" if it is new state.
					 * @method getDirection
					 * @param {Object} state Checked state
					 * @return {"back"|"forward"}
					 * @static
					 * @member ns.history
					 */
					getDirection: function (state) {
						if (state) {
							return state.uid <= historyActiveIndex ? "back" : "forward";
						}
						return "back";
					},

					/**
					 * This method sets volatile mode to true.
					 * @method enableVolatileMode
					 * @static
					 * @member ns.history
					 */
					enableVolatileMode: function () {
						historyVolatileMode = true;
					},

					/**
					 * This method sets volatile mode to false.
					 * @method disableVolatileMode
					 * @static
					 * @member ns.history
					 */
					disableVolatileMode: function () {
						historyVolatileMode = false;
					}
				};

			ns.history = history;
			}(window));

/*global window, define, ns */
/*jslint browser: true, nomen: true */
/**
 * # History manager
 *
 * Control events connected with history change and trigger events to controller
 * or router.
 *
 * @class ns.history.manager
 * @since 2.4
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 */
/**
 * Event historystatechange
 * @event historystatechange
 * @class ns.history.manager
 */
/**
 * Event historyhashchange
 * @event historyhashchange
 * @class ns.history.manager
 */
/**
 * Event historyenabled
 * @event historyenabled
 * @class ns.history.manager
 */
/**
 * Event historydisabled
 * @event historydisabled
 * @class ns.history.manager
 */
(function (window, document) {
	"use strict";
				var manager = Object.create(null), // we don't need the Object proto
				WINDOW_EVENT_POPSTATE = "popstate",
				WINDOW_EVENT_HASHCHANGE = "hashchange",
				DOC_EVENT_VCLICK = "vclick",
				LINK_SELECTOR = "a,tau-button",
				util = ns.util,
				history = ns.history,
				eventUtils = ns.event,
				selectorUtils = util.selectors,
				objectUtils = util.object,
				pathUtils = util.path,
				DOM = util.DOM,
				EVENT_STATECHANGE = "historystatechange",
				EVENT_HASHCHANGE = "historyhashchange",
				EVENT_ENABLED = "historyenabled",
				EVENT_DISABLED = "historydisabled",
				/**
				 * Engine event types
				 * @property {Object} events
				 * @property {string} events.STATECHANGE="historystatechange" event name on history manager change state
				 * @property {string} events.HASHCHANGE="historyhashchange" event name on history manager change hash
				 * @property {string} events.ENABLED="historyenabled" event name on enable history manager
				 * @property {string} events.DISABLED="historydisabled" event name on disable history manager
				 * @static
				 * @readonly
				 * @member ns.history.manager
				 */
				events = {
					STATECHANGE: EVENT_STATECHANGE,
					HASHCHANGE: EVENT_HASHCHANGE,
					ENABLED: EVENT_ENABLED,
					DISABLED: EVENT_DISABLED
				};

			manager.events = events;

			/**
			 * Trigger event "historystatechange" on document
			 * @param {Object} options
			 * @return {boolean}
			 */
			function triggerStateChange(options) {
				return eventUtils.trigger(document, EVENT_STATECHANGE, options, true, true);
			}

			/**
			 * Callback for link click
			 * @param {Event} event
			 * @return {boolean}
			 */
			function onLinkAction(event) {
				var target = event.target,
					link = selectorUtils.getClosestBySelector(target, LINK_SELECTOR),
					href,
					useDefaultUrlHandling,
					options, // this should be empty object but some utils that work on it
					rel; // require hasOwnProperty :(

								if (link && event.which === 1) {
					href = link.getAttribute("href");
					rel = link.getAttribute("rel");
					useDefaultUrlHandling = rel === "external" || link.hasAttribute("target");
					if (!useDefaultUrlHandling) {
						options = DOM.getData(link);
						options.event = event;
						if (rel && !options.rel) {
							options.rel = rel;
						} else {
							rel = options.rel;
						}
						if (href && !options.href) {
							options.href = href;
						}
						if (rel === "popup" && link && !options.link) {
							options.link = link;
						}
						history.disableVolatileMode();
						if (!triggerStateChange(options)) {
							// mark as handled
							// but not on back
							if (!rel || (rel !== "back")) {
								eventUtils.preventDefault(event);
								return false;
							}
						}
					}
				}
				return true;
			}


			/**
			 * Callback on popstate event.
			 * @param {Event} event
			 */
			function onPopState(event) {
				var state = event.state,
					lastState = history.activeState,
					options = {},
					reverse,
					resultOfTrigger = true,
					skipTriggerStateChange = false;

								if (manager.locked) {
					history.disableVolatileMode();
					if (lastState) {
						history.replace(lastState, lastState.stateTitle, lastState.stateUrl);
					}
				} else if (state) {
					reverse = history.getDirection(state) === "back";
					options = objectUtils.merge(options, state, {
						reverse: reverse,
						transition: reverse ? ((lastState && lastState.transition) || "none") : state.transition,
						fromHashChange: true
					});

					if (lastState) {
						resultOfTrigger = eventUtils.trigger(document, EVENT_HASHCHANGE, objectUtils.merge(options,
							{url: pathUtils.getLocation(), stateUrl: lastState.stateUrl}), true, true);

						
						// if EVENT HASHCHANGE has been triggered successfully then skip trigger HistoryStateChange
						skipTriggerStateChange = resultOfTrigger;
					}

					state.url = pathUtils.getLocation();
					history.setActive(state);

					if (!skipTriggerStateChange) {
						options.event = event;
						triggerStateChange(options);
					}
				}
			}

			/**
			 * Callback on "hashchange" event
			 * @param {Event} event
			 */
			function onHashChange(event) {
				var newURL = event.newURL;

								if (newURL && history.activeState.url !== newURL) {
					triggerStateChange({href: newURL, fromHashChange: true, event: event});
				}
			}

			/**
			 * Inform that manager is enabled or not.
			 * @property {boolean} [enabled=true]
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.enabled = true;
			/**
			 * Informs that manager is enabled or not.
			 *
			 * If manager is locked then not trigger events historystatechange.
			 * @property {boolean} [locked=false]
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.locked = false;

			/**
			 * Locks history manager.
			 *
			 * Sets locked property to true.
			 *
			 *	@example
			 *		tau.history.manager.lock();
			 *
			 * @method lock
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.lock = function () {
				this.locked = true;
			};

			/**
			 * Unlocks history manager.
			 *
			 * Sets locked property to false.
			 *
			 *	@example
			 *		tau.history.manager.unlock();
			 *
			 * @method unlock
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.unlock = function () {
				this.locked = false;
			};

			/**
			 * Enables history manager.
			 *
			 * This method adds all event listeners connected with history manager.
			 *
			 * Event listeners:
			 *
			 *  - popstate on window
			 *  - hashchange on window
			 *  - vclick on document
			 *
			 * After set event listeners method sets property enabled to true.
			 *
			 *	@example
			 *		tau.history.manager.enable();
			 *		// add event's listeners
			 *		// after click on link or hash change history manager will handle events
			 *
			 * @method enable
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.enable = function () {
				document.addEventListener(DOC_EVENT_VCLICK, onLinkAction, false);
				window.addEventListener(WINDOW_EVENT_POPSTATE, onPopState, false);
				window.addEventListener(WINDOW_EVENT_HASHCHANGE, onHashChange, false);
				history.enableVolatileMode();
				this.enabled = true;
				eventUtils.trigger(document, EVENT_ENABLED, this);
			};

			/**
			 * Disables history manager.
			 *
			 * This method removes all event listeners connected with history manager.
			 *
			 * After set event listeners method sets property enabled to true.
			 *
			 *	@example
			 *		tau.history.manager.disable();
			 *		// remove event's listeners
			 *		// after click on link or hash change history manager will not handle events
			 *
			 * @method disable
			 * @static
			 * @since 2.4
			 * @member ns.history.manager
			 */
			manager.disable = function () {
				document.removeEventListener(DOC_EVENT_VCLICK, onLinkAction, false);
				window.removeEventListener(WINDOW_EVENT_POPSTATE, onPopState, false);
				window.removeEventListener(WINDOW_EVENT_HASHCHANGE, onHashChange, false);
				history.disableVolatileMode();
				this.enabled = false;
				eventUtils.trigger(document, EVENT_DISABLED, this);
			};

			ns.history.manager = manager;
			}(window, window.document));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #Namespace For Core Widgets
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @class ns.widget.core
 */
(function (document, ns) {
	"use strict";
				ns.widget.core = ns.widget.core || {};
			}(window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * # Page Widget
 * Page is main element of application's structure.
 *
 * ## Default selectors
 * In the Tizen Web UI framework the application page structure is based on a header, content and footer elements:
 *
 * - **The header** is placed at the top, and displays the page title and optionally buttons.
 * - **The content** is the section below the header, showing the main content of the page.
 * - **The footer** is a bottom part of page which can display for example buttons
 *
 * The following table describes the specific information for each section.
 *
 * <table>
 *     <tr>
 *         <th>Section</th>
 *         <th>Class</th>
 *         <th>Mandatory</th>
 *         <th>Description</th>
 *     </tr>
 *     <tr>
 *         <td rowspan="2">Page</td>
 *         <td>ui-page</td>
 *         <td>Yes</td>
 *         <td>Defines the element as a page.
 *
 * The page widget is used to manage a single item in a page-based architecture.
 *
 * A page is composed of header (optional), content (mandatory), and footer (optional) elements.</td>
 *      </tr>
 *      <tr>
 *          <td>ui-page-active</td>
 *          <td>No</td>
 *          <td>If an application has a static start page, insert the ui-page-active class in the page element to
 *          speed up the application launch. The start page with the ui-page-active class can be displayed before
 *          the framework is fully loaded.
 *
 *          If this class is not used, the framework inserts the class automatically to the first page of the
 *          application.
 *
 *          However, this has a slowing effect on the application launch, because the page is displayed only after
 *          *the* framework is fully loaded.</td>
 *      </tr>
 *      <tr>
 *          <td>Header</td>
 *          <td>ui-header</td>
 *          <td>No</td>
 *          <td>Defines the element as a header.</td>
 *      </tr>
 *      <tr>
 *          <td>Content</td>
 *          <td>ui-content</td>
 *          <td>Yes</td>
 *          <td>Defines the element as content.</td>
 *      </tr>
 *      <tr>
 *          <td>Footer</td>
 *          <td>ui-footer</td>
 *          <td>No</td>
 *          <td>Defines the element as a footer.
 *
 * The footer section is mostly used to include option buttons.</td>
 *      </tr>
 *  </table>
 *
 * All elements with class=ui-page will be become page widgets
 *
 *      @example
 *         <!--Page layout-->
 *         <div class="ui-page ui-page-active">
 *             <header class="ui-header"></header>
 *             <div class="ui-content"></div>
 *             <footer class="ui-footer"></footer>
 *         </div>
 *
 *         <!--Page layout with more button in header-->
 *         <div class="ui-page ui-page-active">
 *             <header class="ui-header ui-has-more">
 *                 <h2 class="ui-title">Call menu</h2>
 *                 <button type="button" class="ui-more ui-icon-overflow">More Options</button>
 *             </header>
 *             <div class="ui-content">Content message</div>
 *             <footer class="ui-footer">
 *                 <button type="button" class="ui-btn">Footer Button</button>
 *             </footer>
 *         </div>
 *
 * ## Manual constructor
 * For manual creation of page widget you can use constructor of widget from **tau** namespace:
 *
 *        @example
 *        var pageElement = document.getElementById("page"),
 *            page = tau.widget.Page(buttonElement);
 *
 * Constructor has one require parameter **element** which are base **HTMLElement** to create widget.
 * We recommend get
 * this element by method *document.getElementById*.
 *
 * ## Multi-page Layout
 *
 * You can implement a template containing multiple page containers in the application index.html file.
 *
 * In the multi-page layout, the main page is defined with the ui-page-active class.
 * If no page has the ui-page-active
 * class, the framework automatically sets up the first page in the source order
 * as the main page. You can improve the
 * launch performance by explicitly defining the main page to be displayed first.
 * If the application has to wait for
 * the framework to set up the main page, the page is displayed with some delay
 * only after the framework is fully
 * loaded.
 *
 * You can link to internal pages by referring to the ID of the page. For example, to link to the page with an ID
 * of
 * two, the link element needs the href="#two" attribute in the code, as in the following example.
 *
 *      @example
 *         <!--Main page-->
 *         <div id="one" class="ui-page ui-page-active">
 *             <header class="ui-header"></header>
 *             <div class="ui-content"></div>
 *             <footer class="ui-footer"></footer>
 *         </div>
 *
 *         <!--Secondary page-->
 *         <div id="two" class="ui-page">
 *             <header class="ui-header"></header>
 *             <div class="ui-content"></div>
 *             <footer class="ui-footer"></footer>
 *         </div>
 *
 * To find the currently active page, use the ui-page-active class.
 *
 * ## Changing Pages
 * ### Go to page in JavaScript
 * To change page use method *tau.changePage*
 *
 *      @example
 *      tau.changePage("page-two");
 *
 * ### Back in JavaScript
 * To back to previous page use method *tau.back*
 *
 *      @example
 *      tau.back();
 *
 * ## Transitions
 *
 * When changing the active page, you can use a page transition.
 *
 * Tizen Web UI Framework does not apply transitions by default. To set a custom transition effect,
 * you must add the
 * data-transition attribute to a link:
 *
 *      @example
 *      <a href="index.html" data-transition="slideup">I\'ll slide up</a>
 *
 * To set a default custom transition effect for all pages, use the pageTransition property:
 *
 *      @example
 *      tau.defaults.pageTransition = "slideup";
 *
 * ### Transitions list
 *
 *  - **none** no transition.
 *  - **slideup** Makes the content of the next page slide up, appearing to conceal the content of the previous page.
 *
 * ## Handling Page Events
 *
 * With page widget we have connected many of events.
 *
 * To handle page events, use the following code:
 *
 *      @example
 *        <div id="page" class="ui-page">
 *             <header class="ui-header"></header>
 *             <div class="ui-content"></div>
 *         </div>
 *
 *         <script>
 *             var page = document.getElementById("page");
 *             page.addEventListener("Event", function(event) {
 *                 // Your code
 *             });
 *         </script>
 *
 * To bind an event callback on the Back key, use the following code:
 *
 * Full list of available events is in [events list section](#events-list).
 *
 * To bind an event callback on the Back key, use the following code:
 *
 *      @example
 *         <script>
 *             window.addEventListener("tizenhwkey", function (event) {
 *                 if (event.keyName == "back") {
 *                     // Call window.history.back() to go to previous browser window
 *                     // Call tizen.application.getCurrentApplication().exit() to exit application
 *                     // Add script to add another behavior
 *                 }
 *             });
 *         </script>
 *
 *
 * ## Methods
 *
 * To call method on widget you can use tau API:
 *
 *        @example
 *        var pageElement = document.getElementById("page"),
 *            page = tau.widget.Page(buttonElement);
 *
 *        page.methodName(methodArgument1, methodArgument2, ...);
 *
 * @class ns.widget.core.Page
 * @extends ns.widget.BaseWidget
 * @component-selector .ui-page
 * @component-type container-component
 * @component-constraint 'popup', 'drawer', 'header', 'bottom-button'
 * @component-attachable false
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Damian Osipiuk <d.osipiuk@samsung.com>
 */
(function (document, ns) {
	"use strict";
				/**
			 * Alias for {@link ns.widget.BaseWidget}
			 * @property {Object} BaseWidget
			 * @member ns.widget.core.Page
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.BaseWidget,
				/**
				 * Alias for {@link ns.util}
				 * @property {Object} util
				 * @member ns.widget.core.Page
				 * @private
				 * @static
				 */
				util = ns.util,
				utilsDOM = util.DOM,
				/**
				 * Alias for {@link ns.util.selectors}
				 * @property {Object} utilSelectors
				 * @member ns.widget.core.Page
				 * @private
				 * @static
				 */
				utilSelectors = util.selectors,
				/**
				 * Alias for {@link ns.engine}
				 * @property {Object} engine
				 * @member ns.widget.core.Page
				 * @private
				 * @static
				 */
				engine = ns.engine,
				arrayUtil = ns.util.array,

				Page = function () {
					var self = this;
					/**
					 * Callback on resize
					 * @property {?Function} _contentFillAfterResizeCallback
					 * @private
					 * @member ns.widget.core.Page
					 */

					self._contentFillAfterResizeCallback = null;
					self._initialContentStyle = {};
					/**
					 * Options for widget.
					 * It is empty object, because widget Page does not have any options.
					 * @property {Object} options
					 * @member ns.widget.core.Page
					 */
					self.options = {};

					self._contentStyleAttributes = ["height", "width", "minHeight", "marginTop", "marginBottom"];

					self._ui = {};
				},
				/**
				 * Dictionary for page related event types
				 * @property {Object} EventType
				 * @member ns.widget.core.Page
				 * @static
				 */
				EventType = {
					/**
					 * Triggered on the page we are transitioning to,
					 * after the transition animation has completed.
					 * @event pageshow
					 * @member ns.widget.core.Page
					 */
					SHOW: "pageshow",
					/**
					 * Triggered on the page we are transitioning away from,
					 * after the transition animation has completed.
					 * @event pagehide
					 * @member ns.widget.core.Page
					 */
					HIDE: "pagehide",
					/**
					 * Triggered when the page has been created in the DOM
					 * (for example, through Ajax) but before all widgets
					 * have had an opportunity to enhance the contained markup.
					 * @event pagecreate
					 * @member ns.widget.core.Page
					 */
					CREATE: "pagecreate",
					/**
					 * Triggered when the page is being initialized,
					 * before most plugin auto-initialization occurs.
					 * @event pagebeforecreate
					 * @member ns.widget.core.Page
					 */
					BEFORE_CREATE: "pagebeforecreate",
					/**
					 * Triggered on the page we are transitioning to,
					 * before the actual transition animation is kicked off.
					 * @event pagebeforeshow
					 * @member ns.widget.core.Page
					 */
					BEFORE_SHOW: "pagebeforeshow",
					/**
					 * Triggered on the page we are transitioning away from,
					 * before the actual transition animation is kicked off.
					 * @event pagebeforehide
					 * @member ns.widget.core.Page
					 */
					BEFORE_HIDE: "pagebeforehide"
				},
				/**
				 * Dictionary for page related css class names
				 * @property {Object} classes
				 * @member ns.widget.core.Page
				 * @static
				 * @readonly
				 */
				classes = {
					uiPage: "ui-page",
					/**
					 * Indicates active page
					 * @style ui-page-active
					 * @member ns.widget.core.Page
					 */
					uiPageActive: "ui-page-active",
					uiSection: "ui-section",
					uiHeader: "ui-header",
					uiMore: "ui-more",
					uiHeaderOnlyMoreButton: "ui-header-has-only-more-button",
					uiFooter: "ui-footer",
					uiContent: "ui-content",
					uiTitle: "ui-title",
					uiPageScroll: "ui-scroll-on",
					uiScroller: "ui-scroller",
					uiContentUnderPopup: "ui-content-under-popup"
				},
				HEADER_SELECTOR = "header,[data-role='header'],." + classes.uiHeader,
				FOOTER_SELECTOR = "footer,[data-role='footer'],." + classes.uiFooter,
				//ui-indexscrollbar is needed as widget ads html markup at the
				//same level as content, other wise page content is build on
				//indexscrollbar element
				CONTENT_SELECTOR = "[data-role='content'],." + classes.uiContent,
				ONLY_CHILD_MORE_BUTTON_SELECTOR = "." + classes.uiMore + ":first-child:last-child",
				prototype = new BaseWidget();

			Page.classes = classes;
			Page.events = EventType;

			/**
			 * Configure default options for widget
			 * @method _configure
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._configure = function () {
				var options = this.options;
				/**
				 * Object with default options
				 * @property {Object} options
				 * @property {boolean|string|null} [options.header=false] Sets content of header.
				 * @property {boolean|string|null} [options.footer=false] Sets content of footer.
				 * @property {boolean} [options.autoBuildWidgets=false] Automatically build widgets inside page.
				 * @property {string} [options.content=null] Sets content of popup.
				 * @member ns.widget.core.Page
				 * @static
				 */

				options.header = null;
				options.footer = null;
				options.content = null;
				options.enablePageScroll = ns.getConfig("enablePageScroll");
				options.autoBuildWidgets = ns.getConfig("autoBuildOnPageChange");
				this.options = options;
			};

			/**
			 * Setup size of element to 100% of screen
			 * @method _contentFill
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._contentFill = function () {
				var self = this,
					element = self.element,
					screenWidth = window.innerWidth,
					screenHeight = window.innerHeight,
					elementStyle = element.style,
					ui = self._ui,
					content = ui.content,
					contentStyle,
					header = ui.header,
					top = 0,
					bottom = 0,
					footer = ui.footer;

				elementStyle.width = screenWidth + "px";
				elementStyle.height = screenHeight + "px";

				if (content && !element.classList.contains("ui-page-flex")) {
					contentStyle = content.style;
					
					if (header) {
						top = utilsDOM.getElementHeight(header);
					}

					if (footer) {
						bottom = footer.getBoundingClientRect().height;
						contentStyle.marginBottom = bottom + "px";
						contentStyle.paddingBottom = (-bottom) + "px";
					}

					if (!self.options.enablePageScroll) {
						contentStyle.height = (screenHeight - top - bottom) + "px";
					}
				}
			};

			prototype._storeContentStyle = function () {
				var self = this,
					initialContentStyle = self._initialContentStyle,
					contentStyleAttributes = self._contentStyleAttributes,
					content = self.element.querySelector("." + classes.uiContent),
					contentStyle = content ? content.style : {};

				contentStyleAttributes.forEach(function (name) {
					initialContentStyle[name] = contentStyle[name];
				});
			};

			/**
			 * Restore saved styles for content.
			 * Called on refresh or hide.
			 * @protected
			 */
			prototype._restoreContentStyle = function () {
				var self = this,
					initialContentStyle = self._initialContentStyle,
					contentStyleAttributes = self._contentStyleAttributes,
					content = self.element.querySelector("." + classes.uiContent),
					contentStyle = content ? content.style : {};

				contentStyleAttributes.forEach(function (name) {
					contentStyle[name] = initialContentStyle[name];
				});
			};

			/**
			 * Setter for footer option
			 * @method _setFooter
			 * @param {HTMLElement} element
			 * @param {string} value
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._setFooter = function (element, value) {
				var self = this,
					ui = self._ui,
					footer = ui.footer;

				// footer element if footer does not exist and value is true or string
				if (!footer && value) {
					footer = document.createElement("footer");
					element.appendChild(footer);
					ui.footer = footer;
				}
				if (footer) {
					// remove child if footer does not exist and value is set to false
					if (value === false) {
						element.removeChild(footer);
						ui.footer = null;
					} else {
						// if options is set to true, to string or not is set
						// add class
						footer.classList.add(classes.uiFooter);
						// if is string fill content by string value
						if (typeof value === "string") {
							ui.footer.textContent = value;
						}
					}
					// and remember options
					self.options.footer = value;
				}
			};

			/**
			 * Setter for header option
			 * @method _setHeader
			 * @param {HTMLElement} element
			 * @param {string} value
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._setHeader = function (element, value) {
				var self = this,
					ui = self._ui,
					header = ui.header;

				// header element if header does not exist and value is true or string
				if (!header && value) {
					header = document.createElement("header");
					element.appendChild(header);
					ui.header = header;
				}
				if (header) {
					// remove child if header does not exist and value is set to false
					if (value === false) {
						element.removeChild(header);
						ui.header = null;
					} else {
						// if options is set to true, to string or not is set
						// add class
						header.classList.add(classes.uiHeader);
						// if is string fill content by string value
						if (typeof value === "string") {
							ui.header.textContent = value;
						}

						if (ns.support && ns.support.shape && ns.support.shape.circle) {
							// patch for backward compability - if header has only more button
							// (it was common for rectangle devices) header should be marked
							// and take no place at all.
							if (header.querySelector(ONLY_CHILD_MORE_BUTTON_SELECTOR) && header.textContent.trim() === "") {
								header.classList.add(classes.uiHeaderOnlyMoreButton);
							}
						}
					}
					// and remember options
					self.options.header = value;
				}
			};

			/**
			 * Setter for content option
			 * @method _setContent
			 * @param {HTMLElement} element
			 * @param {string} value
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._setContent = function (element, value) {
				var self = this,
					ui = self._ui,
					content = ui.content,
					child = element.firstChild,
					next;

				if (!content && value) {
					content = document.createElement("div");
					while (child) {
						next = child.nextSibling;
						if (child !== ui.footer && child !== ui.header) {
							content.appendChild(child);
						}
						child = next;
					}
					element.insertBefore(content, ui.footer);
					ui.content = content;
				}
				if (content) {
					// remove child if content exist and value is set to false
					if (value === false) {
						element.removeChild(content);
						ui.content = null;
					} else {
						// if options is set to true, to string or not is set
						// add class
						content.classList.add(classes.uiContent);
						// if is string fill content by string value
						if (typeof value === "string") {
							content.textContent = value;
						}
					}
					// and remember options
					self.options.content = value;
				}
			};

			/**
			 * Method creates empty page header. It also checks for additional
			 * content to be added in header.
			 * @method _buildHeader
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._buildHeader = function (element) {
				var self = this;

				self._ui.header = utilSelectors.getChildrenBySelector(element, HEADER_SELECTOR)[0] || null;
				if (self.options.header === undefined) {
					self.options.header = !!self._ui.header;
				}
				self._setHeader(element, self.options.header);
			};

			/**
			 * Method creates empty page footer.
			 * @method _buildFooter
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._buildFooter = function (element) {
				var self = this;

				self._ui.footer = utilSelectors.getChildrenBySelector(element, FOOTER_SELECTOR)[0] || null;
				if (self.options.footer === undefined) {
					self.options.footer = !!self._ui.footer;
				}
				self._setFooter(element, self.options.footer);
			};

			/**
			 * Method creates empty page content.
			 * @method _buildContent
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._buildContent = function (element) {
				var self = this;

				self._ui.content = utilSelectors.getChildrenBySelector(element, CONTENT_SELECTOR)[0] || null;
				if (self.options.content === undefined) {
					self.options.content = !!self._ui.content;
				}
				self._setContent(element, self.options.content);
			};


			/**
			 * Set ARIA attributes on page structure
			 * @method _setAria
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._setAria = function () {
				var self = this,
					ui = self._ui,
					content = ui.content,
					header = ui.header,
					footer = ui.footer,
					title = ui.title;

				if (content) {
					content.setAttribute("role", "main");
				}

				if (header) {
					header.setAttribute("role", "header");
				}

				if (footer) {
					footer.setAttribute("role", "footer");
				}

				if (title) {
					title.setAttribute("role", "heading");
					title.setAttribute("aria-level", 1);
					title.setAttribute("aria-label", "title");
				}
			};

			/**
			 * Find title of page
			 * @param {HTMLElement} element
			 * @method _setTitle
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._setTitle = function (element) {
				var self = this,
					dataPageTitle = utilsDOM.getNSData(element, "title"),
					header = self._ui.header,
					pageTitle = dataPageTitle,
					titleElements,
					mainTitleElement;

				if (header) {
					titleElements = utilSelectors.getChildrenBySelector(header, "h1, h2, h3, h4, h5, h6");

					mainTitleElement = titleElements[0];

					if (!pageTitle && mainTitleElement) {
						pageTitle = mainTitleElement.innerText;
						self._ui.title = mainTitleElement;
					}

					if (!dataPageTitle && pageTitle) {
						utilsDOM.setNSData(element, "title", pageTitle);
					}

					arrayUtil.forEach(titleElements, function (titleElement) {
						titleElement.classList.add(classes.uiTitle)
					});
				}
			};

			/**
			 * Build page
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._build = function (element) {
				var self = this;

				element.classList.add(classes.uiPage);
				self._buildHeader(element);
				self._buildFooter(element);
				self._buildContent(element);
				self._setTitle(element);
				self._setAria();

				//it means that we are in wearable profile and we want to make a scrollview on page element (not content)
				if (self.options.enablePageScroll === true && !element.querySelector("." + classes.uiScroller)) {
					engine.instanceWidget(element, "Scrollview");
				}
				return element;
			};


			/**
			 * This method sets page active or inactive.
			 *
			 *    @example
			 *    <div id="myPage"></div>
			 *    <script type="text/javascript">
			 *      var page = tau.widget.Page(document.getElementById("myPage"));
			 *      page.setActive(true);
			 *    </script>
			 *
			 * @method setActive
			 * @param {boolean} [value=true] If true, then page will be active. Otherwise, page will be inactive.
			 * @member ns.widget.core.Page
			 */
			prototype.setActive = function (value) {
				var elementClassList = this.element.classList;

				if (value || value === undefined) {
					this.focus();
					elementClassList.add(classes.uiPageActive);
				} else {
					this.blur();
					elementClassList.remove(classes.uiPageActive);
				}
			};

			/**
			 * Return current status of page.
			 * @method isActive
			 * @member ns.widget.core.Page
			 * @instance
			 */
			prototype.isActive = function () {
				return this.element.classList.contains(classes.uiPageActive);
			};

			/**
			 * Sets the focus to page
			 * @method focus
			 * @member ns.widget.core.Page
			 */
			prototype.focus = function () {
				var element = this.element,
					focusable = element.querySelector("[autofocus]") || element;

				focusable.focus();
			};

			/**
			 * Removes focus from page and all descendants
			 * @method blur
			 * @member ns.widget.core.Page
			 */
			prototype.blur = function () {
				var element = this.element,
					focusable = document.activeElement || element;

				focusable.blur();
			};

			/**
			 * Bind events to widget
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._bindEvents = function () {
				var self = this;

				self._contentFillAfterResizeCallback = self._contentFill.bind(self);
				window.addEventListener("resize", self._contentFillAfterResizeCallback, false);
			};

			/**
			 * Refresh widget structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._refresh = function () {
				this._restoreContentStyle();
				this._contentFill();
			};

			/**
			 * Layouting page structure
			 * @method layout
			 * @internal
			 * @member ns.widget.core.Page
			 */
			prototype.layout = function () {
				this._storeContentStyle();
				this._contentFill();
			};

			/**
			 * This method triggers BEFORE_SHOW event.
			 * @method onBeforeShow
			 * @internal
			 * @member ns.widget.core.Page
			 */
			prototype.onBeforeShow = function () {
				this.trigger(EventType.BEFORE_SHOW);
			};

			/**
			 * This method triggers SHOW event.
			 * @method onShow
			 * @internal
			 * @member ns.widget.core.Page
			 */
			prototype.onShow = function () {
								this.trigger(EventType.SHOW);
			};

			/**
			 * This method triggers BEFORE_HIDE event.
			 * @method onBeforeHide
			 * @internal
			 * @member ns.widget.core.Page
			 */
			prototype.onBeforeHide = function () {
				this.trigger(EventType.BEFORE_HIDE);
			};

			/**
			 * This method triggers HIDE event.
			 * @method onHide
			 * @internal
			 * @member ns.widget.core.Page
			 */
			prototype.onHide = function () {
				this._restoreContentStyle();
				this.trigger(EventType.HIDE);
			};

			/**
			 * Destroy widget
			 * @method _destroy
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Page
			 */
			prototype._destroy = function (element) {
				var self = this;

				element = element || self.element;
				
				window.removeEventListener("resize", self._contentFillAfterResizeCallback, false);
				// destroy widgets on children
				engine.destroyAllWidgets(element, true);

				self._contentFillAfterResizeCallback = null;
			};

			/**
			 * Return scroller
			 * @method getScroller
			 * @member ns.widget.core.Page
			 */
			prototype.getScroller = function () {
				var element = this.element,
					scroller = element.querySelector("." + classes.uiScroller);

				return scroller || element.querySelector("." + classes.uiContent) || element;
			};

			Page.prototype = prototype;

			Page.createEmptyElement = function () {
				var div = document.createElement("div");

				div.classList.add(classes.uiPage);
				return div;
			};

			engine.defineWidget(
				"Page",
				"[data-role=page],.ui-page",
				[
					"focus",
					"blur",
					"setActive"
				],
				Page,
				// for register in jQuery Mobile space
				"mobile"
			);

			ns.widget.core.Page = Page;
			}(window.document, ns));
/**
 * #Footer
 *
 * ## Creating footer
 *
 *    @example template
 *      <footer class="ui-footer"></footer>
 *
 * @class ns.widget.core.Footer
 * @component-selector .ui-page > footer, .ui-page .ui-footer
 * @component-type layout-component
 * @extends ns.widget.BaseWidget
 *
 */

/**
 * #Header
 *
 *    @example template
 *      <header class="ui-header"><h2 class="ui-title">Header</h2></header>
 *
 * @class ns.widget.core.Header
 * @component-selector .ui-page > header, .ui-page > .ui-header
 * @component-type layout-component
 * @extends ns.widget.BaseWidget
 */
/**
 * Button for menu with icon in header
 * @style ui-more
 * @selector .ui-btn
 * @member ns.widget.core.Header
 * @wearable
 * @since 2.3.1
 */
/**
 * Icon style for menu button
 * @style ui-icon-detail
 * @selector .ui-btn.ui-more
 * @member ns.widget.core.Header
 * @wearable
 * @since 2.3.1
 */
/**
 * Icon style for menu button
 * @style ui-icon-overflow
 * @selector .ui-btn.ui-more
 * @member ns.widget.core.Header
 * @wearable
 * @since 2.3.1
 */
/**
 * Icon style for menu button
 * @style ui-icon-selectall
 * @selector .ui-btn.ui-more
 * @member ns.widget.core.Header
 * @wearable
 * @since 2.3.1
 */

/**
 * #Content
 *
 *    @example template
 *      <div class="ui-content"></div>
 *
 * @class ns.widget.core.Content
 * @component-selector .ui-content
 * @component-type container-component
 * @component-constraint 'bottom-button', 'checkbox', 'listview', 'processing', 'closet-tau-circle-progress', 'radio', 'toggleswitch', 'text', 'closet-image', 'sectionchanger'
 * @extends ns.widget.BaseWidget
 */
/**
 * Defines the buttons inside column width as 100% of the screen
 * @style ui-grid-col-1
 * @selector div:not(.ui-grid-col-1):not(.ui-grid-col-2):not(.ui-grid-col-3):not(.ui-grid-row)
 * @member ns.widget.core.Content
 * @wearable
 * @since 2.3.1
 */
/**
 * Defines the buttons inside column width as 50% of the screen
 * @style ui-grid-col-2
 * @selector div:not(.ui-grid-col-1):not(.ui-grid-col-2):not(.ui-grid-col-3):not(.ui-grid-row)
 * @member ns.widget.core.Content
 * @wearable
 * @since 2.3.1
 */
/**
 * Defines the buttons inside column width as 50% of the screen
 * @style ui-grid-col-3
 * @selector div:not(.ui-grid-col-1):not(.ui-grid-col-2):not(.ui-grid-col-3):not(.ui-grid-row)
 * @member ns.widget.core.Content
 * @wearable
 * @since 2.3.1
 */
/**
 * Arranges the buttons inside in a row
 * @style ui-grid-row
 * @selector div:not(.ui-grid-col-1):not(.ui-grid-col-2):not(.ui-grid-col-3):not(.ui-grid-row)
 * @member ns.widget.core.Content
 * @wearable
 * @since 2.3.1
 */
;
/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # PageContainer Widget
 * PageContainer is a widget, which is supposed to have multiple child pages but display only one at a time.
 *
 * @class ns.widget.core.PageContainer
 * @extends ns.widget.BaseWidget
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Krzysztof Głodowski <k.glodowski@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				util = ns.util,
				DOM = util.DOM,
				engine = ns.engine,
				classes = {
					pageContainer: "ui-page-container",
					uiViewportTransitioning: "ui-viewport-transitioning",
					out: "out",
					in: "in",
					reverse: "reverse",
					uiPreIn: "ui-pre-in",
					uiBuild: "ui-page-build"
				},
				PageContainer = function () {
					/**
					 * Active page.
					 * @property {ns.widget.core.Page} [activePage]
					 * @member ns.widget.core.PageContainer
					 */
					this.activePage = null;
					this.inTransition = false;
				},
				EventType = {
					/**
					 * Triggered before the changePage() request
					 * has started loading the page into the DOM.
					 * @event pagebeforechange
					 * @member ns.widget.core.PageContainer
					 */
					PAGE_BEFORE_CHANGE: "pagebeforechange",
					/**
					 * Triggered after the changePage() request
					 * has finished loading the page into the DOM and
					 * all page transition animations have completed.
					 * @event pagechange
					 * @member ns.widget.core.PageContainer
					 */
					PAGE_CHANGE: "pagechange",
					PAGE_REMOVE: "pageremove"
				},
				animationend = "animationend",
				webkitAnimationEnd = "webkitAnimationEnd",
				mozAnimationEnd = "mozAnimationEnd",
				msAnimationEnd = "msAnimationEnd",
				oAnimationEnd = "oAnimationEnd",
				animationEndNames = [
					animationend,
					webkitAnimationEnd,
					mozAnimationEnd,
					msAnimationEnd,
					oAnimationEnd
				],
				prototype = new BaseWidget();
			//When resolved deferred function is responsible for triggering events related to page change as well as
			//destroying unused widgets from last page and/or removing last page

			function deferredFunction(fromPageWidget, toPageWidget, self, options) {
				if (fromPageWidget) {
					fromPageWidget.onHide();
					self._removeExternalPage(fromPageWidget, options);
				}
				toPageWidget.onShow();
								self.trigger(EventType.PAGE_CHANGE);
							}

			/**
			 * Dictionary for PageContainer related event types.
			 * @property {Object} events
			 * @property {string} [events.PAGE_CHANGE="pagechange"]
			 * @member ns.router.route.popup
			 * @static
			 */
			PageContainer.events = EventType;

			/**
			 * Dictionary for PageContainer related css class names
			 * @property {Object} classes
			 * @member ns.widget.core.Page
			 * @static
			 * @readonly
			 */
			PageContainer.classes = classes;

			/**
			 * Build widget structure
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._build = function (element) {
				element.classList.add(classes.pageContainer);
				return element;
			};

			/**
			 * This method changes active page to specified element.
			 * @method change
			 * @param {HTMLElement} toPageElement The element to set
			 * @param {Object} [options] Additional options for the transition
			 * @param {string} [options.transition=none] Specifies the type of transition
			 * @param {boolean} [options.reverse=false] Specifies the direction of transition
			 * @member ns.widget.core.PageContainer
			 */
			prototype.change = function (toPageElement, options) {
				var self = this,
					fromPageWidget = self.getActivePage(),
					toPageWidget,
					calculatedOptions = options || {};

				// store options to detect that option was changed before process finish
				self._options = calculatedOptions;

				calculatedOptions.widget = calculatedOptions.widget || "Page";

				// The change should be made only if no active page exists
				// or active page is changed to another one.
				if (!fromPageWidget || (fromPageWidget.element !== toPageElement)) {
					if (toPageElement.parentNode !== self.element) {
						toPageElement = self._include(toPageElement);
					}

					self.trigger(EventType.PAGE_BEFORE_CHANGE);

					toPageElement.classList.add(classes.uiBuild);

					toPageWidget = engine.instanceWidget(toPageElement, calculatedOptions.widget);

					// set sizes of page for correct display
					toPageWidget.layout();

					if (toPageWidget.option("autoBuildWidgets")) {
						engine.createWidgets(toPageElement);
					}

					if (fromPageWidget) {
						fromPageWidget.onBeforeHide();
					}
					toPageWidget.onBeforeShow();

					toPageElement.classList.remove(classes.uiBuild);

					// if options is different that this mean that another change page was called and we need stop
					// previous change page
					if (calculatedOptions === self._options) {
						calculatedOptions.deferred = {
							resolve: deferredFunction
						};
						self._transition(toPageWidget, fromPageWidget, calculatedOptions);
					}
				}
			};

			/**
			 * This method performs transition between the old and a new page.
			 * @method _transition
			 * @param {ns.widget.core.Page} toPageWidget The new page
			 * @param {ns.widget.core.Page} fromPageWidget The page to be replaced
			 * @param {Object} [options] Additional options for the transition
			 * @param {string} [options.transition=none] The type of transition
			 * @param {boolean} [options.reverse=false] Specifies transition direction
			 * @param {Object} [options.deferred] Deferred object
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._transition = function (toPageWidget, fromPageWidget, options) {
				var self = this,
					element = self.element,
					elementClassList = element.classList,
					transition = !fromPageWidget || !options.transition ? "none" : options.transition,
					deferred = options.deferred,
					clearClasses = [classes.in, classes.out, classes.uiPreIn, transition],
					oldDeferredResolve,
					oneEvent;

				if (options.reverse) {
					clearClasses.push(classes.reverse);
				}
				self.inTransition = true;
				elementClassList.add(classes.uiViewportTransitioning);
				oldDeferredResolve = deferred.resolve;
				deferred.resolve = function () {
					var fromPageWidgetClassList = fromPageWidget && fromPageWidget.element.classList,
						toPageWidgetClassList = toPageWidget.element.classList;

					self._setActivePage(toPageWidget);
					self._clearTransitionClasses(clearClasses, fromPageWidgetClassList, toPageWidgetClassList);
					oldDeferredResolve(fromPageWidget, toPageWidget, self, options);
				};

				if (transition !== "none") {
					oneEvent = function () {
						toPageWidget.off(
							animationEndNames,
							oneEvent,
							false
						);
						deferred.resolve();
					};
					toPageWidget.on(
						animationEndNames,
						oneEvent,
						false
					);
					self._appendTransitionClasses(fromPageWidget, toPageWidget, transition, options.reverse);
				} else {
					window.setTimeout(deferred.resolve, 0);
				}
			};

			/**
			 * This method adds proper transition classes to specified page widgets.
			 * @param {ns.widget.core.Page} fromPageWidget Page widget from which transition will occur
			 * @param {ns.widget.core.Page} toPageWidget Destination page widget for transition
			 * @param {string} transition Specifies the type of transition
			 * @param {boolean} reverse Specifies the direction of transition
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._appendTransitionClasses = function (fromPageWidget, toPageWidget, transition, reverse) {
				var classList;

				if (fromPageWidget) {
					classList = fromPageWidget.element.classList;
					classList.add(transition, classes.out);
					if (reverse) {
						classList.add(classes.reverse);
					}
				}

				classList = toPageWidget.element.classList;
				classList.add(transition, classes.in, classes.uiPreIn);
				if (reverse) {
					classList.add(classes.reverse);
				}
			};

			/**
			 * This method removes transition classes from classLists of page widget elements.
			 * @param {Object} clearClasses An array containing classes to be removed
			 * @param {Object} fromPageWidgetClassList classList object from source page element
			 * @param {Object} toPageWidgetClassList classList object from destination page element
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._clearTransitionClasses = function (clearClasses, fromPageWidgetClassList, toPageWidgetClassList) {
				var self = this,
					element = self.element,
					elementClassList = element.classList;

				elementClassList.remove(classes.uiViewportTransitioning);
				self.inTransition = false;
				clearClasses.forEach(function (className) {
					toPageWidgetClassList.remove(className);
				});
				if (fromPageWidgetClassList) {
					clearClasses.forEach(function (className) {
						fromPageWidgetClassList.remove(className);
					});
				}
			};

			/**
			 * This method adds an element as a page.
			 * @method _include
			 * @param {HTMLElement} page an element to add
			 * @return {HTMLElement}
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._include = function (page) {
				var element = this.element;

				if (!page.parentNode || page.ownerDocument !== document) {
					page = util.importEvaluateAndAppendElement(page, element);
				}
				return page;
			};

			/**
			 * This method sets currently active page.
			 * @method _setActivePage
			 * @param {ns.widget.core.Page} page a widget to set as the active page
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._setActivePage = function (page) {
				var self = this;

				if (self.activePage) {
					self.activePage.setActive(false);
				}

				self.activePage = page;

				page.setActive(true);
			};

			/**
			 * This method returns active page widget.
			 * @method getActivePage
			 * @member ns.widget.core.PageContainer
			 * @return {ns.widget.core.Page} Currently active page
			 */
			prototype.getActivePage = function () {
				return this.activePage;
			};

			/**
			 * This method removes page element from the given widget and destroys it.
			 * @method _removeExternalPage
			 * @param {ns.widget.core.Page} fromPageWidget the widget to destroy
			 * @param {Object} [options] transition options
			 * @param {boolean} [options.reverse=false] specifies transition direction
			 * @member ns.widget.core.PageContainer
			 * @protected
			 */
			prototype._removeExternalPage = function (fromPageWidget, options) {
				var fromPageElement = fromPageWidget.element;

				if (options && options.reverse && DOM.hasNSData(fromPageElement, "external") &&
					fromPageElement.parentNode) {
					fromPageWidget.destroy();
					fromPageElement.parentNode.removeChild(fromPageElement);
					this.trigger(EventType.PAGE_REMOVE);
				}
			};

			PageContainer.prototype = prototype;

			// definition
			ns.widget.core.PageContainer = PageContainer;

			engine.defineWidget(
				"pagecontainer",
				"",
				["change", "getActivePage"],
				PageContainer,
				"core"
			);
			}(window.document, ns));

/*global define, ns */
/*jslint nomen: true, plusplus: true, bitwise: false */
/*
 * Copyright (c) 2010 - 2014 Samsung Electronics Co., Ltd.
 * License : MIT License V2
 */
/**
 * #Template Manager
 *
 * Object menage template's engines and renderer HTMLElement by template engine.
 *
 * @class ns.template
 * @since 2.4
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function () {
	"use strict";
				var utilPath = ns.util.path,
				template,
				templateFunctions = {},
				globalOptions = {
					"pathPrefix": "",
					"default": ""
				};

			/**
			 * Function to get global option
			 *
			 *	@example
			 *		tau.template.get("pathPrefix");
			 *		// -> "/prefix/to/all/paths"
			 *
			 * @method get
			 * @param {string} name param name which will be return
			 * @return {*} return value of option
			 * @since 2.4
			 * @member ns.template
			 */
			function get(name) {
				return globalOptions[name];
			}

			/**
			 * Function to set global option
			 *
			 *	@example
			 *		tau.template.set("pathPrefix", "/views");
			 *
			 * @method set
			 * @param {string} name param name which will be set
			 * @param {*} value value to set
			 * @since 2.4
			 * @member ns.template
			 */
			function set(name, value) {
				globalOptions[name] = value;
			}

			/**
			 * Register new template function
			 *
			 * Template function should have 4 arguments:
			 *
			 *  - globalOptions - global options of template engine
			 *  - path - path or id of template content
			 *  - data - data for template render
			 *  - callback - callback call on finish
			 *
			 * and should call callback on finish with arguments:
			 *
			 *  - status - object describing status of render
			 *  - element - base HTMLElement of template results (on error can be null)
			 *
			 * after registration you can use engine in render function.
			 *
			 *	@example
			 *		tau.template.register("inline", function(globalOptions, path, data, callback) {
			 *			callback({
			 *						success: true
			 *					},
			 *					document.createElement("div")
			 *				);
			 *		});
			 *
			 * @method register
			 * @param {string} name Engine name
			 * @param {Function} templateFunction function to renderer template
			 * @since 2.4
			 * @member ns.template
			 */
			function register(name, templateFunction) {
				templateFunctions[name] = templateFunction;
			}

			/**
			 * Unregister template function
			 *
			 * @method unregister
			 * @param {string} name Engine name
			 * @since 2.4
			 * @member ns.template
			 */
			function unregister(name) {
				templateFunctions[name] = null;
			}

			/**
			 * Return engine with given name
			 *
			 * @method engine
			 * @param {string} name Engine name
			 * @since 2.4
			 * @member ns.template
			 */
			function engine(name) {
				return templateFunctions[name];
			}

			/**
			 * Create absolute path for given path.
			 * If parameter withProfile is true, the returned path will have name of profile
			 * separated by dots before the last dot.
			 * @method getAbsUrl
			 * @param {string} path
			 * @param {boolean} withProfile Create path with profile's name
			 * @return {string} changed path
			 * @since 2.4
			 */
			function getAbsUrl(path, withProfile) {
				var profile = ns.info.profile,
					lastDot = path.lastIndexOf(".");

				if (utilPath.isAbsoluteUrl(path)) {
					return path;
				}

				if (withProfile) {
					path = path.substring(0, lastDot) + "." + profile + path.substring(lastDot);
				}

				return utilPath.makeUrlAbsolute((globalOptions.pathPrefix || "") + path, utilPath.getLocation());
			}

			/**
			 * Return HTMLElement for given path
			 *
			 * When engine name is not given then get default name from global options. If this is not set then get first registered engine.
			 *
			 * Result of this method is handed to callback. First parameter of callback is object with status. Second is HTMLElement generated by engine.
			 *
			 * Status object contains properties:
			 *
			 *  - _boolean_ success - inform about success or error
			 *  - _string_ description contains details on error
			 *
			 *	@example
			 *		tau.template.render("external/path/to/file.html", {additionalParameter: true}, function(status, element) {
			 *			if (status.success) {
			 *				document.body.appendChild(element);
			 *			} else {
			 *				console.error(status.description);
			 *			}, "html");
			 *
			 * @method render
			 * @param {string} path Path to file ot other id for template system
			 * @param {Object} data additional data for template system
			 * @param {Function} callback function which will be called on finish
			 * @param {string} [engineName] engine name
			 * @since 2.4
			 * @member ns.template
			 */
			function render(path, data, callback, engineName) {
				var templateFunction = templateFunctions[engineName || get("default") || ""],
					targetCallback = function (status, element) {
						// add current patch
						status.absUrl = targetPath;
						callback(status, element);
					},
					templateCallback = function (status, element) {
						if (status.success) {
							// path was found and callback can be called
							targetCallback(status, element);
						} else {
							// try one more time with path without profile
							targetPath = getAbsUrl(path, false);
							templateFunction(globalOptions, targetPath, data || {}, targetCallback);
						}
					},
					targetPath;

				// if template engine name and default name is not given then we
				// take first registered engine
				if (!templateFunction) {
					templateFunction = templateFunctions[Object.keys(templateFunctions).pop()];
				}

				// if template system exists then we go to him
				if (templateFunction) {
					targetPath = getAbsUrl(path, ns.getConfig("findProfileFile", false));
					templateFunction(globalOptions, targetPath, data || {}, templateCallback);
				} else {
					// else we return error
					callback({
						success: false,
						description: "Can't get engine system"
					}, null);
				}
			}

			template = {
				get: get,
				set: set,
				register: register,
				unregister: unregister,
				engine: engine,
				render: render
			};

			ns.template = template;
			}());

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* global define, HTMLElement, ns */
/**
 * #Router
 *
 * Main class to navigate between pages, popups and other widgets which has own rules in all profiles.
 *
 * Class communicates with PageContainer which deactivate and activate changed pages.
 *
 * Router listening on events triggered by history manager.
 *
 * ## Getting instance
 *
 * To receive instance of router you should use method _getInstance_
 *
 * 	@example
 * 		var router = ns.router.Router.getInstance();
 *
 * By default TAU create instance of router and getInstance method return this instance.
 *
 * ##Connected widgets
 *
 * Router cooperate with widgets:
 *
 *  - Page
 *  - Popup
 *  - Drawer
 *  - Dialog (mobile)
 *  - CircularIndexScrollBar (wearable - circle)
 *
 * Opening or closing these widgets are possible by create link with correct rel.
 *
 * ##Global options used in router
 *
 *  - *pageContainer* = document.body - default container element
 *  - *pageContainerBody* = false - use body instead pageContainer option
 *  - *autoInitializePage* = true - automatically initialize first page
 *  - *addPageIfNotExist* = true - automatically add page if doesn't exist
 *  - *loader* = false - enable loader on change page
 *  - *disableRouter* = false - disable auto initialize of router
 *
 * @class ns.router.Router
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @author Hyunkook, Cho <hk0713.cho@samsung.com>
 * @author Piotr Czajka <p.czajka@samsung.com>
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 * @author Michał Szepielak <m.szepielak@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Heeju Joo <heeju.joo@samsung.com>
 */
(function (window, document) {
	"use strict";
				/**
			 * Local alias for ns.util
			 * @property {Object} util Alias for {@link ns.util}
			 * @member ns.router.Router
			 * @static
			 * @private
			 */
			var util = ns.util,
				/**
				 * Local alias for ns.event
				 * @property {Object} eventUtils Alias for {@link ns.event}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				eventUtils = ns.event,
				/**
				 * Alias for {@link ns.util.DOM}
				 * @property {Object} DOM
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				DOM = util.DOM,
				/**
				 * Local alias for ns.util.path
				 * @property {Object} path Alias for {@link ns.util.path}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				path = util.path,
				/**
				 * Local alias for ns.util.selectors
				 * @property {Object} selectors Alias for {@link ns.util.selectors}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				selectors = util.selectors,
				/**
				 * Local alias for ns.util.object
				 * @property {Object} object Alias for {@link ns.util.object}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				object = util.object,
				/**
				 * Local alias for ns.engine
				 * @property {Object} engine Alias for {@link ns.engine}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				engine = ns.engine,
				/**
				 * Local alias for ns.router
				 * @property {Object} router Alias for namespace ns.router
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				router = ns.router,
				/**
				 * Local alias for ns.history
				 * @property {Object} history Alias for {@link ns.history}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				history = ns.history,
				historyManager = history.manager,
				/**
				 * Local alias for ns.history.manager.events
				 * @property {Object} historyManagerEvents Alias for (@link ns.history.manager.events}
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				historyManagerEvents = historyManager.events,
				/**
				 * Local alias for ns.router.route
				 * @property {Object} route Alias for namespace ns.router.route
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				route = router.route,
				/**
				 * Local alias for document body element
				 * @property {HTMLElement} body
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				body = document.body,
				/**
				 * Alias to Array.slice method
				 * @method slice
				 * @member ns.router.Router
				 * @private
				 * @static
				 */
				slice = [].slice,
				/**
				 * Local instance of the Router
				 * @property {Object} routerInstance
				 * @member ns.router.Router
				 * @static
				 * @private
				 */
				_isLock = false,

				ORDER_NUMBER = {
					1: "page",
					10: "panel",
					100: "popup",
					101: "dialog",
					1000: "drawer",
					2000: "circularindexscrollbar"
				},

				eventType = {
					BEFORE_ROUTER_INIT: "beforerouterinit",
					ROUTER_INIT: "routerinit"
				},

				HASH_REGEXP = /[#|\s]/g,

				Page = ns.widget.core.Page,

				routerInstance,

				template = ns.template,

				Router = function () {
					var self = this;

					/**
					 * Instance of widget PageContainer which controls page changing.
					 * @property {?ns.widget.core.PageContainer} [container=null]
					 * @member ns.router.Router
					 */
					self.container = null;
					/**
					 * Settings for last call of method open
					 * @property {Object} [settings={}]
					 * @member ns.router.Router
					 */
					self.settings = {};

					/**
					 * Handler for event "statechange"
					 * @property {Function} [_onStateChangeHandler=null]
					 * @member ns.router.Router
					 * @protected
					 * @since 2.4
					 */
					self._onStateChangeHandler = null;
					/**
					 * Handler for event "hashchange"
					 * @property {Function} [_onHashChangeHandler=null]
					 * @member ns.router.Router
					 * @protected
					 * @since 2.4
					 */
					self._onHashChangeHandler = null;
					/**
					 * Handler for event "controllercontent"
					 * @property {Function} [_onControllerContent=null]
					 * @member ns.router.Router
					 * @protected
					 * @since 2.4
					 */
					self._onControllerContent = null;

					/**
					 * Router locking flag
					 * @property {boolean} locked=false
					 * @member ns.router.Router
					 * @since 2.4
					 */
					self.locked = false;
				};

			/**
			 * Default values for router
			 * @property {Object} defaults
			 * @property {boolean} [defaults.fromHashChange=false] Sets if will be changed after hashchange.
			 * @property {boolean} [defaults.reverse=false] Sets the direction of change.
			 * @property {boolean} [defaults.volatileRecord=false] Sets if the current history entry will be modified or a new one will be created.
			 * @member ns.router.Router
			 */
			Router.prototype.defaults = {
				fromHashChange: false,
				reverse: false,
				volatileRecord: false
			};

			/**
			 * Find the closest link for element
			 * @method findClosestLink
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @private
			 * @static
			 * @member ns.router.Router
			 */
			function findClosestLink(element) {
				while (element) {
					if (element.nodeType === Node.ELEMENT_NODE && element.nodeName && element.nodeName === "A") {
						break;
					}
					element = element.parentNode;
				}
				return element;
			}

			/**
			 * Handle event link click
			 * @method linkClickHandler
			 * @param {ns.router.Router} router
			 * @param {Event} event
			 * @private
			 * @static
			 * @member ns.router.Router
			 */
			function linkClickHandler(router, event) {
				var link = findClosestLink(event.target),
					href,
					useDefaultUrlHandling,
					options;

				if (link && event.which === 1) {
					href = link.getAttribute("href");
					useDefaultUrlHandling = (link.getAttribute("rel") === "external") || link.hasAttribute("target");
					if (!useDefaultUrlHandling) {
						options = DOM.getData(link);
						router.open(href, options, event);
						eventUtils.preventDefault(event);
					}
				}
			}

			Router.prototype.linkClick = function (event) {
				linkClickHandler(this, event);
			};

			function openUrlFromState(instanceRouter, state) {
				var rules = router.route,
					prevState = history.activeState,
					reverse = state && history.getDirection(state) === "back",
					maxOrderNumber,
					orderNumberArray = [],
					ruleKey,
					options,
					url = path.getLocation(),
					isContinue = true,
					transition,
					rule;

				transition = reverse ? ((prevState && prevState.transition) || "none") : state.transition;
				options = object.merge({}, state, {
					reverse: reverse,
					transition: transition,
					fromHashChange: true
				});

				// find rule with max order number
				for (ruleKey in rules) {
					if (rules.hasOwnProperty(ruleKey) && rules[ruleKey].active) {
						orderNumberArray.push(rules[ruleKey].orderNumber);
					}
				}
				maxOrderNumber = Math.max.apply(null, orderNumberArray);
				rule = rules[ORDER_NUMBER[maxOrderNumber]];

				if (rule && rule.onHashChange(url, options, prevState)) {
					if (maxOrderNumber === 10) {
						// rule is panel
						return;
					}
					isContinue = false;
				}

				history.setActive(state);
				if (isContinue) {
					instanceRouter.open(state.url, options);
				}
			}

			/**
			 * Detect rel attribute from HTMLElement.
			 *
			 * This method tries to match element to each rule filter and return first rule name which match.
			 *
			 * If don't match any rule then return null.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.detectRel(document.getElementById("pageId"));
			 *		// if HTML element will be match to selector of page then return rule for page
			 *
			 * @param {HTMLElement} to element to check
			 * @member ns.router.Router
			 * @return {?string}
			 */
			Router.prototype.detectRel = function (to) {
				var rule,
					i;

				for (i in route) {
					if (route.hasOwnProperty(i)) {
						rule = route[i];
						if (selectors.matchesSelector(to, rule.filter)) {
							return i;
						}
					}
				}

				return null;
			};


			/**
			 * Open given page with deferred
			 * @method _openDeferred
			 * @param {HTMLElement} to HTMLElement of page
			 * @param {Object} [options]
			 * @param {"page"|"popup"|"external"} [options.rel = "page"] Represents kind of link as "page"
			 * or "popup"
			 * or "external" for linking to another domain.
			 * @param {string} [options.transition = "none"] Sets the animation used during change of
			 * page.
			 * @param {boolean} [options.reverse = false] Sets the direction of change.
			 * @param {boolean} [options.fromHashChange = false] Sets if will be changed after hashchange.
			 * @param {boolean} [options.volatileRecord = false] Sets if the current history entry will
			 * be modified or
			 * a new one will be created.
			 * @param {boolean} [options.dataUrl] Sets if page has url attribute.
			 * @param {?string} [options.container = null] It is used in RoutePopup as selector for
			 * container.
			 * @param {Event} event
			 * @member ns.router.Router
			 * @protected
			 */
			Router.prototype._openDeferred = function (to, options, event) {
				var self = this,
					rule = route[options.rel],
					deferred = {
						resolve: function (_options, content) {
							rule.open(content, _options, event);
						},
						reject: function (_options) {
							eventUtils.trigger(self.container.element, "changefailed", _options);
						}
					};

				if (typeof to === "string") {
					if (to.replace(HASH_REGEXP, "")) {
						self._loadUrl(to, options, rule, deferred);
					}
				} else {
					// execute deferred object immediately
					if (to && selectors.matchesSelector(to, rule.filter)) {
						deferred.resolve(options, to);
					} else {
						deferred.reject(options);
					}
				}
			};

			/**
			 * Change page to page given in parameter "to".
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.open("pageId");
			 *		// open page with given id
			 *		router.open("page.html");
			 *		// open page from html file
			 *		router.open("popupId");
			 *		// open popup with given id
			 *
			 * @method open
			 * @param {string|HTMLElement} to Id of page or file url or HTMLElement of page
			 * @param {Object} [options]
			 * @param {"page"|"popup"|"external"} [options.rel="page"] Represents kind of link as "page" or "popup" or "external" for linking to another domain.
			 * @param {string} [options.transition="none"] Sets the animation used during change of page.
			 * @param {boolean} [options.reverse=false] Sets the direction of change.
			 * @param {boolean} [options.fromHashChange=false] Sets if will be changed after hashchange.
			 * @param {boolean} [options.volatileRecord=false] Sets if the current history entry will be modified or a new one will be created.
			 * @param {boolean} [options.dataUrl] Sets if page has url attribute.
			 * @param {?string} [options.container=null] It is used in RoutePopup as selector for container.
			 * @param {Event} [event] Event object
			 * @member ns.router.Router
			 */
			Router.prototype.open = function (to, options, event) {
				var self = this,
					rel,
					rule;

				if (!_isLock) {
					to = getHTMLElement(to);
					rel = (options && options.rel) ||
						(to instanceof HTMLElement && self.detectRel(to));
					rel = rel || "page";
					rule = route[rel];

					if (rel === "back") {
						history.back();
					} else if (rule) {
						options = object.merge(
							{
								rel: rel
							},
							self.defaults,
							rule.option(),
							options
						);
						self._openDeferred(to, options, event);
					} else {
						throw new Error("Not defined router rule [" + rel + "]");
					}
				}
			};


			/**
			 * Init routes defined in router
			 * @method _initRoutes
			 * @member ns.router.Router
			 */
			Router.prototype._initRoutes = function () {
				var ruleKey,
					rules = router.route;

				for (ruleKey in rules) {
					if (rules.hasOwnProperty(ruleKey) && rules[ruleKey].init) {
						rules[ruleKey].init();
					}
				}
			};

			function removeActivePageClass(containerElement) {
				var PageClasses = Page.classes,
					uiPageActiveSelector = "." + PageClasses.uiPageActive,
					activePages = slice.call(containerElement.querySelectorAll(uiPageActiveSelector));

				activePages.forEach(function (page) {
					page.classList.remove(uiPageActiveSelector);
				});
			}

			Router.prototype._autoInitializePage = function (containerElement, pages, pageSelector) {
				var self = this,
					page,
					location = window.location,
					uiPageActiveClass = Page.classes.uiPageActive,
					firstPage = containerElement.querySelector("." + uiPageActiveClass);

				if (!firstPage) {
					firstPage = pages[0];
				}

				if (firstPage) {
					removeActivePageClass(containerElement);
				}

				if (location.hash) {
					//simple check to determine if we should show firstPage or other
					page = document.getElementById(location.hash.replace("#", ""));
					if (page && selectors.matchesSelector(page, pageSelector)) {
						firstPage = page;
					}
				}

				if (!firstPage && ns.getConfig("addPageIfNotExist", true)) {
					firstPage = Page.createEmptyElement();
					while (containerElement.firstChild) {
						firstPage.appendChild(containerElement.firstChild);
					}
					containerElement.appendChild(firstPage);
				}

				if (self.justBuild) {
										if (firstPage) {
						self.register(
							engine.instanceWidget(containerElement, "pagecontainer"),
							firstPage
						);
					}
				}

				return firstPage;
			};

			/**
			 * Method initializes page container and builds the first page if flag autoInitializePage is
			 * set.
			 * @method init
			 * @param {boolean} justBuild
			 * @member ns.router.Router
			 */
			Router.prototype.init = function (justBuild) {
				var containerElement,
					firstPage,
					pages,
					pageDefinition = ns.engine.getWidgetDefinition("Page"),
					pageSelector = pageDefinition.selector,
					self = this;

				eventUtils.trigger(document, eventType.BEFORE_ROUTER_INIT, self, false);

				body = document.body;
				self.justBuild = justBuild;

				containerElement = ns.getConfig("pageContainer") || body;
				pages = slice.call(containerElement.querySelectorAll(pageSelector));

				if (!ns.getConfig("pageContainerBody", false)) {
					containerElement = pages.length ? pages[0].parentNode : containerElement;
				}

				if (ns.getConfig("autoInitializePage", true)) {
					firstPage = self._autoInitializePage(containerElement, pages, pageSelector);
					if (justBuild) {
						return;
					}
				}

				historyManager.enable();

				// init router's routes
				self._initRoutes();

				self.register(
					engine.instanceWidget(containerElement, "pagecontainer"),
					firstPage
				);

				eventUtils.trigger(document, eventType.ROUTER_INIT, self, false);
			};

			/**
			 * Method removes all events listeners set by router.
			 *
			 * Also remove singleton instance of router;
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.destroy();
			 *		var router2 = tau.router.Router.getInstance();
			 *		// router !== router2
			 *
			 * @method destroy
			 * @member ns.router.Router
			 */
			Router.prototype.destroy = function () {
				var self = this;

				historyManager.disable();

				window.removeEventListener("popstate", self.popStateHandler, false);
				if (body) {
					body.removeEventListener("pagebeforechange", self.pagebeforechangeHandler, false);
					body.removeEventListener("vclick", self.linkClickHandler, false);
				}
				ns.setConfig("pageContainer", null);
			};

			/**
			 * Method sets instance of PageContainer widget
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.setContainer(new ns.widget.PageContainer());
			 *
			 * @method setContainer
			 * @param {ns.widget.core.PageContainer} container
			 * @member ns.router.Router
			 */
			Router.prototype.setContainer = function (container) {
				this.container = container;
			};

			/**
			 * Method returns instance of PageContainer widget
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		containerWidget = router.getContainer();
			 *
			 * @method getContainer
			 * @return {ns.widget.core.PageContainer}
			 * @member ns.router.Router
			 */
			Router.prototype.getContainer = function () {
				return this.container;
			};

			/**
			 * Method returns ths first page HTMLElement
			 * @method getFirstPage
			 * @return {HTMLElement}
			 * @member ns.router.Router
			 */
			Router.prototype.getFirstPage = function () {
				return this.getRoute("page").getFirstElement();
			};


			/**
			 * Callback for event "historyhashchange" which is triggered by history manager after hash is changed
			 * @param {ns.router.Router} router
			 * @param {Event} event
			 */
			function onHistoryHashChange(router, event) {
								openUrlFromState(router, event.detail);
			}

			/**
			 * Callback for event "historystatechange" which is triggered by history manager after hash is changed
			 * @param {ns.router.Router} router
			 * @param {Event} event
			 */
			function onHistoryStateChange(router, event) {
				var options = event.detail,
					//
					url = options.reverse ? options.url : (options.href || options.url);

				delete options.event;
				router.open(url, options);
				// prevent current event
				eventUtils.preventDefault(event);
				eventUtils.stopImmediatePropagation(event);
			}

			/**
			 * Convert HTML string to HTMLElement
			 * @param {string|HTMLElement} content
			 * @param {string} title
			 * @return {?HTMLElement}
			 */
			function convertToNode(content, title) {
				var contentNode = null,
					externalDocument = document.implementation.createHTMLDocument(title),
					externalBody = externalDocument.body;

				if (content instanceof HTMLElement) {
					// if content is HTMLElement just set to contentNode
					contentNode = content;
				} else {
					// otherwise convert string to HTMLElement
					try {
						externalBody.insertAdjacentHTML("beforeend", content);
						contentNode = externalBody.firstChild;
					} catch (e) {
						ns.error("Failed to inject element", e);
					}
				}
				return contentNode;
			}

			/**
			 * Set data-url on HTMLElement if not exists
			 * @param {HTMLElement} contentNode
			 * @param {string} url
			 */
			function setURLonElement(contentNode, url) {
				if (url) {
					if (contentNode instanceof HTMLElement && !DOM.hasNSData(contentNode, "url")) {
						// if url is missing we need set data-url attribute for good finding by method open in router
						url = url.replace(/^#/, "");
						DOM.setNSData(contentNode, "url", url);
					}
				}
			}

			/**
			 * Callback for event "controller-content-available" which is triggered by controller after application handle hash change
			 * @param {ns.router.Router} router
			 * @param {Event} event
			 */
			function onControllerContent(router, event) {
				var data = event.detail,
					content = data.content,
					options = data.options,
					contentNode,
					url = (options.href || options.url);

				// if controller give content
				if (content) {
					// convert to node if content is string
					contentNode = convertToNode(content, options.title);

					// set data-url on node
					setURLonElement(contentNode, url);

					// calling open method
					router.open(contentNode, options);

					//prevent event
					eventUtils.preventDefault(event);
				}
			}


			/**
			 * Method registers page container and the first page.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.register(new ns.widget.PageContainer(), document.getElementById("firstPage"));
			 *
			 * @method register
			 * @param {ns.widget.core.PageContainer} container
			 * @param {HTMLElement} firstPage
			 * @member ns.router.Router
			 */
			Router.prototype.register = function (container, firstPage) {
				var self = this,
					routePopup = this.getRoute("popup");

				// sets instance of PageContainer widget
				self.container = container;

				// sets first page HTMLElement
				self.getRoute("page").setFirstElement(firstPage);

				eventUtils.trigger(document, "themeinit", self);

				// sets events handlers
				if (!self._onHashChangeHandler) {
					self._onHashChangeHandler = onHistoryHashChange.bind(null, self);
					window.addEventListener(historyManagerEvents.HASHCHANGE, self._onHashChangeHandler, false);
				}
				if (!self._onStateChangeHandler) {
					self._onStateChangeHandler = onHistoryStateChange.bind(null, self);
					window.addEventListener(historyManagerEvents.STATECHANGE, self._onStateChangeHandler, false);
				}
				if (!self._onControllerContent) {
					self._onControllerContent = onControllerContent.bind(null, self);
					window.addEventListener("controller-content-available", self._onControllerContent, false);
				}

				// if loader config is set then create loader widget
				if (ns.getConfig("loader", false)) {
					container.element.appendChild(self.getLoader().element);
				}

				// set history support
				history.enableVolatileMode();

				// if first page exist open this page without transition
				if (firstPage) {
					self.open(firstPage, {transition: "none"});
				}

				if (routePopup) {
					routePopup.setActive(null);
				}
			};

			/**
			 * Convert string id to HTMLElement or return HTMLElement if is given
			 * @param {string|HTMLElement} idOrElement
			 * @return {HTMLElement|string}
			 */
			function getHTMLElement(idOrElement) {
				var stringId,
					toElement;

				// if given argument is string then
				if (typeof idOrElement === "string") {
					if (idOrElement[0] === "#") {
						// trim first char if it is #
						stringId = idOrElement.substr(1);
					} else {
						stringId = idOrElement;
					}
					// find element by id
					toElement = document.getElementById(stringId);

					if (toElement) {
						// is exists element by id then return it
						idOrElement = toElement;
					}
					// otherwise return string
				}
				return idOrElement;
			}

			/**
			 * Method close route element, eg page or popup.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.close("popupId", {transition: "none"});
			 *
			 * @method close
			 * @param {string|HTMLElement} to Id of page or file url or HTMLElement of page
			 * @param {Object} [options]
			 * @param {"page"|"popup"|"external"} [options.rel="page"] Represents kind of link as "page" or "popup" or "external" for linking to another domain
			 * @member ns.router.Router
			 */
			Router.prototype.close = function (to, options) {
				var rel = "back",
					closingWidget = getHTMLElement(to),
					rule;

				if (options && options.rel) {
					rel = options.rel;
				} else if (closingWidget) {
					rel = this.detectRel(closingWidget);
				}

				rule = route[rel];

				// if router is not locked
				if (!this.locked) {
					// if rel is back then call back method
					if (rel === "back") {
						history.back();
					} else {
						// otherwise if rule exists
						if (rule) {
							// call close on rule
							rule.close(closingWidget, options);
						} else {
							throw new Error("Not defined router rule [" + rel + "]");
						}
					}
				}
			};

			/**
			 * Method back to previous state.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.back();
			 *
			 * @method close
			 * @member ns.router.Router
			 */
			Router.prototype.back = function () {

				// if router is not locked
				if (!this.locked) {
					history.back();
				}
			};

			/**
			 * Method opens popup.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.openPopup("popupId", {transition: "none"});
			 *
			 * @method openPopup
			 * @param {HTMLElement|string} to Id or HTMLElement of popup.
			 * @param {Object} [options]
			 * @param {string} [options.transition="none"] Sets the animation used during change of page.
			 * @param {boolean} [options.reverse=false] Sets the direction of change.
			 * @param {boolean} [options.fromHashChange=false] Sets if will be changed after hashchange.
			 * @param {boolean} [options.volatileRecord=false] Sets if the current history entry will be modified or a new one will be created.
			 * @param {boolean} [options.dataUrl] Sets if page has url attribute.
			 * @param {?string} [options.container=null] It is used in RoutePopup as selector for container.
			 * @member ns.router.Router
			 */
			Router.prototype.openPopup = function (to, options) {
				// call method open with overwrite rel option
				this.open(to, object.fastMerge({rel: "popup"}, options));
			};

			/**
			 * Method closes popup.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance();
			 *		router.closePopup();
			 *
			 * @method closePopup
			 * @param {Object} options
			 * @param {string=} [options.transition]
			 * @param {string=} [options.ext="in ui-pre-in"] options.ext
			 * @member ns.router.Router
			 */
			Router.prototype.closePopup = function (options) {
				var popupRoute = this.getRoute("popup");

				if (popupRoute) {
					popupRoute.close(null, options);
				}
			};

			/**
			 * Lock router
			 * @method lock
			 * @member ns.router.Router
			 */
			Router.prototype.lock = function () {
				this.locked = true;
			};

			/**
			 * Unlock router and history manager
			 * @method unlock
			 * @member ns.router.Router
			 */
			Router.prototype.unlock = function () {
				this.locked = false;
			};

			/**
			 * Load content from url.
			 *
			 * Method prepare url and call template function to load external file.
			 *
			 * If option showLoadMsg is ste to tru open loader widget before start loading.
			 *
			 * @method _loadUrl
			 * @param {string} url full URL to load
			 * @param {Object} options options for this and next methods in chain
			 * @param {boolean} [options.data] Sets if page has url attribute.
			 * @param {Object} rule rule which support given call
			 * @param {Object} deferred object with callbacks
			 * @param {Function} deferred.reject callback on error
			 * @param {Function} deferred.resolve callback on success
			 * @member ns.router.Router
			 * @protected
			 */
			Router.prototype._loadUrl = function (url, options, rule, deferred) {
				var absUrl = path.makeUrlAbsolute(url, path.getLocation()),
					content,
					self = this,
					data = options.data || {};

				// check if content is loaded in current document
				content = rule.find(absUrl);

				// if content doesn't find and url is embedded url
				if (!content && path.isEmbedded(absUrl)) {
					// reject
					deferred.reject({});
				} else {
					// If the content we are interested in is already in the DOM,
					// and the caller did not indicate that we should force a
					// reload of the file, we are done. Resolve the deferred so that
					// users can bind to .done on the promise
					if (content) {
						// content was found and we resolve
						deferred.resolve(object.fastMerge({absUrl: absUrl}, options), content);
					} else {

						// Load the new content.
						eventUtils.trigger(self.getContainer().element, options.rel + "beforeload");

						// force return full document from template system
						data.fullDocument = true;
						// we put url, not the whole path to function render,
						// because this path can be modified by template's module
						template.render(url, data, function (status, element) {
							// if template loaded successful
							if (status.success) {
								self._loadSuccess(status.absUrl, options, rule, deferred, element);
								eventUtils.trigger(self.getContainer().element, options.rel + "load");
							} else {
								self._loadError(status.absUrl, options, deferred);
							}
						});
					}
				}
			};

			/**
			 * Error handler for loading content by AJAX
			 * @method _loadError
			 * @param {string} absUrl full URL to load
			 * @param {Object} options options for this and next methods in chain
			 * @param {boolean} [options.showLoadMsg=true] Sets if message will be shown during loading.
			 * @param {Object} deferred object with callbacks
			 * @param {Function} deferred.reject callback on error
			 * @member ns.router.Router
			 * @protected
			 */
			Router.prototype._loadError = function (absUrl, options, deferred) {
				var detail = object.fastMerge({url: absUrl}, options),
					self = this;

				ns.error("load error, file: ", absUrl);

				self.container.trigger("loadfailed", detail);
				deferred.reject(detail);
			};

			// TODO it would be nice to split this up more but everything appears to be "one off"
			//	or require ordering such that other bits are sprinkled in between parts that
			//	could be abstracted out as a group
			/**
			 * Success handler for loading content by AJAX
			 * @method _loadSuccess
			 * @param {string} absUrl full URL to load
			 * @param {Object} options options for this and next methods in chain
			 * @param {boolean} [options.showLoadMsg=true] Sets if message will be shown during loading.
			 * @param {Object} rule rule which support given call
			 * @param {Object} deferred object with callbacks
			 * @param {Function} deferred.reject callback on error
			 * @param {Function} deferred.resolve callback on success
			 * @param {string} html
			 * @member ns.router.Router
			 * @protected
			 */
			Router.prototype._loadSuccess = function (absUrl, options, rule, deferred, html) {
				var detail = object.fastMerge({url: absUrl}, options),
					// find element with given id in returned html
					content = rule.parse(html, absUrl);

				if (content) {
					deferred.resolve(detail, content);
				} else {
					deferred.reject(detail);
				}
			};

			// TODO the first page should be a property set during _create using the logic
			//	that currently resides in init
			/**
			 * Get initial content
			 * @method _getInitialContent
			 * @member ns.router.Router
			 * @return {HTMLElement} the first page
			 * @protected
			 */
			Router.prototype._getInitialContent = function () {
				return this.getRoute("page").getFirstElement();
			};

			/**
			 * Report an error loading
			 * @method _showError
			 * @param {string} absUrl
			 * @member ns.router.Router
			 * @protected
			 */
			Router.prototype._showError = function (absUrl) {
				ns.error("load error, file: ", absUrl);
			};

			/**
			 * Returns Page or Popup widget
			 * @param {string} [routeName="page"] in default page or popup
			 * @method getActive
			 * @return {ns.widget.BaseWidget}
			 * @member ns.router.Router
			 */
			Router.prototype.getActive = function (routeName) {
				var route = this.getRoute(routeName || "page");

				return route && route.getActive();
			};

			/**
			 * Returns true if element in given route is active.
			 * @param {string} [routeName="page"] in default page or popup
			 * @method hasActive
			 * @return {boolean}
			 * @member ns.router.Router
			 */
			Router.prototype.hasActive = function (routeName) {
				var route = this.getRoute(routeName || "page");

				return !!(route && route.hasActive());
			};

			/**
			 * Returns true if any popup is active.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance(),
			 *			hasActivePopup = router.hasActivePopup();
			 *			// -> true | false
			 *
			 * @method hasActivePopup
			 * @return {boolean}
			 * @member ns.router.Router
			 */
			Router.prototype.hasActivePopup = function () {
				return this.hasActive("popup");
			};

			/**
			 * This function returns proper route.
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance(),
			 *			route = router.getRoute("page"),
			 *			// -> Object with pages support
			 *			activePage = route.getActive();
			 *			// instance of Page widget
			 *
			 * @method getRoute
			 * @param {string} type Type of route
			 * @return {?ns.router.route.interface}
			 * @member ns.router.Router
			 */
			Router.prototype.getRoute = function (type) {
				return route[type];
			};

			/**
			 * Returns instance of loader widget.
			 *
			 * If loader not exist then is created on first element matched to selector
			 * or is created new element.
			 *
			 *	@example
			 *		var loader = router.getLoader();
			 *		// get or create loader
			 *		loader.show();
			 *		// show loader
			 *
			 * @return {?ns.widget.mobile.Loader}
			 * @member ns.router.Page
			 * @method getLoader
			 */
			Router.prototype.getLoader = function () {
				var loaderDefinition = engine.getWidgetDefinition("Loader"),
					loaderSelector = loaderDefinition.selector,
					loaderElement;

				if (loaderDefinition) {
					loaderElement = document.querySelector(loaderSelector);
					return engine.instanceWidget(loaderElement, "Loader");
				}
				return null;
			};

			/**
			 * Creates a new instance of the router and returns it
			 *
			 *	@example
			 *		var router = Router.newInstance();
			 *
			 * @method newInstance
			 * @member ns.router.Router
			 * @static
			 * @return {ns.router.Router}
			 * @since 2.4
			 */
			Router.newInstance = function () {
				return (routerInstance = new Router());
			};

			/**
			 * Returns a instance of the router, creates a new if does not exist
			 *
			 *	@example
			 *		var router = tau.router.Router.getInstance(),
			 *			// if router not exists create new instance and return
			 *			router2 = tau.router.Router.getInstance();
			 *			// only return router from first step
			 *			// router === router2
			 *
			 * @method getInstance
			 * @member ns.router.Router
			 * @return {ns.router.Router}
			 * @since 2.4
			 * @static
			 */
			Router.getInstance = function () {
				if (!routerInstance) {
					return this.newInstance();
				}
				return routerInstance;
			};

			router.Router = Router;

			Router.eventType = eventType;

			/**
			 * Returns router instance
			 * @deprecated 2,4
			 * @return {ns.router.Router}
			 */
			engine.getRouter = function () { //@TODO FIX HACK old API
				//@TODO this is suppressed since the tests are unreadable
				// tests need fixes
				ns.warn("getRouter() method is deprecated! Use tau.router.Router.getInstance() instead");
				return Router.getInstance();
			};

			if (!ns.getConfig("disableRouter", false)) {
				document.addEventListener(engine.eventType.READY, function () {
					Router.getInstance().init();
				}, false);
				document.addEventListener(engine.eventType.DESTROY, function () {
					Router.getInstance().destroy();
				}, false);
				document.addEventListener(engine.eventType.STOP_ROUTING, function () {
					Router.getInstance().destroy();
				}, false);
			}

			//engine.initRouter(Router);
			}(window, window.document));

/*global window, ns, define, HTMLElement */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping router
 * Object maps router from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.router
 */
(function (window, document) {
	"use strict";
	
			var engine = ns.engine,
				$ = ns.jqm.jQuery,
				eventType = ns.router.Router.eventType,
				jqmRouter = {
					/**
					 * Enables support jQM before router init.
					 * @method beforeinit
					 * @member ns.jqm.router
					 */
					beforeinit: function () {
						var container,
							router = ns.router.Router.getInstance();

						if ($) {
							if ($.mobile) {
								if ($.mobile.pageContainer) {
									container = $.mobile.pageContainer;
									if (container instanceof $) {
										container = container[0];
									}
									if (!(container instanceof HTMLElement)) {
										container = document.body;
									}
									ns.setConfig("pageContainer", container);
									$.mobile.pageContainer = $(container);
								}
								if ($.mobile.autoInitializePage !== undefined) {
									ns.setConfig("autoInitializePage", $.mobile.autoInitializePage);
								}
								if ($.mobile._bindPageRemove !== undefined) {
									ns.setConfig("_bindPageRemove", $.mobile._bindPageRemove);
								}
								$.mobile.changePage = function (toPage, options) {
									var htmlElementToPage;

									if (toPage instanceof $) {
										htmlElementToPage = $(toPage).get(0);
										return router.open(htmlElementToPage, options);
									}
									return router.open(toPage, options);
								};
								document.addEventListener("pagechange", function () {
									var route = router.getRoute("page"),
										activePage = route && route.getActive(),
										target = activePage && activePage.element;

									$.mobile.activePage = $(target);
								}, true);
								$.mobile.activePage = $();
								$.mobile.firstPage = $(router.getRoute("page").getFirstElement());
								$.mobile.pageContainer = $();
								$.mobile.subPageUrlKey = ns.widget.core.Page.classes.uiPage;
								$.mobile.ajaxEnabled = true;
								$.mobile.hashListeningEnabled = true;
								$.mobile.linkBindingEnabled = true;
								$.mobile.maxTransitionWidth = false;
								$.mobile.minScrollBack = 250;
								$.mobile.touchOverflowEnabled = false;
								$.mobile.defaultDialogTransition = "pop";
								$.mobile.pageLoadErrorMessage = "Error Loading Page";
								$.mobile.pageLoadErrorMessageTheme = "e";
								$.mobile.phonegapNavigationEnabled = false;
								$.mobile.autoInitializePage = false;
								$.mobile.pushStateEnabled = true;
								$.mobile.ignoreContentEnabled = false;
								$.mobile.orientationChangeEnabled = true;
								$.mobile.ajaxBlacklist = false;
								$.mobile.defaultTransitionHandler = null;
								$.mobile.transitionHandlers = {};
								$.mobile.transitionFallbacks = {};
								$.mobile._maybeDegradeTransition = null;
								$.mobile.focusPage = null;
								//$.mobile.urlHistory = ns.router.urlHistory;
								$.mobile.dialogHashKey = "&ui-state=dialog";
								$.mobile.allowCrossDomainPages = false;
								$.mobile.getDocumentUrl = ns.util.path.getDocumentUrl;
								$.mobile.getDocumentBase = ns.util.path.getDocumentBase;
								$.mobile._bindPageRemove = null;
								$.mobile.loadPage = router.loadPage === undefined ? ns.error.bind(null, "router PageExternal is not loaded") : router.loadPage.bind(router);
								$.mobile.navreadyDeferred = router.navreadyDeferred;
								$.mobile.initializePage = null;
								$.mobile._handleHashChange = router._hashChangeHandler;
							} else {
								$.mobile = {};
							}
						}
					},
					/**
					 * Enables support jQM after router init.
					 * @method init
					 * @member ns.jqm.router
					 */
					init: function () {
						var transitions,
							name,
							router = ns.router.Router.getInstance(),
							containerWidget;

						if ($) {
							$.mobile.defaultPageTransition = "none";

							if (router.getTransitions) {
								transitions = router.getTransitions();
								for (name in transitions) {
									if (transitions.hasOwnProperty(name)) {
										if (transitions[name].fallback !== undefined) {
											$.mobile.transitionFallbacks[name] = transitions[name].fallback;
										}
										if (transitions[name].handler !== undefined) {
											$.mobile.transitionHandlers[name] = transitions[name].handler;
										}
									}
								}
								$.mobile.defaultTransitionHandler = transitions.sequential.handler;
								$.mobile._maybeDegradeTransition = router._maybeDegradeTransition.bind(router);
								$.mobile.getMaxScrollForTransition = router.getMaxScrollForTransition.bind(router);
							}

							$.mobile.focusPage = function (toPage) {
								var page = $(toPage)[0],
									pageWidget = engine.getBinding(page);

								pageWidget.focus();
							};

							$.mobile.initializePage = router.init.bind(router);
							containerWidget = router.getContainer();
							if (containerWidget) {
								$.mobile.pageContainer = $(containerWidget.element);
							}
						}
					},
					destroy: function () {
						document.removeEventListener(eventType.ROUTER_INIT, jqmRouter.init, false);
						document.removeEventListener(eventType.BEFORE_ROUTER_INIT, jqmRouter.beforeinit, false);
						document.removeEventListener(eventType.DESTROY, jqmRouter.destroy, false);
					}
				};

			document.addEventListener(eventType.ROUTER_INIT, jqmRouter.init, false);
			document.addEventListener(eventType.BEFORE_ROUTER_INIT, jqmRouter.beforeinit, false);
			document.addEventListener(eventType.DESTROY, jqmRouter.destroy, false);

			ns.jqm.router = jqmRouter;
			}(window, window.document));

/*global window, ns, define */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping support
 * Object maps support object from TAU namespace to jQuery Mobile namespace.
 * @class ns.jqm.support
 */
(function (window, document) {
	"use strict";
				var support = ns.support,
				$ = ns.jqm.jQuery,
				object = ns.util.object,
				eventType = ns.engine.eventType,
				jqmSupport = {
					/**
					 * Touch support flag
					 * @property {boolean} touch
					 * @member ns.jqm.support
					 */
					touch: document.ontouchend !== undefined,
					/**
					 * Enables support in jQM after TAU init
					 * @method init
					 * @member ns.jqm.support
					 */
					init: function () {
						var router = ns.router.Router.getInstance();

						if ($) {
							ns.support = object.merge($.support, support);
							$.mobile = $.mobile || {};
							$.mobile.support = $.mobile.support || {};
							$.mobile.support.touch = support.touch;
							$.mobile.base = support.dynamicBaseTag && {
								element: router.resetBase === undefined ? ns.error.bind(null, "router PageExternal is not loaded") : router.resetBase(),
								set: router.setBase === undefined ? ns.error.bind(null, "router PageExternal is not loaded") : router.setBase.bind(router),
								reset: router.resetBase === undefined ? ns.error.bind(null, "router PageExternal is not loaded") : router.resetBase.bind(router)
							};
							$.mobile.gradeA = ns.support.gradeA.bind(ns.support);
							$.mobile.browser = ns.support.browser;
						}
					},
					/**
					 * Removes events listeners on framework destroy
					 */
					destroy: function () {
						document.removeEventListener(eventType.INIT, jqmSupport.init, false);
						document.removeEventListener(eventType.DESTROY, jqmSupport.destroy, false);
					}
				};

			// Listen when framework is ready
			document.addEventListener(eventType.INIT, jqmSupport.init, false);
			document.addEventListener(eventType.DESTROY, jqmSupport.destroy, false);

			ns.jqm.support = jqmSupport;
			}(window, window.document));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Colors Utility
 * Class supports converting between color formats
 * @class ns.util.colors
 */

(function (window, document, ns) {
	"use strict";
				ns.util.colors = {
				/**
				 * Round to the nearest Integer
				 * @method nearestInt
				 * @param {number} val
				 * @return {number}
				 * @member ns.util.colors
				 * @static
				 */
				nearestInt: function (val) {
					var theFloor = Math.floor(val);

					return (((val - theFloor) > 0.5) ? (theFloor + 1) : theFloor);
				},

				/**
				 * Converts html color string to rgb array.
				 * @method HTMLToRGB
				 * @param {string} clrStr is of the form "#aabbcc"
				 * @return {number[]} Returns: [ r, g, b ], where
				 * r is in [0, 1]
				 * g is in [0, 1]
				 * b is in [0, 1]
				 * @member ns.util.colors
				 * @static
				 */
				HTMLToRGB: function (clrStr) {
					clrStr = (("#" === clrStr.charAt(0)) ? clrStr.substring(1) : clrStr);
					return ([
						clrStr.substring(0, 2),
						clrStr.substring(2, 4),
						clrStr.substring(4, 6)
					].map(function (val) {
						return parseInt(val, 16) / 255.0;
					}));
				},

				/**
				 * Converts rgb array to html color string.
				 * @method RGBToHTML
				 * @param {number[]} rgb Input: [ r, g, b ], where
				 * r is in [0, 1]
				 * g is in [0, 1]
				 * b is in [0, 1]
				 * @return {string} Returns string of the form "#aabbcc"
				 * @member ns.util.colors
				 * @static
				 */
				RGBToHTML: function (rgb) {
					return ("#" +
					rgb.map(function (val) {
						var ret = val * 255,
							theFloor = Math.floor(ret);

						ret = ((ret - theFloor > 0.5) ? (theFloor + 1) : theFloor);
						ret = (((ret < 16) ? "0" : "") + (ret & 0xff).toString(16));
						return ret;
					})
						.join(""));
				},

				/**
				 * Converts hsl to rgb.
				 * @method HSLToRGB
				 * @param {number[]} hsl Input: [ h, s, l ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * l is in [0,   1]
				 * @return {number[]} Returns: [ r, g, b ], where
				 * r is in [0, 1]
				 * g is in [0, 1]
				 * b is in [0, 1]
				 * @member ns.util.colors
				 * @static
				 */
				HSLToRGB: function (hsl) {
					var h = hsl[0] / 360.0,
						s = hsl[1],
						l = hsl[2],
						temp1,
						temp2,
						temp3,
						ret;

					if (0 === s) {
						ret = [l, l, l];
					} else {
						temp2 = ((l < 0.5) ? l * (1.0 + s) : l + s - l * s);
						temp1 = 2.0 * l - temp2;
						temp3 = {
							r: h + 1.0 / 3.0,
							g: h,
							b: h - 1.0 / 3.0
						};

						temp3.r = ((temp3.r < 0) ? (temp3.r + 1.0) : ((temp3.r > 1) ? (temp3.r - 1.0) : temp3.r));
						temp3.g = ((temp3.g < 0) ? (temp3.g + 1.0) : ((temp3.g > 1) ? (temp3.g - 1.0) : temp3.g));
						temp3.b = ((temp3.b < 0) ? (temp3.b + 1.0) : ((temp3.b > 1) ? (temp3.b - 1.0) : temp3.b));

						ret = [
							(((6.0 * temp3.r) < 1) ? (temp1 + (temp2 - temp1) * 6.0 * temp3.r) :
								(((2.0 * temp3.r) < 1) ? temp2 :
									(((3.0 * temp3.r) < 2) ? (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp3.r) * 6.0) :
										temp1))),
							(((6.0 * temp3.g) < 1) ? (temp1 + (temp2 - temp1) * 6.0 * temp3.g) :
								(((2.0 * temp3.g) < 1) ? temp2 :
									(((3.0 * temp3.g) < 2) ? (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp3.g) * 6.0) :
										temp1))),
							(((6.0 * temp3.b) < 1) ? (temp1 + (temp2 - temp1) * 6.0 * temp3.b) :
								(((2.0 * temp3.b) < 1) ? temp2 :
									(((3.0 * temp3.b) < 2) ? (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp3.b) * 6.0) :
										temp1)))
						];
					}

					return ret;
				},

				/**
				 * Converts hsv to rgb.
				 * @method HSVToRGB
				 * @param {number[]} hsv Input: [ h, s, v ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * v is in [0,   1]
				 * @return {number[]} Returns: [ r, g, b ], where
				 * r is in [0, 1]
				 * g is in [0, 1]
				 * b is in [0, 1]
				 * @member ns.util.colors
				 */
				HSVToRGB: function (hsv) {
					return this.HSLToRGB(this.HSVToHSL(hsv));
				},

				/**
				 * Converts rgb to hsv.
				 * @method HSVToRGB
				 * @param {number[]} rgb Input: [ r, g, b ], where
				 * r is in [0,   1]
				 * g is in [0,   1]
				 * b is in [0,   1]
				 * @return {number[]} Returns: [ h, s, v ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * v is in [0,   1]
				 * @member ns.util.colors
				 * @static
				 */
				RGBToHSV: function (rgb) {
					var min,
						max,
						delta,
						h,
						s,
						v,
						r = rgb[0],
						g = rgb[1],
						b = rgb[2];

					min = Math.min(r, Math.min(g, b));
					max = Math.max(r, Math.max(g, b));
					delta = max - min;

					h = 0;
					s = 0;
					v = max;

					if (delta > 0.00001) {
						s = delta / max;

						if (r === max) {
							h = (g - b) / delta;
						} else {
							if (g === max) {
								h = 2 + (b - r) / delta;
							} else {
								h = 4 + (r - g) / delta;
							}
						}

						h *= 60;

						if (h < 0) {
							h += 360;
						}
					}

					return [h, s, v];
				},

				/**
				 * Converts Converts hsv to hsl.
				 * @method HSVToHSL
				 * @param {number[]} hsv Input: [ h, s, v ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * v is in [0,   1]
				 * @return {number[]} Returns: [ h, s, l ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * l is in [0,   1]
				 * @member ns.util.colors
				 * @static
				 */
				HSVToHSL: function (hsv) {
					var max = hsv[2],
						delta = hsv[1] * max,
						min = max - delta,
						sum = max + min,
						halfSum = sum / 2,
						sDivisor = ((halfSum < 0.5) ? sum : (2 - max - min));

					return [hsv[0], ((0 === sDivisor) ? 0 : (delta / sDivisor)), halfSum];
				},

				/**
				 * Converts rgb to hsl
				 * @method RGBToHSL
				 * @param {number[]} rgb Input: [ r, g, b ], where
				 * r is in [0,   1]
				 * g is in [0,   1]
				 * b is in [0,   1]
				 * @return {number[]} Returns: [ h, s, l ], where
				 * h is in [0, 360]
				 * s is in [0,   1]
				 * l is in [0,   1]
				 * @member ns.util.colors
				 */
				RGBToHSL: function (rgb) {
					return this.HSVToHSL(this.RGBToHSV(rgb));
				}
			};
			}(window, window.document, ns));

/*global window, ns, define */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #jQuery Mobile mapping colors
 * Object maps color support object from TAU namespace to
 * jQuery Mobile namespace.
 * @class ns.jqm.colors
 */
(function (document) {
	"use strict";
				var eventType = ns.engine.eventType,
				$ = ns.jqm.jQuery,
				colors = {
					/**
					 * Initializes colors util in jQueryMobile namespace
					 */
					init: function () {
						if ($) {
							$.mobile.tizen.clrlib = colors;
						}
					},

					/**
					 * Destroys colors util in jQueryMobile namespace
					 */
					destroy: function () {
						document.removeEventListener(eventType.INIT, colors.init, false);
						document.removeEventListener(eventType.DESTROY, colors.destroy, false);
						if ($) {
							delete $.mobile.tizen.clrlib;
						}
						window.ns = null;
						$ = null;
						eventType = null;
						colors = null;
					}
				};

			// Listen when framework is ready
			document.addEventListener(eventType.INIT, colors.init, false);
			document.addEventListener(eventType.DESTROY, colors.destroy, false);

			}(window.document));

/*global define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Anchor Highlight Utility
 *
 * Utility enables highlight on clickable components.
 * @class ns.util.anchorHighlight
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Damian Osipiuk <d.osipiuk@samsung.com>
 * @author Konrad Lipner <k.lipner@samsung.com>
 */
(function (document, window, ns) {
	"use strict";
				/* anchorHighlightController.js
			 To prevent performance regression when scrolling,
			 do not apply hover class in anchor.
			 Instead, this code checks scrolling for time threshold and
			 decide how to handle the color.
			 When scrolling with anchor, it checks flag and decide to highlight anchor.
			 While it helps to improve scroll performance,
			 it lowers responsiveness of the element for 50msec.
			 */

			/**
			 * Touch start x
			 * @property {number} startX
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			var startX = 0,
				/**
				 * Touch start y
				 * @property {number} startY
				 * @member ns.util.anchorHighlight
				 * @private
				 * @static
				 */
				startY = 0,
				/**
				 * Touch target element
				 * @property {HTMLElement} target
				 * @member ns.util.anchorHighlight
				 * @private
				 * @static
				 */
				classes = {
					/**
					 * Class used to mark element as active
					 * @property {string} [classes.ACTIVE_LI="ui-li-active"]
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					ACTIVE_LI: "ui-li-active",
					/**
					 * Class used to mark button as active
					 * @property {string} [classes.ACTIVE_BTN="ui-btn-active"]
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					ACTIVE_BTN: "ui-btn-active",
					/**
					 * Class used to mark button as inactive
					 * @property {string} [classes.INACTIVE_BTN="ui-btn-inactive"]
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					INACTIVE_BTN: "ui-btn-inactive",
					/**
					 * Class used to select button
					 * @property {string} [classes.BUTTON="ui-btn"] btn
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					BUTTON: "ui-btn",
					/**
					 * Class used to select button in header (old notation)
					 * @property {string} [classes.HEADER_BUTTON="ui-header-btn"] btn
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					HEADER_BUTTON: "ui-header-btn",
					/**
					 * Class used to select anchor in tabbar widget
					 * @property {string} [classes.TABBAR_ANCHOR="ui-tabbar-anchor"] anchor
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					TABBAR_ANCHOR: "ui-tabbar-anchor",
					/**
					 * Class used to select navigation item
					 * @property {string} [classes.NAVIGATION_BUTTON="ui-navigation-item"] btn
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					NAVIGATION_BUTTON: "ui-navigation-item"
				},
				events = {
					ACTIVE_LI: "anchorhighlightactiveli"
				},
				/**
				 * Alias for class {@link ns.util.selectors}
				 * @property {Object} selectors
				 * @member ns.util.anchorHighlight
				 * @private
				 * @static
				 */
				selectors = ns.util.selectors,
				/**
				 * Alias for class {@link ns.event}
				 * @property {Object} event
				 * @member ns.util.anchorHighlight
				 * @private
				 * @static
				 */
				eventUtil = ns.event,

				// cache function
				abs = Math.abs,

				/**
				 * Get closest li element
				 * @method detectLiElement
				 * @param {HTMLElement} target
				 * @return {HTMLElement}
				 * @member ns.util.anchorHighlight
				 * @private
				 * @static
				 */
				detectLiElement = function (target) {
					return selectors.getClosestByTag(target, "li");
				},

				anchorHighlight = {
					/**
					 * Object with default options
					 * @property {Object} options
					 * Threshold after which didScroll will be set
					 * @property {number} [options.scrollThreshold=10]
					 * Time to wait before adding activeClass
					 * @property {number} [options.addActiveClassDelay=50]
					 * Time to stay activeClass after touch end
					 * @property {number} [options.keepActiveClassDelay=100]
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					options: {
						scrollThreshold: 10,
						addActiveClassDelay: 50,
						keepActiveClassDelay: 100
					},
					_startTime: 0,
					_startRemoveTime: 0,
					// inform that touch was ended
					_touchEnd: false,
					_liTarget: null,

					/**
					 * Touch button target element
					 * @property {HTMLElement} buttonTarget
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					_target: null,
					/**
					 * Did page scrolled
					 * @property {boolean} didScroll
					 * @member ns.util.anchorHighlight
					 * @private
					 * @static
					 */
					_didScroll: false,
					_buttonTarget: null,
					// inform that animation of button's activation was ended
					_activeAnimationFinished: false,
					//cache function
					_requestAnimationFrame: ns.util.windowRequestAnimationFrame
				},

				// cache function
				slice = Array.prototype.slice;


			/**
			 * Get closest highlightable element
			 * @method detectHighlightTarget
			 * @param {HTMLElement} target
			 * @return {HTMLElement}
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function detectHighlightTarget(target) {
				return selectors.getClosestBySelector(target, "a, label");
			}

			/**
			 * Get closest button element
			 * @method detectLiElement
			 * @param {HTMLElement} target
			 * @return {HTMLElement}
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function detectBtnElement(target) {
				return selectors.getClosestByClass(target, classes.BUTTON) ||
					selectors.getClosestByClass(target, classes.HEADER_BUTTON) ||
					selectors.getClosestByClass(target, classes.NAVIGATION_BUTTON) ||
					selectors.getClosestByClass(target, classes.TABBAR_ANCHOR);
			}

			/**
			 * Clear active class on button
			 * @method clearBtnActiveClass
			 * @param {Event} event
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function clearBtnActiveClass(event) {
				var target = event.target,
					classList = target.classList;
				// if this is callback of activate animation and

				if (classList.contains(classes.ACTIVE_BTN) && !classList.contains(classes.INACTIVE_BTN)) {
					// set that animation was ended (used in touch end)
					anchorHighlight._activeAnimationFinished = true;

					// if touch end previously
					if (anchorHighlight._touchEnd || target !== anchorHighlight._buttonTarget) {
						// start inactivate animation
						classList.add(classes.INACTIVE_BTN);
					}
				} else {
					//when target of animationend event is child of active element instead of active element
					// itself
					if (!classList.contains(classes.ACTIVE_BTN) &&
						!classList.contains(classes.INACTIVE_BTN)) {
						target.parentNode.classList.remove(classes.ACTIVE_BTN);
						target.parentNode.classList.remove(classes.INACTIVE_BTN);
					}
					// this is callback for inactive animation end
					classList.remove(classes.INACTIVE_BTN);
					classList.remove(classes.ACTIVE_BTN);
				}
			}

			/**
			 * Add inactive class on touch end
			 * @method addButtonInactiveClass
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function addButtonInactiveClass() {
				if (anchorHighlight._buttonTarget) {
					anchorHighlight._buttonTarget.classList.add(classes.INACTIVE_BTN);
				}
			}

			/**
			 * Add active class on touch end
			 * @method addButtonActiveClass
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function addButtonActiveClass() {
				anchorHighlight._buttonTarget.classList.add(classes.ACTIVE_BTN);
				anchorHighlight._activeAnimationFinished = false;
			}

			/**
			 * Clear classes on page or popup hide
			 * @method hideClear
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function hideClear() {
				var btnTarget = anchorHighlight._buttonTarget;

				if (btnTarget) {
					btnTarget.classList.remove(classes.ACTIVE_BTN);
					btnTarget.classList.remove(classes.INACTIVE_BTN);
				}
				if (anchorHighlight._target) {
					anchorHighlight._target.classList.remove(classes.ACTIVE_LI);
				}
			}

			/**
			 * Add active class to touched element
			 * @method addActiveClass
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function addActiveClass() {
				var btnTargetClassList,
					dTime;

				if (anchorHighlight._startTime) {
					dTime = Date.now() - anchorHighlight._startTime;

					if (dTime > anchorHighlight.options.addActiveClassDelay) {
						anchorHighlight._startTime = 0;
						anchorHighlight._buttonTarget = detectBtnElement(anchorHighlight._target);
						anchorHighlight._target = detectHighlightTarget(anchorHighlight._target);
						if (!anchorHighlight._didScroll) {
							anchorHighlight._liTarget = anchorHighlight._detectLiElement(anchorHighlight._target);
							if (anchorHighlight._liTarget) {
								anchorHighlight._liTarget.classList.add(classes.ACTIVE_LI);
								eventUtil.trigger(anchorHighlight._liTarget, events.ACTIVE_LI, {});
							}
							anchorHighlight._liTarget = null;
							if (anchorHighlight._buttonTarget) {
								btnTargetClassList = anchorHighlight._buttonTarget.classList;
								btnTargetClassList.remove(classes.ACTIVE_BTN);
								btnTargetClassList.remove(classes.INACTIVE_BTN);
								anchorHighlight._requestAnimationFrame(addButtonActiveClass);
							}
						}
					} else {
						anchorHighlight._requestAnimationFrame(addActiveClass);
					}
				}
			}

			/**
			 * Get all active elements
			 * @method getActiveElements
			 * @return {Array}
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function getActiveElements() {
				return slice.call(document.getElementsByClassName(classes.ACTIVE_LI));
			}

			/**
			 * Remove active class from current active objects
			 * @method clearActiveClass
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function clearActiveClass() {
				var activeA = getActiveElements(),
					activeALength = activeA.length,
					i = 0;

				for (; i < activeALength; i++) {
					activeA[i].classList.remove(classes.ACTIVE_LI);
				}
			}

			/**
			 * Remove active class from active elements
			 * @method removeActiveClass
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function removeActiveClassLoop() {
				var dTime = Date.now() - anchorHighlight._startRemoveTime;

				if (dTime > anchorHighlight.options.keepActiveClassDelay) {
					// after touchend
					clearActiveClass();
				} else {
					anchorHighlight._requestAnimationFrame(removeActiveClassLoop);
				}
			}

			/**
			 * Function invoked during touch move (and mouse)
			 * @method touchmoveHandler
			 * @param {Event} event
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function touchmoveHandler(event) {
				var touch = event.touches[0],
					scrollThreshold = anchorHighlight.options.scrollThreshold;

				// if move looks like scroll
				if (!anchorHighlight._didScroll &&
					// if move is bigger then threshold
					(abs(touch.clientX - startX) > scrollThreshold ||
					abs(touch.clientY - startY) > scrollThreshold)) {
					anchorHighlight._startTime = 0;
					// we clear active classes
					anchorHighlight._requestAnimationFrame(clearActiveClass);
					anchorHighlight._didScroll = true;
				}
			}

			/**
			 * Function invoked after touch start (and mouse)
			 * @method touchstartHandler
			 * @param {Event} event
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function touchstartHandler(event) {
				var touches = event.touches,
					pointer = (!touches) ? event : // mouse event
						(touches.length === 1) ? touches[0] : null; // touch event

				if (pointer) {
					anchorHighlight._didScroll = false;
					startX = pointer.clientX;
					startY = pointer.clientY;
					anchorHighlight._target = event.target;
					anchorHighlight._startTime = Date.now();
					anchorHighlight._startRemoveTime = 0;
					anchorHighlight._requestAnimationFrame(addActiveClass);
					anchorHighlight._touchEnd = false;
				}
			}


			/**
			 * Function invoked after touch (and mouse)
			 * @method touchendHandler
			 * @param {Event} event
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function touchendHandler(event) {
				anchorHighlight._startRemoveTime = event.timeStamp;

				if (!event.touches || event.touches && event.touches.length === 0) {
					if (!anchorHighlight._didScroll) {
						anchorHighlight._startTime = 0;
						anchorHighlight._requestAnimationFrame(removeActiveClassLoop);
					}
					// if we finished activate animation then start inactive animation
					if (anchorHighlight._activeAnimationFinished) {
						anchorHighlight._requestAnimationFrame(addButtonInactiveClass);
					}
					anchorHighlight._didScroll = false;
					anchorHighlight._touchEnd = true;
				}
			}

			/**
			 * Function invoked after visibilitychange event
			 * @method checkPageVisibility
			 * @member ns.util.anchorHighlight
			 * @private
			 * @static
			 */
			function checkPageVisibility() {
				/* istanbul ignore if  */
				if (document.visibilityState === "hidden") {
					anchorHighlight._removeActiveClassLoop();
				}
			}

			ns.util.anchorHighlight = anchorHighlight;
			anchorHighlight.enable = enable;
			anchorHighlight.disable = disable;
			anchorHighlight._clearActiveClass = clearActiveClass;
			anchorHighlight._detectHighlightTarget = detectHighlightTarget;
			anchorHighlight._detectBtnElement = detectBtnElement;
			anchorHighlight._clearBtnActiveClass = clearBtnActiveClass;
			anchorHighlight._removeActiveClassLoop = removeActiveClassLoop;
			anchorHighlight._addButtonInactiveClass = addButtonInactiveClass;
			anchorHighlight._addButtonActiveClass = addButtonActiveClass;
			anchorHighlight._hideClear = hideClear;
			anchorHighlight._addActiveClass = addActiveClass;
			anchorHighlight._detectLiElement = detectLiElement;
			anchorHighlight._touchmoveHandler = touchmoveHandler;
			anchorHighlight._touchendHandler = touchendHandler;
			anchorHighlight._touchstartHandler = touchstartHandler;
			anchorHighlight._checkPageVisibility = checkPageVisibility;
			anchorHighlight._hideClear = hideClear;
			anchorHighlight._clearBtnActiveClass = clearBtnActiveClass;

			/**
			 * Bind events to document
			 * @method enable
			 * @member ns.util.anchorHighlight
			 * @static
			 */
			function enable() {
				document.addEventListener("touchstart", anchorHighlight._touchstartHandler, false);
				document.addEventListener("touchend", anchorHighlight._touchendHandler, false);
				document.addEventListener("touchmove", anchorHighlight._touchmoveHandler, false);
				// for TAU in browser
				document.addEventListener("mousedown", anchorHighlight._touchstartHandler, false);
				document.addEventListener("mouseup", anchorHighlight._touchendHandler, false);

				document.addEventListener("visibilitychange", anchorHighlight._checkPageVisibility, false);
				document.addEventListener("pagehide", anchorHighlight._hideClear, false);
				document.addEventListener("popuphide", anchorHighlight._hideClear, false);
				document.addEventListener("animationend", anchorHighlight._clearBtnActiveClass, false);
				document.addEventListener("animationEnd", anchorHighlight._clearBtnActiveClass, false);
				document.addEventListener("webkitAnimationEnd", anchorHighlight._clearBtnActiveClass,
					false);
			}

			/**
			 * Unbinds events from document.
			 * @method disable
			 * @member ns.util.anchorHighlight
			 * @static
			 */
			function disable() {
				document.removeEventListener("touchstart", anchorHighlight._touchstartHandler, false);
				document.removeEventListener("touchend", anchorHighlight._touchendHandler, false);
				document.removeEventListener("touchmove", anchorHighlight._touchmoveHandler, false);
				// for TAU in browser
				document.removeEventListener("mousedown", anchorHighlight._touchstartHandler, false);
				document.removeEventListener("mouseup", anchorHighlight._touchendHandler, false);

				document.removeEventListener("visibilitychange", anchorHighlight._checkPageVisibility,
					false);
				document.removeEventListener("pagehide", anchorHighlight._hideClear, false);
				document.removeEventListener("popuphide", anchorHighlight._hideClear, false);
				document.removeEventListener("animationend", anchorHighlight._clearBtnActiveClass, false);
				document.removeEventListener("animationEnd", anchorHighlight._clearBtnActiveClass, false);
				document.removeEventListener("webkitAnimationEnd", anchorHighlight._clearBtnActiveClass,
					false);
			}

			enable();

			}(document, window, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Grid Utility
 * Object helps creating grids.
 * @class ns.util.grid
 */
(function (ns) {
	"use strict";
				/**
			 * Local alias for ns.util.selectors
			 * @property {Object} selectors Alias for {@link ns.util.selectors}
			 * @member ns.util.grid
			 * @static
			 * @private
			 */
			var selectors = ns.util.selectors,
				/**
				 * Alias to Array.slice method
				 * @method slice
				 * @member ns.util.grid
				 * @private
				 * @static
				 */
				slice = [].slice,
				/**
				 * grid types
				 * @property {Array} gridTypes
				 * @member ns.util.grid
				 * @static
				 * @private
				 */
				gridTypes = [
					null,
					"solo", //1
					"a",	//2
					"b",	//3
					"c",	//4
					"d"	//5
				];

			/**
			 * Add classes on the matched elements
			 * @method setClassOnMatches
			 * @param {HTMLElementCollection} elements
			 * @param {string} selector
			 * @param {string} className
			 * @private
			 * @member ns.util.grid
			 * @static
			 */
			function setClassOnMatches(elements, selector, className) {
				elements.forEach(function (item) {
					if (selectors.matchesSelector(item, selector)) {
						item.classList.add(className);
					}
				});
			}

			ns.util.grid = {
				/**
				 * make css grid
				 * @method makeGrid
				 * @param {HTMLElement} element
				 * @param {?string} [gridType="a"]
				 * @static
				 * @member ns.util.grid
				 */
				makeGrid: function (element, gridType) {
					var gridClassList = element.classList,
						kids = slice.call(element.children),
						iterator;

					if (!gridType) {
						gridType = gridTypes[kids.length];
						if (!gridType) {
							//if gridType is not defined in gritTypes
							//make it grid type "a""
							gridType = "a";
							iterator = 2;
							gridClassList.add("ui-grid-duo");
						}
					}
					if (!iterator) {
						//jquery grid doesn't care if someone gives non-existing gridType
						iterator = gridTypes.indexOf(gridType);
					}

					gridClassList.add("ui-grid-" + gridType);

					setClassOnMatches(kids, ":nth-child(" + iterator + "n+1)", "ui-block-a");

					if (iterator > 1) {
						setClassOnMatches(kids, ":nth-child(" + iterator + "n+2)", "ui-block-b");
					}
					if (iterator > 2) {
						setClassOnMatches(kids, ":nth-child(" + iterator + "n+3)", "ui-block-c");
					}
					if (iterator > 3) {
						setClassOnMatches(kids, ":nth-child(" + iterator + "n+4)", "ui-block-d");
					}
					if (iterator > 4) {
						setClassOnMatches(kids, ":nth-child(" + iterator + "n+5)", "ui-block-e");
					}
				}
			};
			}(ns));

/*global window, ns, define, Object, Element, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Data Attributes Utility
 * Object menages data attributes
 * @class ns.util.data
 */
(function (window, document, ns) {
	"use strict";
				var hashMap = {},
				eventUtils = ns.event,
				objectUtils = ns.util.object,
				body = document.body,
				/**
				 * Return hash for object
				 * @method fetchDom
				 * @param {HTMLElement} element
				 * @param {string} key
				 * @return {?Object}
				 * @member ns.util.data
				 * @static
				 * @private
				 */
				fetchDom = function (element, key) {
					var dataKey = "data-" + key,
						data,
						result;

					if (element.hasAttribute(dataKey)) {
						data = element.getAttribute(dataKey);
						try {
							result = JSON.parse(data);
						} catch (ignore) {
						}
					}
					return result;
				},
				/**
				 * Remove attribute from element
				 * @method removeDom
				 * @param {HTMLElement} element
				 * @param {string} key
				 * @member ns.util.data
				 * @static
				 * @private
				 */
				removeDom = function (element, key) {
					var dataKey = "data-" + key;

					if (element.hasAttribute(dataKey)) {
						element.removeAttribute(dataKey);
					}
				},
				/**
				 * Return hash for object
				 * @method hashObject
				 * @param {*} value
				 * @return {string}
				 * @member ns.util.data
				 * @static
				 * @private
				 */
				hashObject = function (value) {
					var hash;

					if (value === undefined ||
						value === null ||
						value === false) {
						throw "Hashed object/primitive can not be undefined, null or false";
					}

					if (value instanceof Element && value.hasAttribute("data-ns-hash")) {
						return value.getAttribute("data-ns-hash");
					}

					if (value instanceof Object) {
						value.__tauHash = value.__tauHash || ns.getUniqueId();
					}
					hash = (typeof value) + "-" + (value instanceof Object ?
						value.__tauHash : value.toString());

					if (value instanceof Element) {
						value.setAttribute("data-ns-hash", hash);
					}
					return hash;
				};

			ns.util.data = {
				/**
				 * Set value for element
				 * @method set
				 * @param {HTMLElement} element
				 * @param {string} key
				 * @param {*} value
				 * @return {*}
				 * @member ns.util.data
				 * @static
				 */
				set: function (element, key, value) {
					var hash = hashObject(element);

					if (!hash) {
						return false;
					}

					if (!hashMap[hash]) {
						hashMap[hash] = {};
					}

					hashMap[hash][key] = value;

					if (element instanceof Element) {
						eventUtils.trigger(element, "setData", {
							"key": key,
							"value": value
						});
					}
					eventUtils.trigger(body, "globalSetData", {
						"element": element,
						"key": key,
						"value": value
					});

					return value;
				},

				/**
				 * Get value for element
				 * @method get
				 * @param {HTMLElement} element
				 * @param {string} key
				 * @param {?*} defaultValue
				 * @return {Mixed}
				 * @member ns.util.data
				 * @static
				 */
				get: function (element, key, defaultValue) {
					var hash = hashObject(element),
						value;

					if (hash) {
						if (hashMap[hash] && hashMap[hash][key] !== undefined) {
							value = hashMap[hash][key];
						}

						if (element instanceof Element) {
							if (value === undefined) {
								value = fetchDom(element, key);
								// pass it to memory HashMap
								hashMap[hash] = hashMap[hash] || {};
								hashMap[hash][key] = hashMap[hash][key] || value;
							}
							eventUtils.trigger(element, "getData", {
								"key": key,
								"value": value
							});
						}
						eventUtils.trigger(body, "globalGetData", {
							"element": element,
							"key": key,
							"value": value
						});

						return value;
					}

					return defaultValue;
				},

				/**
				 * remove value for element
				 * @method remove
				 * @param {HTMLElement} element
				 * @param {string} key
				 * @return {boolean}
				 * @member ns.util.data
				 * @static
				 */
				remove: function (element, key) {
					var hash = hashObject(element),
						value;

					if (hash && hashMap[hash] && hashMap[hash][key] !== undefined) {
						value = hashMap[hash][key];

						// Delete keyword has a performance impact on the execution, that's why we assign
						// undefined
						hashMap[hash][key] = undefined;

						// If any property is defined we cannot clear the hashMap[hash]
						if (objectUtils.hasPropertiesOfValue(hashMap[hash], undefined)) {
							hashMap[hash] = undefined;
						}

						if (element instanceof Element) {
							removeDom(element, key);
							eventUtils.trigger(element, "removeData", {
								"key": key,
								"value": value
							});
						}
						eventUtils.trigger(body, "globalRemoveData", {
							"element": element,
							"key": key,
							"value": value
						});

						return true;
					}
					return false;
				}
			};
			}(window, window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, define, ns */
/**
 * #Date Utility
 * Object supports work with date and time
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @class ns.util.date
 */
(function (ns) {
	"use strict";
				var timeRegex = /([\-0-9.]*)(ms|s)?/i,
				date = {
					/**
					 * Convert string time length to miliseconds
					 * Note: this was implemented only for animation package
					 * and the string input should be conforming to css <time>
					 * unit definition (ref: https://developer.mozilla.org/en-US/docs/Web/CSS/time)
					 * If a different format or more functionality needs to be implemented, please
					 * change this function and usage cases in animation package accordingly
					 * @method convertToMiliseconds
					 * @param {string} string
					 * @return {number}
					 * @static
					 * @member ns.util.date
					 */
					convertToMiliseconds: function (string) {
						var parsed = string.match(timeRegex),
							miliseconds = 0,
							parsedNumber;

						if (parsed.length === 3) {
							parsedNumber = parseFloat(parsed[1]) || 0;
							if (parsed[2] === "ms") {
								miliseconds = parsedNumber;
							} else if (parsed[2] === "s") {
								miliseconds = parsedNumber * 1000;
							}
						}
						return miliseconds;
					}
				};

			ns.util.date = date;
			}(ns));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Deferred When Utility
 * Class groups many deferred object to one.
 * @class ns.util.deferredWhen
 */
(function (window, document, ns) {
	"use strict";
				var when = function (subordinate /* , ..., subordinateN */) {
				var i = 0,
					resolveValues = [].slice.call(arguments),
					length = resolveValues.length,

					/**
					 * The count of uncompleted subordinates
					 * @property {number} remaining
					 * @member ns.util.deferredWhen
					 * @private
					 */
					remaining = length !== 1 || (subordinate && (typeof subordinate.promise === "function")) ? length : 0,

					/**
					 * The master Deferred. If resolveValues consist of only
					 * a single Deferred, just use that.
					 * @property {ns.util.deferred} deferred
					 * @member ns.util.deferredWhen
					 * @private
					 */
					deferred = remaining === 1 ? subordinate : new ns.util.deferred(),

					/**
					 * Update function for both resolve and progress values
					 * @method updateFunc
					 * @param {number} i
					 * @param {Array} contexts
					 * @param {Array} values
					 * @return {Function} representing the current state
					 * "pending" | "resolved" | "rejected"
					 * @member ns.util.deferredWhen
					 * @private
					 */
					updateFunc = function (i, contexts, values) {
						return function (value) {
							contexts[i] = this;
							values[i] = arguments.length > 1 ? [].slice.call(arguments) : value;
							if (values === progressValues) {
								deferred.notifyWith(contexts, values);

							} else if (!(--remaining)) {
								deferred.resolveWith(contexts, values);
							}
						};
					},

					progressValues,
					progressContexts,
					resolveContexts;

				// add listeners to Deferred subordinates; treat others as resolved
				if (length > 1) {
					progressValues = [];
					progressValues.length = length;
					progressContexts = [];
					progressContexts.length = length;
					resolveContexts = [];
					resolveContexts.length = length;
					for (; i < length; i++) {
						if (resolveValues[i] && (typeof resolveValues[i].promise === "function")) {
							resolveValues[i].promise()
								.done(updateFunc(i, resolveContexts, resolveValues))
								.fail(deferred.reject)
								.progress(updateFunc(i, progressContexts, progressValues));
						} else {
							--remaining;
						}
					}
				}

				// if we're not waiting on anything, resolve the master
				if (!remaining) {
					deferred.resolveWith(resolveContexts, resolveValues);
				}

				return deferred.promise();
			};

			ns.util.deferredWhen = when;

			}(window, window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window: false, define: false, Math: false, ns */
/**
 * #Bezier Curves Utility
 * Class supports calculating Bezier Curves.
 * @class ns.util.bezierCurve
 */
(function (ns) {
	"use strict";
			/**
		 * Store constant value for half PI
		 * @property {number} HALF_PI
		 * @member ns.util.bezierCurve
		 * @private
		 * @static
		 */
		var HALF_PI = Math.PI / 2,
			/**
			 * Store constant value for DEFAULT_STEP
			 * @property {number} DEFAULT_STEP
			 * @member ns.util.bezierCurve
			 * @private
			 * @static
			 */
			DEFAULT_STEP = 0.001,
			BezierCurve,
			/**
			 * Calculates the arc length
			 * @method arcLength3d
			 * @param {Array} p0
			 * @param {Array} p1
			 * @return {number}
			 * @member ns.util.bezierCurve
			 * @private
			 * @static
			 */
			arcLength3d = function (p0, p1) {
				var d = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];

				return Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
			};

		BezierCurve = function () {
			return this;
		};
		BezierCurve.prototype = {
			points: [],
			step: DEFAULT_STEP,
			length: 0,
			levels: [],
			/**
			 * Init BezierCurve
			 * @method init
			 * @param {Object} data
			 * @return {ns.util.BezierCurve} self
			 * @member ns.util.bezierCurve
			 */
			init: function (data) {
				this.points = data.points;
				this.step = data.step || DEFAULT_STEP;
				this.length = this.calculateTotalLength();
				this.levels = this.calculateLevel(data.maxLevel) || [];
				return this;
			},
			/**
			 * Calculate levels
			 * @method calculateLevel
			 * @param {?number} [maxLevel=null]
			 * @return {?Array} levels
			 * @member ns.util.bezierCurve
			 */
			calculateLevel: function (maxLevel) {
				var totalLength = this.length,
					interval = totalLength / maxLevel,
					levels = [],
					i;

				if (!maxLevel) {
					return null;
				}

				for (i = 0; i < maxLevel; i += 1) {
					levels[maxLevel - i] = this.getPercent(0, interval * i);
				}

				return levels;
			},
			/**
			 * Calculate total length
			 * @method calculateTotalLength
			 * @return {number}
			 * @member ns.util.bezierCurve
			 */
			calculateTotalLength: function () {
				var step = this.step,
					current = this.getPosition(0),
					last = current,
					length = 0,
					percent;

				for (percent = step; percent <= 1; percent += step) {
					current = this.getPosition(percent);
					length += arcLength3d(last, current);
					last = current;
				}
				return length;
			},
			/**
			 * Get position
			 * @method getPosition
			 * @param {number} percent
			 * @return {Array}
			 * @member ns.util.bezierCurve
			 */
			getPosition: function (percent) {
				var points = this.points,
					getValue = function (p1, c1, c2, p2, t) {
						return Math.pow(1 - t, 3) * p1 +
							3 * t * Math.pow(1 - t, 2) * c1 +
							3 * Math.pow(t, 2) * (1 - t) * c2 +
							Math.pow(t, 3) * p2;
					},
					result = [
						getValue(points[0][0], points[1][0], points[2][0], points[3][0], percent),
						getValue(points[0][2], points[1][2], points[2][2], points[3][2], percent)
					];

				return [result[0], 0, result[1]];
			},
			/**
			 * Get percent
			 * @method getPercent
			 * @param {number} [start=0]
			 * @param {?number} interval
			 * @return {number}
			 * @member ns.util.bezierCurve
			 */
			getPercent: function (start, interval) {
				var step = this.step,
					current,
					last,
					targetLength,
					length = 0,
					percent;

				start = start || 0;
				current = this.getPosition(start);
				last = current;
				targetLength = start + interval;
				for (percent = start + step; percent <= 1; percent += step) {
					current = this.getPosition(percent);
					length += arcLength3d(last, current);
					if (length >= targetLength) {
						return percent;
					}
					last = current;
				}
				return 1;
			},
			/**
			 * Get angle
			 * @method getAngle
			 * @param {number} percent
			 * @return {number}
			 * @member ns.util.bezierCurve
			 */
			getAngle: function (percent) {
				var points = this.points,
					getTangent = function (p1, c1, c2, p2, t) {
						return 3 * t * t * (-p1 + 3 * c1 - 3 * c2 + p2) + 6 * t * (p1 - 2 * c1 + c2) + 3 * (-p1 + c1);
					},
					tx = getTangent(points[0][0], points[1][0], points[2][0], points[3][0], percent),
					ty = getTangent(points[0][2], points[1][2], points[2][2], points[3][2], percent);

				return Math.atan2(tx, ty) - HALF_PI;
			}
		};
		ns.util.bezierCurve = new BezierCurve();

		}(ns));

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Animation namespace
 * Namespace for animation utilities
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @class ns.util.anim
 */
(function (ns) {
	"use strict";
				ns.util.anim = ns.util.anim || {};
			}(ns));

/*global window, define, ns */
/*jslint nomen: true, plusplus: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Keyframes
 *
 * Keyframes class for easy keyframe css syntax creation and
 * managing. Each frame is specified as an element of an array
 * with size 100.
 *
 * @example

 *        <div id="test"
 *                style="width: 10px; height: 10px; background: red;"></div>
 *
 *        <script>
 *        var frames = [{ "background-color": "red" }],
 *            anim,
 *            keys;
 *
 *        frames[100] = {"background-color": "blue"};
 *        keys = new tau.util.anim.Keyframes(frames);
 *        anim = new tau.util.anim.Animation({
 *				element: document.getElementById("test"),
 *				fillMode: "both",
 *				delay: "2s",
 *				duration: "5s",
 *				steps: keys,
 *				onEnd: function () {
 *					console.log("Yay, finished!");
 *				}
 *			});
 *        </script>
 *
 * @class ns.util.anim.Keyframes
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function (document) {
	"use strict";
				// Reference to stylesheet
			var styleContainer = null,
				/**
				 * Helper function for generating css string from
				 * optimized (most usages will use maybe up to 3-5
				 * array elements, when it has 100) but thats not
				 * important for the moment
				 * frames array
				 * @param {string} prefix
				 * @param {string} name
				 * @param {Array} steps
				 * @return {string}
				 * @private
				 * @static
				 * @method keyframesToString
				 * @member ns.utils.anim.Keyframes
				 */
				// @TODO the steps array could be probably be more
				keyframesToString = function (prefix, name, steps) {
					var buff = "@" + prefix + "keyframes " + name + " {",
						i,
						l,
						prop,
						step;

					for (i = 0, l = steps.length; i < l; ++i) {
						step = steps[i];
						if (!step) {
							continue;
						}
						buff += i + "% { ";
						for (prop in step) {
							if (step.hasOwnProperty(prop)) {
								buff += prop + ": " + step[prop] + "; ";
							}
						}
						buff += "} ";
					}
					buff += "} ";
					return buff;
				},

				cssPropertyPrefix = ns.support.cssAnimationPrefix,

				Keyframes = function (steps) {
					var id = ns.getUniqueId(),
						element;

					if (!styleContainer) {
						element = document.createElement("style");
						// a text node hack, it forces the browser
						// to create a stylesheet object in the
						// HTMLStyleElement object, which we can
						// then use
						element.appendChild(document.createTextNode(""));
						document.head.appendChild(element);
						styleContainer = element.sheet;
					}
					styleContainer.insertRule(keyframesToString(cssPropertyPrefix, id, steps),
						0);
					/**
					 * Keyframes rule reference
					 * @property {CSSRule} keyframes
					 * @readonly
					 */
					this.keyframes = styleContainer.rules[0];
					/**
					 * Keyframes name
					 * @property {string} id
					 * @readonly
					 */
					this.id = id;
				};

			/**
			 * Destroys keyframes and removes css references from stylesheet
			 * @method destroy
			 * @member ns.util.anim.Keyframes
			 */
			Keyframes.prototype.destroy = function () {
				var keyframes = this.keyframes,
					stylesheet = keyframes.parentStyleSheet,
					rules = stylesheet.rules,
					i,
					l;

				// no other way for removal than with index
				// and since it changes we have to search for it
				// :(
				for (i = 0, l = rules.length; i < l; ++i) {
					if (rules[i] === keyframes) {
						stylesheet.deleteRule(i);
						break;
					}
				}
			};

			ns.util.anim.Keyframes = Keyframes;
			}(window.document));

/*global window, define, ns */
/*jslint nomen: true, plusplus: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Animation
 *
 * Animation class for easy animations of elements. There can be
 * multiple animations on one element but in such case the usage
 * of tau.util.anim.Chain is preferred.
 *
 * ## Usage example
 *
 * @example
 *
 *        <div id="test"
 *                style="width: 10px; height: 10px; background: red;"></div>
 *
 *        <script>
 *            var a = new tau.util.anim.Animation({
 *				element: document.getElementById("test"),
 *				fillMode: "both",
 *				delay: "2s",
 *				duration: "5s",
 *				from: {
 *					"background-color": "red"
 *				},
 *				to: {
 *					"background-color": "blue"
 *				},
 *				onEnd: function () {
 *					console.log("Yay, finished!");
 *				}
 *			});
 *        </script>
 *
 * @class ns.util.anim.Animation
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function () {
	"use strict";
	
			var objectUtils = ns.util.object,
				Keyframes = ns.util.anim.Keyframes,
				CSSUtils = ns.util.DOM,
				dateUtils = ns.util.date,
				cssPropertyPrefix = ns.support.cssAnimationPrefix,

				/**
				 * Simple helper for using trim in Array.map() function
				 * @param {string} string
				 * @return {string}
				 * @private
				 * @static
				 * @method trim
				 * @member ns.util.anim.Animation
				 */
				trim = function (string) {
					return string.trim();
				},

				/**
				 * Helper for fetching animation index in animation list
				 * @param {string|string[]} props
				 * @param {string} name
				 * @return {string}
				 * @private
				 * @static
				 * @member ns.util.anim.Animation
				 */
				getAnimationIndex = function (props, name) {
					if (typeof props === "string") {
						props = props.split(",").map(trim);
					}
					return props.indexOf(name);
				},

				eventPrefix = (cssPropertyPrefix || "").replace(/\-/gi, ""),
				endEventName = eventPrefix.length > 0 ? eventPrefix +
				"AnimationEnd" : "animationEnd",
				// paused state flag
				PAUSED = 0,
				// playing state flag
				PLAYING = 1,
				// finished state flag
				FINISHED = 2,
				// alias for function string for typeof conditionals
				TYPE_FUNCTION = "function",
				/**
				 * Animation end handler
				 * @param {ns.util.anim.Animation} self
				 * @param {Event} event
				 * @private
				 * @static
				 * @member ns.util.anim.Animation
				 */
				handleEnd = function (self, event) {
					var options = self.options,
						element = options.element,
						onEnd = options.onEnd,
						onPause = options.onPause;

					if (event.animationName === self.keyframes.id) {
						switch (self.state) {
							case PLAYING:
								self.state = FINISHED;
								if (typeof onEnd === TYPE_FUNCTION) {
									onEnd(self, element, event);
								}
								break;
							case PAUSED:
								if (typeof onPause === TYPE_FUNCTION) {
									onPause(self, element, event);
								}
								break;
						}
					}
				},
				/**
				 * Helper for playing/pausing
				 * @param {ns.util.anim.Animation} self
				 * @param {string} state
				 * @return {ns.util.anim.Animation}
				 * @private
				 * @static
				 * @member ns.util.anim.Animation
				 */
				changeState = function (self, state) {
					var options,
						element = null,
						onPlay = null,
						style,
						keyframes,
						propString,
						propsArray,
						index;

					if (!self._applied) { // !set before keyframe fetch
						self._apply();
					}

					options = self.options;
					element = options.element;
					onPlay = options.onPlay;
					style = element.style;
					keyframes = self.keyframes;
					propString = style.getPropertyValue(cssPropertyPrefix +
						"animation-play-state");
					propsArray = (propString && propString.split(",").map(trim)) || [];
					index = keyframes ? getAnimationIndex(
						style.getPropertyValue(cssPropertyPrefix +
							"animation-name"),
						keyframes.id
					) : -1;

					if (index > -1) {
						propsArray[index] = state || "running";
						style.setProperty(cssPropertyPrefix +
							"animation-play-state", propsArray.join(","));
						self.state = PLAYING;
						if (typeof onPlay === TYPE_FUNCTION) {
							window.clearTimeout(self.playTimer);
							self.playTimer = window.setTimeout(function () {
								onPlay(self, element);
							}, dateUtils.convertToMiliseconds(options.delay));
						}
					}
					return self;
				},
				/**
				 * Constructor for Animation object
				 * @param {Object} options
				 * @param {HTMLElement} options.element The animated element
				 * @param {Object|null} [options.from=null] The starting step, this can be defined later
				 * @param {Object|null} [options.to=null]  The finishing step, this can also be defined later
				 * @param {Object[]} [options.steps=Array(0)] Animation steps, when advanced keying is required, the array must have 100 elements, which are percentages of the timeline (animation duration)
				 * @param {string} [options.duration="0"] The duration of the animation
				 * @param {string} [options.direction="normal"] The direction of the animation (for possible values, refer to CSS Animation spec)
				 * @param {string} [options.delay="0"] The delay of the animation. Please remember when using ns.util.anim.Chain with concurrent option to false, the of subsequent animations will be modified
				 * @param {string} [options.fillMode="none"] The fill mode of the animations (for possible values, refer to CSS Animation spec)
				 * @param {string} [options.timingFunction="ease"] Chooses the timing function for the css animation
				 * @param {boolean} [options.autoPlay=false] Defines if the animation will start after definition
				 * @constructor
				 * @member ns.util.anim.Animation
				 */
				Animation = function (options) {
					var self = this,
						/**
						 * @property {Object} options
						 * @property {HTMLElement} options.element The animated element
						 * @property {Object|null} [options.from=null] The starting step, this
						 *        can be defined later
						 * @property {Object|null} [options.to=null]  The finishing step, this
						 *        can also be defined later
						 * @property {Object[]} [options.steps=Array(0)] Animation steps,
						 *        when advanced keying is required, the array must have 100 elements,
						 *        which are percentages of the timeline (animation duration)
						 * @property {string} [options.duration="0"] The duration of the animation
						 * @property {string} [options.direction="normal"] The direction of the
						 *        animation (for possible values, refer to CSS Animation spec)
						 * @property {string} [options.delay="0"] The delay of the animation.
						 *        Please remember when using ns.util.anim.Chain with concurrent
						 *        option to false, the of subsequent animations will be modified
						 * @property {string} [options.fillMode="none"] The fill mode of the
						 *        animations (for possible values, refer to CSS Animation spec)
						 * @property {boolean} [options.preserve=false] Indicates if the last
						 *        key frame props should be kept after animation is destroyed
						 *        (not implemented!)
						 * @property {string} [options.timingFunction="ease"] Chooses the timing
						 *        function for the css animation (for possible values, refer to CSS
						 *        Animation spec)
						 * @property {boolean} [options.autoPlay=false] Defines if the animation
						 *        will start after definition
						 * @member ns.util.anim.Animation
						 */
						opts = objectUtils.merge({
							element: null,
							from: null,
							to: null,
							steps: [],
							duration: "0",
							direction: "normal",
							delay: "0",
							iterationCount: 1,
							infinite: false,
							fillMode: "none",
							preserve: false, //@TODO preserve props after animation destroy!
							onEnd: null,
							onPause: null,
							onPlay: null,
							timingFunction: "ease",
							autoPlay: false
						}, options || {}),
						steps,
						props,
						endCallback = handleEnd.bind(null, this),
						element = opts.element;

					if (opts.steps.length === 0) {
						steps = [];
						steps.length = 101;
						if (opts.to) {
							steps[100] = opts.to;
						}
						if (!opts.from) {
							if (opts.to && opts.element) {
								props = Object.keys(opts.to);
								CSSUtils.extractCSSProperties(opts.element, props);
								steps[0] = props;
							}
						} else {
							steps[0] = opts.from;
						}
					} else {
						steps = opts.steps;
					}

					self.options = opts;
					/**
					 * @property {Array.<Object>} steps Array of animation steps
					 * @readonly
					 */
					self.steps = steps;
					// indicates if the css props were applied
					self._applied = false;
					/**
					 * @property {ns.util.anim.Keyframes|null} keyframes Keyframes reference
					 * @readonly
					 */
					self.keyframes = null;
					/**
					 * @property {number} [state=0] Animation state
					 *        (ns.util.anim.Animation.states.*)
					 * @readonly
					 */
					self.state = PAUSED;
					// timer for onPlay callback (we need to simulate actual event firing
					self.playTimer = null;
					this._endCallback = endCallback;

					if (element) {
						element.addEventListener(endEventName, endCallback, false);
						if (opts.autoPlay) {
							self.play();
						}
					}

				},
				proto = {};

			/**
			 * Applies css properties for the element
			 * @method _apply
			 * @protected
			 * @member ns.util.anim.Animation
			 */
			proto._apply = function () {
				var self = this,
					opts = self.options,
					element = opts.element,
					style = element.style,
					propString = style.getPropertyValue(cssPropertyPrefix + "animation"),
					propsArray = (propString && propString.split(",").map(trim)) || [],
					id;

				self.keyframes = new Keyframes(self.steps);
				id = self.keyframes.id;
				if (element) {
					propsArray.push(id + " " + opts.duration + " " + opts.timingFunction +
						" " + opts.delay + " " + opts.iterationCount + " " + opts.direction +
						" " + opts.fillMode);
					element.style.setProperty(cssPropertyPrefix + "animation",
						propsArray.join(","));
					self._applied = true;
				}
			};

			/**
			 * Adds step to animation
			 * Note: this will reset the whole animation, so do it only in paused state
			 * @param {number} timePoint A keyframe number between from 0 to 100
			 * @param {Object} stepOptions Css props to change in the keyframe
			 * @return {ns.util.anim.Animation}
			 * @method step
			 * @member ns.util.anim.Animation
			 */
			proto.step = function (timePoint, stepOptions) {
				var self = this;

				self.steps[timePoint] = stepOptions;
				return self.reset();
			};

			/**
			 * Resets the animation
			 * @return {ns.util.anim.Animation}
			 * @method reset
			 * @member ns.util.anim.Animation
			 */
			proto.reset = function () {
				var self = this,
					keyframes = self.keyframes,
					style = self.options.element.style,
					propString = style.getPropertyValue(cssPropertyPrefix + "animation-name"),
					propsArray = (propString && propString.split(",").map(trim)) || [],
					index = keyframes ? propsArray.indexOf(keyframes.id) : -1;

				if (self.keyframes) {
					self.keyframes.destroy();
				}

				keyframes = new Keyframes(self.steps);
				if (index > -1) {
					propsArray[index] = keyframes.id;
					self.keyframes = keyframes;
					style.setProperty(cssPropertyPrefix + "animation-name",
						propsArray.join(","));
				}

				return self;
			};

			/**
			 * Starts playback
			 * @return {ns.util.anim.Animation}
			 * @method play
			 * @member ns.util.anim.Animation
			 */
			proto.play = function () {
				return changeState(this, "running");
			};

			/**
			 * Pauses playback
			 * @return {ns.util.anim.Animation}
			 * @method pause
			 * @member ns.util.anim.Animation
			 */
			proto.pause = function () {
				return changeState(this, "paused");
			};

			/**
			 * Destroys the animation
			 * Note: Please use "preserve" options to keep applied last animation props
			 * @return {ns.util.anim.Animation}
			 * @method destroy
			 * @member ns.util.anim.Animation
			 */
			proto.destroy = function () {
				var self = this,
					element = self.options.element,
					prop,
					style,
					keyframes = self.keyframes,
					endCallback = self._endCallback,
					propRegexp;

				if (element) {
					if (self._applied && keyframes) {
						style = element.style;
						prop = style.getPropertyValue(cssPropertyPrefix + "animation");
						if (prop) {
							propRegexp = new RegExp(",? ?" + keyframes.id + "[^,%]*,? ?", "i");
							style.removeProperty(cssPropertyPrefix + "animation",
								prop.replace(propRegexp, ""));
						}
						keyframes.destroy();
						self._applied = false;
					}
					if (endCallback) {
						element.removeEventListener(endEventName, endCallback, false);
					}
				}
				window.clearTimeout(self.playTimer);
			};

			/**
			 * @property {Object} states animation state definitions
			 * @property {number} [states.PAUSED=0] paused state
			 * @property {number} [states.PLAYING=1] playing state
			 * @property {number} [states.FINISHED=2] finished state
			 * @static
			 * @readonly
			 * @member ns.util.anim.Animation
			 */
			Animation.states = {
				"PAUSED": PAUSED,
				"PLAYING": PLAYING,
				"FINISHED": FINISHED
			};
			Animation.prototype = proto;
			ns.util.anim.Animation = Animation;
			}());

/*global define, ns */
/*jslint plusplus: true, nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Chain
 *
 * Chain class for easy multiple animations management. The chain
 * can be executed as is (animations are concurrent) or in order
 * of adding in which animations are delayed so that the execute
 * in turn
 *
 * ## Usage example
 *
 * @example
 *
 *        <div id="test"
 *                style="width: 10px; height: 10px; background: red; position: absolute;">
 *        </div>
 *
 *        <script>
 *            var element = document.getElementById("test"),
 *                chain = new tau.util.anim.Chain(
 *                    {
 *						concurrent: false,
 *						onPlay: function () {
 *							console.log("chain started to play");
 *						},
 *						onPause: function () {
 *							console.log("chain paused");
 *						},
 *						onEnd: function () {
 *							console.log("chain finished");
 *						}
 *					},
 *                    [
 *                        {
 *							element: element,
 *							from: { "background-color": "red" },
 *							to: { "background-color": "blue"},
 *							duration: "3s",
 *							onPlay: function () {
 *								console.log("animation 1 started to play");
 *							},
 *							onPause: function () {
 *								console.log("animation 1 paused");
 *							},
 *							onEnd: function () {
 *								console.log("animation 1 finished");
 *							}
 *						},
 *                        {
 *							element: element,
 *						from: { "-webkit-transform": "translate3d(0,0,0)" },
 *							to: { "-webkit-transform": "translate3d(100px, 100px, 0)"},
 *							duration: "3s",
 *							onPlay: function () {
 *								console.log("animation 2 started to play");
 *							},
 *							onPause: function () {
 *								console.log("animation 2 paused");
 *							},
 *							onEnd: function () {
 *								console.log("animation 2 finished");
 *							}
 *						}
 *                    ]
 *                );
 *            chain.play();
 *        </script>
 *
 * @class ns.util.anim.Chain
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function () {
	"use strict";
				var Animation = ns.util.anim.Animation,
				objectUtils = ns.util.object,
				dateUtils = ns.util.date,
				// paused state flag
				PAUSED = 0,
				// playing state flag
				PLAYING = 1,
				// finished state flag
				FINISHED = 2,
				// function type for typeof comparisons
				TYPE_FUNCTION = "function",
				/**
				 * Animation end handler
				 * @param {ns.util.anim.Chain} self
				 * @param {ns.util.anim.Animation} animation
				 * @method handleEnd
				 * @member ns.util.anim.Chain
				 * @private
				 * @static
				 */
				handleEnd = function (self, animation) {
					var onEnd = self.options.onEnd,
						animations = self.animations,
						index = animations.indexOf(animation);

					self.current = index;
					if (index === animations.length - 1) {
						self.state = FINISHED;
						if (typeof onEnd === TYPE_FUNCTION) {
							onEnd(self);
						}
					}
				},
				Chain = function (options, animations) {
					/**
					 * @property {Object} options
					 * @property {boolean} [options.concurent='false'] Sets the type of the chain
					 * @property {function(ns.util.anim.Chain)} [options.onPlay=null] a callback for play start
					 * @property {function(ns.util.anim.Chain)} [options.onPause=null] a callback for play pause
					 * @property {function(ns.util.anim.Chain)} [options.onEnd=null] a callback for play end
					 */
					var opts = objectUtils.merge({
							conocurrent: true,
							onPlay: null,
							onPause: null,
							onEnd: null
						}, options || {}),
						self = this;
					/**
					 * @property {number} current Marks current animation
					 * @readonly
					 */

					self.current = null;
					/**
					 * @property {Array.<ns.util.anim.Animation>} animations The animations holder
					 * @readonly
					 */
					self.animations = [];
					/**
					 * @property {number} totalTime
					 * @readonly
					 */
					self.totalTime = 0;
					self.options = opts;
					/**
					 * @property {number} state=0 (ns.util.anim.Chain.states.*)
					 * @readonly
					 */
					self.state = PAUSED;

					if (animations && animations.length > 0) {
						self.addMultiple(animations);
					}
				},
				proto = {
					/**
					 * Adds animation to chain
					 * @param {ns.util.anim.Animation} animation
					 * @return {ns.util.anim.Chain}
					 * @method add
					 * @member ns.util.anim.Chain
					 */
					add: function (animation) {
						var animationInstance = animation instanceof Animation ?
								animation :
								new Animation(animation),
							animationOptions = animationInstance.options,
							time = dateUtils.convertToMiliseconds(animationOptions.duration),
							delay = dateUtils.convertToMiliseconds(animationOptions.delay),
							onEndCallback = animationOptions.onEnd,
							self = this;

						if (typeof onEndCallback === TYPE_FUNCTION) {
							animationOptions.onEnd = function (_animation, element, event) {
								onEndCallback(_animation, element, event);
								handleEnd(self, _animation);
							};
						} else {
							animationOptions.onEnd = handleEnd.bind(null, self);
						}

						if (self.options.concurrent === false) {
							animationOptions.delay = (delay + self.totalTime) + "ms";
							self.totalTime += delay + time;
						}

						self.animations.push(animationInstance);
						if (!self.current) {
							self.current = 0;
						}
						return self;
					},

					/**
					 * Adds multiple animations to chain
					 * @param {Array.<ns.util.anim.Animation>} animations
					 * @return {ns.util.anim.Chain}
					 * @method addMultiple
					 * @member ns.util.anim.Chain
					 */
					addMultiple: function (animations) {
						var i,
							l;

						for (i = 0, l = animations.length; i < l; ++i) {
							this.add(animations[i]);
						}
						return this;
					},

					/**
					 * Starts playing animation chain
					 * @method play
					 * @return {ns.util.anim.Chain}
					 * @member ns.util.anim.Chain
					 */
					play: function () {
						var i,
							l,
							self = this,
							onPlay = self.options.onPlay;

						for (i = self.current, l = self.animations.length; i < l; ++i) {
							self.animations[i].play();
						}
						self.state = PLAYING;
						if (typeof onPlay === TYPE_FUNCTION) {
							onPlay(self);
						}
						return self;
					},

					/**
					 * Pauses playback
					 * @method pause
					 * @return {ns.util.anim.Animation}
					 * @member ns.util.anim.Chain
					 */
					pause: function () {
						var i,
							l,
							self = this,
							onPause = self.options.onPause;

						for (i = self.current, l = self.animations.length; i < l; ++i) {
							self.animations[i].pause();
						}
						self.state = PAUSED;
						if (typeof onPause === TYPE_FUNCTION) {
							onPause(self);
						}
						return self;
					},

					/**
					 * Destroys chain and animations
					 * @method destroy
					 * @member ns.util.anim.Chain
					 */
					destroy: function () {
						var i,
							l;

						for (i = 0, l = this.animations.length; i < l; ++i) {
							this.animations[i].destroy();
						}
					}
				};

			/**
			 * Animation chain states
			 * @property {Object} states
			 * @property {number} [states.PAUSED = 0]
			 * @property {number} [states.PLAYING = 1]
			 * @property {number} [states.FINISHED = 2]
			 * @readonly
			 * @static
			 * @member ns.util.anim.Chain
			 */
			Chain.states = {
				"PAUSED": PAUSED,
				"PLAYING": PLAYING,
				"FINISHED": FINISHED
			};
			Chain.prototype = proto;
			ns.util.anim.Chain = Chain;
			}());

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Event hwkey
 * Namespace to support tizenhwkey event
 * @class ns.event.hwkey
 */
(function (window, ns) {
	"use strict";
				var popupClose = function (event) {
					var keyName = event.keyName,
						activePopup = ns.activePopup,
						container,
						containerClass,
						focused;

					// Check enableHWKeyHandler property
					if (ns.getConfig("enableHWKeyHandler", true) && activePopup) {
						container = activePopup._ui.container;
						containerClass = container && container.classList;
						if (keyName === "menu") {
							focused = activePopup.element.querySelector(".ui-focus");
							if (focused) {
								// NOTE: If a popup is opened and focused element exists in it,
								//       do not close that popup.
								//       'false' is returned here, hence popup close routine is not run.
								event.preventDefault();
								event.stopPropagation();
								return;
							}
						}
						if (keyName === "menu" || keyName === "back") {
							if (containerClass && (!containerClass.contains("ui-datetimepicker") || containerClass.contains("in"))) {
								activePopup.close();
								event.preventDefault();
								event.stopPropagation();
							}
						}
					}
				},
				selectMenuClose = function (event) {
					var keyName = event.keyName,
						elActiveSelectMenu,
						activeSelectMenu;

					if (ns.getConfig("enableHWKeyHandler", true) && (keyName === "menu" || keyName === "back")) {
						elActiveSelectMenu = document.querySelector("div.ui-selectmenu-active select");
						if (elActiveSelectMenu) {
							activeSelectMenu = ns.widget.SelectMenu(elActiveSelectMenu);
							activeSelectMenu.close();
							event.preventDefault();
							event.stopPropagation();
						}
					}
				},
				eventType = ns.engine.eventType,
				hwkey = {
					/**
					 * Bind event tizenhwkey to support hardware keys.
					 * @method bind
					 * @static
					 * @member ns.event.hwkey
					 */
					bind: function () {
						document.addEventListener("tizenhwkey", popupClose, true);
						document.addEventListener("tizenhwkey", selectMenuClose, true);
					},

					/**
					 * Unbind event tizenhwkey to support hardware keys.
					 * @method unbind
					 * @static
					 * @member ns.event.hwkey
					 */
					unbind: function () {
						document.removeEventListener("tizenhwkey", popupClose, true);
						document.removeEventListener("tizenhwkey", selectMenuClose, true);
					}
				};

			ns.event.hwkey = hwkey;

			function init() {
				hwkey.unbind();
				hwkey.bind();
			}

			function destroy() {
				document.removeEventListener(eventType.INIT, init, false);
				document.removeEventListener(eventType.DESTROY, destroy, false);
			}

			document.addEventListener(eventType.INIT, init, false);
			document.addEventListener(eventType.DESTROY, destroy, false);

			}(window, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define */
/**
 * #Event throttledresize
 * Object supports throttledresize event.
 * @class ns.event.throttledresize
 */
/**
 * Event throttledresize
 * @event throttledresize
 * @member ns.event.throttledresize
 */
(function (window) {
	"use strict";
				var throttledresize = {
					/**
					 * State of event support
					 * @property {boolean} [enabled=true]
					 * @static
					 * @member ns.event.throttledresize
					 */
					enabled: ns.getConfig("enableThrottleResize", true),
					/**
					 * Timeout of triggering event.
					 * @property {number} [ttl=250]
					 * @static
					 * @member ns.event.throttledresize
					 */
					ttl: 250
				},
				timerID,
				eventUtils = ns.event,
				resizeHandler = function () {
					if (timerID) {
						window.clearTimeout(timerID);
					}
					timerID = window.setTimeout(function () {
						eventUtils.trigger(window, "throttledresize");
					}, throttledresize.ttl);
				},
				/**
				 * Enables event support
				 * @method enable
				 * @static
				 * @member ns.event.throttledresize
				 */
				enable = function () {
					if (!throttledresize.enabled) {
						throttledresize.enabled = true;
					}
					window.addEventListener("resize", resizeHandler, true);
				},

				unbind = function () {
					throttledresize.enabled = false;
					window.removeEventListener("resize", resizeHandler, true);
				};

			if (throttledresize.enabled) {
				enable();
			}

			throttledresize.enable = enable;
			throttledresize.unbind = unbind;

			ns.event.throttledresize = throttledresize;

			}(window));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* global window, define, ns, CustomEvent */
/*
 * @class ns.event.gesture
 */
(function (ns) {
	"use strict";
			var event = ns.event,
			gesture = function (elem, options) {
				return new ns.event.gesture.Instance(elem, options);
			};

			/**
			 * Default values for Gesture feature
			 * @property {Object} defaults
			 * @property {boolean} [defaults.triggerEvent=false]
			 * @property {number} [defaults.updateVelocityInterval=16]
			 * Interval in which Gesture recalculates current velocity in ms
			 * @property {number} [defaults.estimatedPointerTimeDifference=15]
			 * pause time threshold.. tune the number to up if it is slow
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.defaults = {
			triggerEvent: false,
			updateVelocityInterval: 16,
			estimatedPointerTimeDifference: 15
		};

		/**
			 * Dictionary of orientation
			 * @property {Object} Orientation
			 * @property {1} Orientation.VERTICAL vertical orientation
			 * @property {2} Orientation.HORIZONTAL horizontal orientation
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.Orientation = {
			VERTICAL: "vertical",
			HORIZONTAL: "horizontal"
		};

		/**
			 * Dictionary of direction
			 * @property {Object} Direction
			 * @property {1} Direction.UP up
			 * @property {2} Direction.DOWN down
			 * @property {3} Direction.LEFT left
			 * @property {4} Direction.RIGHT right
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.Direction = {
			UP: "up",
			DOWN: "down",
			LEFT: "left",
			RIGHT: "right"
		};

		/**
			 * Dictionary of gesture events state
			 * @property {Object} Event
			 * @property {"start"} Event.START start
			 * @property {"move"} Event.MOVE move
			 * @property {"end"} Event.END end
			 * @property {"cancel"} Event.CANCEL cancel
			 * @property {"blocked"} Event.BLOCKED blocked
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.Event = {
			START: "start",
			MOVE: "move",
			END: "end",
			CANCEL: "cancel",
			BLOCKED: "blocked"
		};

		/**
			 * Dictionary of gesture events flags
			 * @property {Object} Result
			 * @property {number} [Result.PENDING=1] is pending
			 * @property {number} [Result.RUNNING=2] is running
			 * @property {number} [Result.FINISHED=4] is finished
			 * @property {number} [Result.BLOCK=8] is blocked
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.Result = {
			PENDING: 1,
			RUNNING: 2,
			FINISHED: 4,
			BLOCK: 8
		};

		/**
			 * Create plugin namespace.
			 * @property {Object} plugin
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.plugin = {};

		/**
			 * Create object of Detector
			 * @method createDetector
			 * @param {string} gesture
			 * @param {HTMLElement} eventSender
			 * @param {Object} options
			 * @return {ns.event.gesture.Gesture}
			 * @member ns.event.gesture
			 * @static
			 */
		gesture.createDetector = function (gesture, eventSender, options) {
			if (!gesture.plugin[gesture]) {
				throw gesture + " gesture is not supported";
			}
			return new gesture.plugin[gesture](eventSender, options);
		};

		event.gesture = gesture;
		}(ns));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Gesture.Detector class
 * Base class for create detectors in gestures.
 *
 * @class ns.event.gesture.Detector
 */
(function (ns) {
	"use strict";
				var gesture = ns.event.gesture,

				objectMerge = ns.util.object.merge,

				Detector = function (strategy, sender) {
					this.sender = sender;
					this.strategy = strategy.create();
					this.name = this.strategy.name;
					this.index = this.strategy.index || 100;
					this.options = this.strategy.options || {};
				};

			/**
			 * Start of gesture detection of given type
			 * @method detect
			 * @param {string} gestureEvent
			 * @return {Object}
			 * @member ns.event.gesture.Detector
			 */
			Detector.prototype.detect = function (gestureEvent) {
				return this.strategy.handler(gestureEvent, this.sender, this.strategy.options);
			};

			Detector.Sender = {
				sendEvent: function () {
				// Empty function for creating interface
				}
			};

		/**
			 * Create plugin namespace.
			 * @property {Object} plugin
			 * @member ns.event.gesture.Detector
			 */
			Detector.plugin = {};

		/**
			 * Methods creates plugin
			 * @method create
			 * @param {Object} gestureHandler
			 * @return {ns.event.gesture.Detector} gestureHandler
			 * @member ns.event.gesture.Detector.plugin
			 */
			Detector.plugin.create = function (gestureHandler) {
				var detector;

				if (!gestureHandler.types) {
					gestureHandler.types = [gestureHandler.name];
				}

				detector = function (options) {
					this.options = objectMerge({}, gestureHandler.defaults, options);
				};

				detector.prototype.create = function () {
					return objectMerge({
						options: this.options
					}, gestureHandler);
				};

				Detector.plugin[gestureHandler.name] = detector;

				return detector;
			};

		// definition
			gesture.Detector = Detector;

		}(ns));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Gesture Utilities
 * Contains helper function to gesture support.
 * @class ns.event.gesture.utils
 */
(function (ns, Math) {
	"use strict";
	
		/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.utils
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture;

			gesture.utils = {

			/**
				 * Get center from array of touches
				 * @method getCenter
				 * @param {Event[]} touches description
				 * @member ns.event.gesture.utils
				 * @return {Object} position
				 * @return {number} return.clientX position X
				 * @return {number} return.clientY position Y
				 */
				getCenter: function (touches) {
					var valuesX = [],
						valuesY = [];

					[].forEach.call(touches, function (touch) {
					// I prefer clientX because it ignore the scrolling position
						valuesX.push(!isNaN(touch.clientX) ? touch.clientX : touch.pageX);
						valuesY.push(!isNaN(touch.clientY) ? touch.clientY : touch.pageY);
					});

					return {
						clientX: (Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2,
						clientY: (Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2
					};
				},

			/**
				 * Get velocity
				 * @method getVelocity
				 * @param {number} deltaTime Delta of time
				 * @param {number} deltaX Position change on x axis
				 * @param {number} deltaY Position change on y axis
				 * @return {Object} velocity
				 * @return {number} return.x velocity on X axis
				 * @return {number} return.y velocity on Y axis
				 * @member ns.event.gesture.utils
				 */
				getVelocity: function (deltaTime, deltaX, deltaY) {
					return {
						x: Math.abs(deltaX / deltaTime) || 0,
						y: Math.abs(deltaY / deltaTime) || 0
					};
				},

			/**
				 * Get angel between position of two touches
				 * @method getAngle
				 * @param {Event} touch1 first touch
				 * @param {Event} touch2 second touch
				 * @return {number} angel (deg)
				 * @member ns.event.gesture.utils
				 */
				getAngle: function (touch1, touch2) {
					var y = touch2.clientY - touch1.clientY,
						x = touch2.clientX - touch1.clientX;

					return Math.atan2(y, x) * 180 / Math.PI;
				},

			/**
				 * Get direction indicated by position of two touches
				 * @method getDirection
				 * @param {Event} touch1 first touch
				 * @param {Event} touch2 second touch
				 * @return {ns.event.gesture.Direction.LEFT|ns.event.gesture.Direction.RIGHT|ns.event.gesture.Direction.UP|ns.event.gesture.Direction.DOWN}
				 * @member ns.event.gesture.utils
				 */
				getDirection: function (touch1, touch2) {
					var x = Math.abs(touch1.clientX - touch2.clientX),
						y = Math.abs(touch1.clientY - touch2.clientY);

					if (x >= y) {
						return touch1.clientX - touch2.clientX > 0 ? gesture.Direction.LEFT : gesture.Direction.RIGHT;
					}
					return touch1.clientY - touch2.clientY > 0 ? gesture.Direction.UP : gesture.Direction.DOWN;
				},

			/**
				 * Get distance indicated by position of two touches
				 * @method getDistance
				 * @param {Event} touch1 first touch
				 * @param {Event} touch2 second touch
				 * @return {number} distance
				 * @member ns.event.gesture.utils
				 */
				getDistance: function (touch1, touch2) {
					var x = touch2.clientX - touch1.clientX,
						y = touch2.clientY - touch1.clientY;

					return Math.sqrt((x * x) + (y * y));
				},

			/**
				 * Get scale indicated by position of the first and the last touch
				 * @method getScale
				 * @param {Event} start start touch
				 * @param {Event} end end touch
				 * @return {number} scale
				 * @member ns.event.gesture.utils
				 */
				getScale: function (start, end) {
				// need two fingers...
					if (start.length >= 2 && end.length >= 2) {
						return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]);
					}
					return 1;
				},

			/**
				 * Get value of rotation indicated by position
				 * of the first and the last touch
				 * @method getRotation
				 * @param {Event} start start touch
				 * @param {Event} end end touch
				 * @return {number} angle (deg)
				 * @member ns.event.gesture.utils
				 */
				getRotation: function (start, end) {
				// need two fingers
					if (start.length >= 2 && end.length >= 2) {
						return this.getAngle(end[1], end[0]) -
							this.getAngle(start[1], start[0]);
					}
					return 0;
				},

			/**
				 * Check if the direction is vertical
				 * @method isVertical
				 * @param {ns.event.gesture.Direction.LEFT|ns.event.gesture.Direction.RIGHT|ns.event.gesture.Direction.UP|ns.event.gesture.Direction.DOWN} direction start touch
				 * @return {boolean}
				 * @member ns.event.gesture.utils
				 */
				isVertical: function (direction) {
					return direction === gesture.Direction.UP || direction === gesture.Direction.DOWN;
				},

			/**
				 * Check if the direction is horizontal
				 * @method isHorizontal
				 * @param {ns.event.gesture.Direction.LEFT|ns.event.gesture.Direction.RIGHT|ns.event.gesture.Direction.UP|ns.event.gesture.Direction.DOWN} direction start touch
				 * @return {boolean}
				 * @member ns.event.gesture.utils
				 */
				isHorizontal: function (direction) {
					return direction === gesture.Direction.LEFT || direction === gesture.Direction.RIGHT;
				},

			/**
				 * Check if the direction is horizontal
				 * @method getOrientation
				 * @param {ns.event.gesture.Direction.LEFT|ns.event.gesture.Direction.RIGHT|ns.event.gesture.Direction.UP|ns.event.gesture.Direction.DOWN} direction
				 * @return {boolean}
				 * @member ns.event.gesture.utils
				 */
				getOrientation: function (direction) {
					return this.isVertical(direction) ? gesture.Orientation.VERTICAL : gesture.Orientation.HORIZONTAL;
				}
			};
		}(ns, window.Math));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Gesture.Manager class
 * Main class controls all gestures.
 * @class ns.event.gesture.Manager
 */
(function (ns, window, document) {
	"use strict";
	
		/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.Manager
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture,

				gestureUtils = gesture.utils,

				utilObject = ns.util.object,

				instance = null,

				touchCheck = /touch/,

				Manager = function () {
					var self = this;

					self.instances = [];
					self.gestureDetectors = [];
					self.runningDetectors = [];
					self.detectorRequestedBlock = null;

					self.unregisterBlockList = [];

					self.gestureEvents = {};
					self.velocity = null;

					self._isReadyDetecting = false;
					self._blockMouseEvent = false;
					self.touchSupport = "ontouchstart" in window;
				};

			function sortInstances(a, b) {
				if (a.index < b.index) {
					return -1;
				} else if (a.index > b.index) {
					return 1;
				}
				return 0;
			}

			Manager.prototype = {
			/**
				 * Bind start events
				 * @method _bindStartEvents
				 * @param {ns.event.gesture.Instance} _instance gesture instance
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_bindStartEvents: function (_instance) {
					var element = _instance.getElement();

					if (this.touchSupport) {
						element.addEventListener("touchstart", this, false);
					} else {
						element.addEventListener("mousedown", this, false);
					}
				},

			/**
				 * Bind move, end and cancel events
				 * @method _bindEvents
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_bindEvents: function () {
					var self = this;

					if (self.touchSupport) {
						document.addEventListener("touchmove", self);
						document.addEventListener("touchend", self);
						document.addEventListener("touchcancel", self);
					} else {
						document.addEventListener("mousemove", self);
						document.addEventListener("mouseup", self);
					}
				},

			/**
				 * Unbind start events
				 * @method _unbindStartEvents
				 * @param {ns.event.gesture.Instance} _instance gesture instance
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_unbindStartEvents: function (_instance) {
					var element = _instance.getElement();

					if (this.touchSupport) {
						element.removeEventListener("touchstart", this, false);
					} else {
						element.removeEventListener("mousedown", this, false);
					}
				},

			/**
				 * Unbind move, end and cancel events
				 * @method _bindEvents
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_unbindEvents: function () {
					var self = this;

					if (self.touchSupport) {
						document.removeEventListener("touchmove", self, false);
						document.removeEventListener("touchend", self, false);
						document.removeEventListener("touchcancel", self, false);
					} else {
						document.removeEventListener("mousemove", self, false);
						document.removeEventListener("mouseup", self, false);
					}
				},

			/**
				 * Detect that event should be processed by handleEvent
				 * @param {Event} event Input event object
				 * @return {null|string}
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_detectEventType: function (event) {
					var eventType = event.type;

					if (eventType.match(touchCheck)) {
						this._blockMouseEvent = true;
					} else {
						if (this._blockMouseEvent || event.which !== 1) {
							return null;
						}
					}
					return eventType;
				},

			/**
				 * Handle event
				 * @method handleEvent
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				handleEvent: function (event) {
					var self = this,
						eventType = self._detectEventType(event);

					switch (eventType) {
						case "mousedown":
						case "touchstart":
							self._start(event);
							break;
						case "mousemove":
						case "touchmove":
							self._move(event);
							break;
						case "mouseup":
						case "touchend":
							self._end(event);
							break;
						case "touchcancel":
							self._cancel(event);
							break;
					}
				},

			/**
				 * Handler for gesture start
				 * @method _start
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_start: function (event) {
					var self = this,
						element = event.currentTarget,
						startEvent = {},
						detectors = [];

					if (!self._isReadyDetecting) {
						self._resetDetecting();
						self._bindEvents();

						startEvent = self._createDefaultEventData(gesture.Event.START, event);

						self.gestureEvents = {
							start: startEvent,
							last: startEvent
						};

						self.velocity = {
							event: startEvent,
							x: 0,
							y: 0
						};

						startEvent = utilObject.fastMerge(startEvent,
							self._createGestureEvent(gesture.Event.START, event));
						self._isReadyDetecting = true;
					}

					self.instances.forEach(function (_instance) {
						if (_instance.getElement() === element) {
							detectors = detectors.concat(_instance.getGestureDetectors());
						}
					}, self);

					detectors.sort(sortInstances);

					self.gestureDetectors = self.gestureDetectors.concat(detectors);

					self._detect(detectors, startEvent);
				},

			/**
				 * Handler for gesture move
				 * @method _move
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_move: function (event) {
					var newEvent,
						self = this;

					if (self._isReadyDetecting) {
						newEvent = self._createGestureEvent(gesture.Event.MOVE, event);
						self._detect(self.gestureDetectors, newEvent);
						self.gestureEvents.last = newEvent;
					}
				},

			/**
				 * Handler for gesture end
				 * @method _end
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_end: function (event) {
					var self = this,
						newEvent = utilObject.merge(
						{},
							self.gestureEvents.last,
							self._createDefaultEventData(gesture.Event.END, event)
					);

					if (newEvent.pointers.length === 0) {
						self._detect(self.gestureDetectors, newEvent);

						self.unregisterBlockList.forEach(function (_instance) {
							this.unregister(_instance);
						}, self);

						self._resetDetecting();
						self._blockMouseEvent = false;
					}
				},

			/**
				 * Handler for gesture cancel
				 * @method _cancel
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_cancel: function (event) {
					var self = this;

					event = utilObject.merge(
					{},
						self.gestureEvents.last,
						self._createDefaultEventData(gesture.Event.CANCEL, event)
				);

					self._detect(self.gestureDetectors, event);

					self.unregisterBlockList.forEach(function (_instance) {
						this.unregister(_instance);
					}, self);

					self._resetDetecting();
					self._blockMouseEvent = false;
				},

			/**
				 * Detect gesture
				 * @method _detect
				 * @param {Array} detectors
				 * @param {Event} event
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_detect: function (detectors, event) {
					var self = this,
						finishedDetectors = [];

					detectors.forEach(function (detector) {
						var result;

						if (!self.detectorRequestedBlock) {
							result = detector.detect(event);
							if ((result & gesture.Result.RUNNING) &&
								self.runningDetectors.indexOf(detector) < 0) {
								self.runningDetectors.push(detector);
							}
							if (result & gesture.Result.FINISHED) {
								finishedDetectors.push(detector);
							}
							if (result & gesture.Result.BLOCK) {
								self.detectorRequestedBlock = detector;
							}
						}
					});

				// remove finished detectors.
					finishedDetectors.forEach(function (detector) {
						var idx = self.gestureDetectors.indexOf(detector);

						if (idx > -1) {
							self.gestureDetectors.splice(idx, 1);
						}
						idx = self.runningDetectors.indexOf(detector);
						if (idx > -1) {
							self.runningDetectors.splice(idx, 1);
						}
					});

				// remove all detectors except the detector that return block result
					if (self.detectorRequestedBlock) {
					// send to cancel event.
						self.runningDetectors.forEach(function (detector) {
							var cancelEvent = utilObject.fastMerge({}, event);

							cancelEvent.eventType = gesture.Event.BLOCKED;
							detector.detect(cancelEvent);
						});
						self.runningDetectors.length = 0;
						self.gestureDetectors.length = 0;
						if (finishedDetectors.indexOf(self.detectorRequestedBlock) < 0) {
							self.gestureDetectors.push(self.detectorRequestedBlock);
						}
					}
				},

			/**
				 * Reset of gesture manager detector
				 * @method _resetDetecting
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_resetDetecting: function () {
					var self = this;

					self._isReadyDetecting = false;

					self.gestureDetectors.length = 0;
					self.runningDetectors.length = 0;
					self.detectorRequestedBlock = null;

					self.gestureEvents = {};
					self.velocity = null;

					self._unbindEvents();
				},

			/**
				 * Create default event data
				 * @method _createDefaultEventData
				 * @param {string} type event type
				 * @param {Event} event source event
				 * @return {Object} default event data
				 * @return {string} return.eventType
				 * @return {number} return.timeStamp
				 * @return {Touch} return.pointer
				 * @return {TouchList} return.pointers
				 * @return {Event} return.srcEvent
				 * @return {Function} return.preventDefault
				 * @return {Function} return.stopPropagation
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_createDefaultEventData: function (type, event) {
					var pointers = event.touches;

					if (!pointers) {
						if (event.type === "mouseup") {
							pointers = [];
						} else {
							event.identifier = 1;
							pointers = [event];
						}
					}

					return {
						eventType: type,
						timeStamp: Date.now(),
						pointer: pointers[0],
						pointers: pointers,

						srcEvent: event,
						preventDefault: event.preventDefault.bind(event),
						stopPropagation: event.stopPropagation.bind(event)
					};
				},

			/**
				 * Create gesture event
				 * @method _createGestureEvent
				 * @param {string} type event type
				 * @param {Event} event source event
				 * @return {Object} gesture event consist from Event class and additional properties
				 * @return {number} return.deltaTime
				 * @return {number} return.deltaX
				 * @return {number} return.deltaY
				 * @return {number} return.velocityX
				 * @return {number} return.velocityY
				 * @return {number} return.estimatedX
				 * @return {number} return.estimatedY
				 * @return {number} return.estimatedDeltaX
				 * @return {number} return.estimatedDeltaY
				 * @return {number} return.distance
				 * @return {number} return.angle
				 * @return {number} return.direction
				 * @return {number} return.scale
				 * @return {number} return.rotation (deg)
				 * @return {Event} return.startEvent
				 * @return {Event} return.lastEvent
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_createGestureEvent: function (type, event) {
					var self = this,
						defaultEvent = self._createDefaultEventData(type, event),
						startEvent = self.gestureEvents.start,
						lastEvent = self.gestureEvents.last,
						velocity = self.velocity,
						velocityEvent = velocity.event,
						delta = {
							time: defaultEvent.timeStamp - startEvent.timeStamp,
							x: defaultEvent.pointer.clientX - startEvent.pointer.clientX,
							y: defaultEvent.pointer.clientY - startEvent.pointer.clientY
						},
						deltaFromLast = {
							x: defaultEvent.pointer.clientX - lastEvent.pointer.clientX,
							y: defaultEvent.pointer.clientY - lastEvent.pointer.clientY
						},
					/* pause time threshold.util. tune the number to up if it is slow */
						timeDifference = gesture.defaults.estimatedPointerTimeDifference,
						estimated;

					// reset start event for multi touch
					if (startEvent && defaultEvent.pointers.length !== startEvent.pointers.length) {
						startEvent.pointers = Array.prototype.slice.call(defaultEvent.pointers);
					}

					if (defaultEvent.timeStamp - velocityEvent.timeStamp >
						gesture.defaults.updateVelocityInterval) {
						utilObject.fastMerge(velocity, gestureUtils.getVelocity(
							defaultEvent.timeStamp - velocityEvent.timeStamp,
							defaultEvent.pointer.clientX - velocityEvent.pointer.clientX,
							defaultEvent.pointer.clientY - velocityEvent.pointer.clientY
					));
						velocity.event = defaultEvent;
					}

					estimated = {
						x: Math.round(defaultEvent.pointer.clientX +
							(timeDifference * velocity.x * (deltaFromLast.x < 0 ? -1 : 1))),
						y: Math.round(defaultEvent.pointer.clientY +
							(timeDifference * velocity.y * (deltaFromLast.y < 0 ? -1 : 1)))
					};

				// Prevent that point goes back even though direction is not changed.
					if ((deltaFromLast.x < 0 && estimated.x > lastEvent.estimatedX) ||
						(deltaFromLast.x > 0 && estimated.x < lastEvent.estimatedX)) {
						estimated.x = lastEvent.estimatedX;
					}

					if ((deltaFromLast.y < 0 && estimated.y > lastEvent.estimatedY) ||
						(deltaFromLast.y > 0 && estimated.y < lastEvent.estimatedY)) {
						estimated.y = lastEvent.estimatedY;
					}

					utilObject.fastMerge(defaultEvent, {
						deltaTime: delta.time,
						deltaX: delta.x,
						deltaY: delta.y,

						velocityX: velocity.x,
						velocityY: velocity.y,

						estimatedX: estimated.x,
						estimatedY: estimated.y,
						estimatedDeltaX: estimated.x - startEvent.pointer.clientX,
						estimatedDeltaY: estimated.y - startEvent.pointer.clientY,

						distance: gestureUtils.getDistance(startEvent.pointer, defaultEvent.pointer),

						angle: gestureUtils.getAngle(startEvent.pointer, defaultEvent.pointer),

						direction: gestureUtils.getDirection(startEvent.pointer, defaultEvent.pointer),

						scale: gestureUtils.getScale(startEvent.pointers, defaultEvent.pointers),
						rotation: gestureUtils.getRotation(startEvent.pointers, defaultEvent.pointers),

						startEvent: startEvent,
						lastEvent: lastEvent
					});

					return defaultEvent;
				},

			/**
				 * Register instance of gesture
				 * @method register
				 * @param {ns.event.gesture.Instance} instance gesture instance
				 * @member ns.event.gesture.Manager
				 */
				register: function (instance) {
					var self = this,
						idx = self.instances.indexOf(instance);

					if (idx < 0) {
						self.instances.push(instance);
						self._bindStartEvents(instance);
					}
				},

			/**
				 * Unregister instance of gesture
				 * @method unregister
				 * @param {ns.event.gesture.Instance} instance gesture instance
				 * @member ns.event.gesture.Manager
				 */
				unregister: function (instance) {
					var idx,
						self = this;

					if (self.gestureDetectors.length) {
						self.unregisterBlockList.push(instance);
					} else {
						idx = self.instances.indexOf(instance);
						if (idx > -1) {
							self.instances.splice(idx, 1);
							self._unbindStartEvents(instance);
						}

						if (!self.instances.length) {
							self._destroy();
						}
					}
				},

			/**
				 * Destroy instance of Manager
				 * @method _destroy
				 * @member ns.event.gesture.Manager
				 * @protected
				 */
				_destroy: function () {
					var self = this;

					self._resetDetecting();
					self.instances.length = 0;
					self.unregisterBlockList.length = 0;
					self._blockMouseEvent = false;
					instance = null;
				}

			};

			Manager.getInstance = function () {
				if (!instance) {
					instance = new Manager();
				}
				return instance;
			};

			gesture.Manager = Manager;
		}(ns, window, window.document));

/*global ns, window, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function (ns) {
	"use strict";
			/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.Instance
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture,
			/**
				 * Local alias for {@link ns.event.gesture.Detector}
				 * @property {Object}
				 * @member ns.event.gesture.Instance
				 * @private
				 * @static
				 */
				Detector = gesture.Detector,
			/**
				 * Local alias for {@link ns.event.gesture.Manager}
				 * @property {Object}
				 * @member ns.event.gesture.Instance
				 * @private
				 * @static
				 */
				Manager = gesture.Manager,
			/**
				 * Local alias for {@link ns.event}
				 * @property {Object}
				 * @member ns.event.gesture.Instance
				 * @private
				 * @static
				 */
				events = ns.event,
			/**
				 * Alias for method {@link ns.util.object.merge}
				 * @property {Function} merge
				 * @member ns.event.gesture.Instance
				 * @private
				 * @static
				 */
				merge = ns.util.object.merge,

			/**
				 * #Gesture.Instance class
				 * Creates instance of gesture manager on element.
				 * @param {HTMLElement} element
				 * @param {Object} options
				 * @class ns.event.gesture.Instance
				 */
				Instance = function (element, options) {
					this.element = element;
					this.eventDetectors = [];
					this.options = merge({}, gesture.defaults, options);

					this.gestureManager = Manager.getInstance();
					this.eventSender = merge({}, Detector.Sender, {
						sendEvent: this.trigger.bind(this)
					});
				};

			Instance.prototype = {
			/**
				 * Set options
				 * @method setOptions
				 * @param {Object} options options
				 * @return {ns.event.gesture.Instance}
				 * @member ns.event.gesture.Instance
				 */
				setOptions: function (options) {
					merge(this.options, options);
					return this;
				},

			/**
				 * Add detector
				 * @method addDetector
				 * @param {Object} detectorStrategy strategy
				 * @return {ns.event.gesture.Instance}
				 * @member ns.event.gesture.Instance
				 */
				addDetector: function (detectorStrategy) {
					var detector = new Detector(detectorStrategy, this.eventSender),
						alreadyHasDetector = !!this.eventDetectors.length;

					this.eventDetectors.push(detector);

					if (!!this.eventDetectors.length && !alreadyHasDetector) {
						this.gestureManager.register(this);
					}

					return this;
				},

			/**
				 * Remove detector
				 * @method removeDetector
				 * @param {Object} detectorStrategy strategy
				 * @return {ns.event.gesture.Instance}
				 * @member ns.event.gesture.Instance
				 */
				removeDetector: function (detectorStrategy) {
					var idx = this.eventDetectors.indexOf(detectorStrategy);

					if (idx > -1) {
						this.eventDetectors.splice(idx, 1);
					}

					if (!this.eventDetectors.length) {
						this.gestureManager.unregister(this);
					}

					return this;
				},

			/**
				 * Triggers the gesture event
				 * @method trigger
				 * @param {string} gestureName gestureName name
				 * @param {Object} eventInfo data provided to event object
				 * @member ns.event.gesture.Instance
				 */
				trigger: function (gestureName, eventInfo) {
					return events.trigger(this.element, gestureName, eventInfo, false);
				},

			/**
				 * Get HTML element assigned to gesture event instance
				 * @method getElement
				 * @member ns.event.gesture.Instance
				 */
				getElement: function () {
					return this.element;
				},

			/**
				 * Get gesture event detectors assigned to instance
				 * @method getGestureDetectors
				 * @member ns.event.gesture.Instance
				 */
				getGestureDetectors: function () {
					return this.eventDetectors;
				},

			/**
				 * Destroy instance
				 * @method destroy
				 * @member ns.event.gesture.Instance
				 */
				destroy: function () {
					this.element = null;
					this.eventHandlers = {};
					this.gestureManager = null;
					this.eventSender = null;
					this.eventDetectors.length = 0;
				}
			};

			gesture.Instance = Instance;

		}(ns));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Gesture Plugin: drag
 * Plugin enables drag event.
 *
 * @class ns.event.gesture.Drag
 */
(function (ns, window, tizen) {
	"use strict";
	
		/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.Drag
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture,
			/**
				 * Local alias for {@link ns.event.gesture.Detector}
				 * @property {Object}
				 * @member ns.event.gesture.Drag
				 * @private
				 * @static
				 */
				gestureUtils = gesture.utils,

				Detector = gesture.Detector,
			/**
				 * Alias for method {@link ns.util.object.merge}
				 * @property {Function} merge
				 * @member ns.event.gesture.Drag
				 * @private
				 * @static
				 */
				merge = ns.util.object.merge,

				eventNames = {
					start: "dragstart",
					drag: "drag",
					end: "dragend",
					cancel: "dragcancel",
					prepare: "dragprepare"
				},

			// TODO UA test will move to support.
				isTizenWebkit2Browser = !!window.navigator.userAgent.match(/tizen/i) && (function () {
					var result = true,
						version;

					if (tizen && tizen.systeminfo && tizen.systeminfo.getCapability) {
						try {
							version = tizen.systeminfo.getCapability("http://tizen.org/feature/platform.version");
							return version < "3.0";
						} catch (error) {
							ns.error("Error name: " + error.name + ", message: " + error.message);
						}
					}
					return result;
				})(),

				isChromeBrowser = window.navigator.userAgent.indexOf("Chrome") > -1,

				RESULTS = gesture.Result,

				Drag = Detector.plugin.create({

				/**
					 * Gesture name
					 * @property {string} [name="drag"]
					 * @member ns.event.gesture.Drag
					 */
					name: "drag",

				/**
					 * Gesture Index
					 * @property {number} [index=500]
					 * @member ns.event.gesture.Drag
					 */
					index: 500,

				/**
					 * Default values for drag gesture
					 * @property {Object} defaults
					 * @property {boolean} [defaults.blockHorizontal=false]
					 * @property {boolean} [defaults.blockVertical=false]
					 * @property {number} [defaults.threshold=20]
					 * @property {number} [defaults.delay=0]
					 * @member ns.event.gesture.Drag
					 */
					defaults: {
						blockHorizontal: false,
						blockVertical: false,
						threshold: 20,
						delay: 0
					},

				/**
					 * Triggered
					 * @property {boolean} [isTriggered=false]
					 * @member ns.event.gesture.Drag
					 */
					isTriggered: false,

				/**
					 * Handler for drag gesture
					 * @method handler
					 * @param {Event} gestureEvent gesture event
					 * @param {Object} sender event's sender
					 * @param {Object} options options
					 * @return {number}
					 * @member ns.event.gesture.Drag
					 */
					handler: function (gestureEvent, sender, options) {
						var newGestureEvent,
							threshold = options.threshold,
							result = RESULTS.PENDING,
							direction = gestureEvent.direction;

						if (!this.isTriggered && gestureEvent.eventType === gesture.Event.MOVE) {
							if (Math.abs(gestureEvent.deltaX) < threshold && Math.abs(gestureEvent.deltaY) < threshold) {
							// Branching statement for specifying Tizen 2.X and Tizen 3.0
								if (isChromeBrowser) {
									gestureEvent.preventDefault();
								}
								return RESULTS.PENDING;
							}

							if (options.delay && gestureEvent.deltaTime < options.delay) {
								if (!isTizenWebkit2Browser) {
									gestureEvent.preventDefault();
								}
								return RESULTS.PENDING;
							}

							if (options.blockHorizontal && gestureUtils.isHorizontal(gestureEvent.direction) ||
								options.blockVertical && gestureUtils.isVertical(gestureEvent.direction)) {
								return RESULTS.FINISHED;
							}

							this.fixedStartPointX = 0;
							this.fixedStartPointY = 0;
							if (gestureUtils.isHorizontal(gestureEvent.direction)) {
								this.fixedStartPointX = (gestureEvent.deltaX < 0 ? 1 : -1) * threshold;
							} else {
								this.fixedStartPointY = (gestureEvent.deltaY < 0 ? 1 : -1) * threshold;
							}
						}

						if (options.blockHorizontal) {
							direction = gestureEvent.deltaY < 0 ? gesture.Direction.UP : gesture.Direction.DOWN;
						}

						if (options.blockVertical) {
							direction = gestureEvent.deltaX < 0 ? gesture.Direction.LEFT : gesture.Direction.RIGHT;
						}

						newGestureEvent = merge({}, gestureEvent, {
							deltaX: gestureEvent.deltaX + this.fixedStartPointX,
							deltaY: gestureEvent.deltaY + this.fixedStartPointY,
							estimatedDeltaX: gestureEvent.estimatedDeltaX + this.fixedStartPointX,
							estimatedDeltaY: gestureEvent.estimatedDeltaY + this.fixedStartPointY,

							direction: direction
						});

						switch (newGestureEvent.eventType) {
							case gesture.Event.START:
								this.isTriggered = false;
								if (sender.sendEvent(eventNames.prepare, newGestureEvent) === false) {
									result = RESULTS.FINISHED;
								}
								break;
							case gesture.Event.MOVE:
								if (!this.isTriggered && sender.sendEvent(eventNames.start, newGestureEvent) === false) {
									newGestureEvent.preventDefault();
								}
								result = sender.sendEvent(eventNames.drag, newGestureEvent) ? RESULTS.RUNNING : RESULTS.FINISHED;
								if (result === false) {
									newGestureEvent.preventDefault();
								}
								this.isTriggered = true;
								break;

							case gesture.Event.BLOCKED:
							case gesture.Event.END:
								result = RESULTS.FINISHED;
								if (this.isTriggered) {
									if (sender.sendEvent(eventNames.end, newGestureEvent) === false) {
										newGestureEvent.preventDefault();
									}
									this.isTriggered = false;
								}
								break;

							case gesture.Event.CANCEL:
								result = RESULTS.FINISHED;
								if (this.isTriggered) {
									if (sender.sendEvent(eventNames.cancel, newGestureEvent) === false) {
										newGestureEvent.preventDefault();
									}
									this.isTriggered = false;
								}
								break;
						}

						return result;
					}
				});

			ns.event.gesture.Drag = Drag;
		}(ns, window, window.tizen));

/*global ns, window, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Gesture Plugin: swipe
 * Plugin enables swipe event.
 *
 * @class ns.event.gesture.Swipe
 */
(function (ns) {
	"use strict";
	
			var gesture = ns.event.gesture,
				Result = gesture.Result,
				Detector = gesture.Detector,
				Swipe = Detector.plugin.create({
				/**
					 * Gesture name
					 * @property {string} [name="swipe"]
					 * @member ns.event.gesture.Swipe
					 */
					name: "swipe",

				/**
					 * Gesture Index
					 * @property {number} [index=400]
					 * @member ns.event.gesture.Swipe
					 */
					index: 400,

				/**
					 * Default values for swipe gesture
					 * @property {Object} defaults
					 * @property {number} [defaults.timeThreshold=400]
					 * @property {number} [defaults.velocity=0.6]
					 * @property {ns.event.gesture.HORIZONTAL|ns.event.gesture.VERTICAL} [defaults.orientation=ns.event.gesture.HORIZONTAL]
					 * @member ns.event.gesture.Swipe
					 */
					defaults: {
						timeThreshold: 400,
						velocity: 0.6,
						orientation: gesture.Orientation.HORIZONTAL
					},

				/**
					 * Handler for swipe gesture
					 * @method handler
					 * @param {Event} gestureEvent gesture event
					 * @param {Object} sender event's sender
					 * @param {Object} options options
					 * @return {number}
					 * @member ns.event.gesture.Swipe
					 */
					handler: function (gestureEvent, sender, options) {
						var result = Result.PENDING,
							velocity = options.velocity;

						if (gestureEvent.eventType === gesture.Event.END) {
							if ((gestureEvent.deltaTime > options.timeThreshold) ||
								(options.orientation !== gesture.utils.getOrientation(gestureEvent.direction))) {
								result = Result.FINISHED;
							} else if (gestureEvent.velocityX > velocity || gestureEvent.velocityY > velocity) {
								sender.sendEvent(this.name, gestureEvent);
								result = Result.FINISHED | Result.BLOCK;
							}
						}

						return result;
					}
				});

			gesture.Swipe = Swipe;

		}(ns));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Gesture Plugin: pinch
 * Plugin enables pinch event.
 *
 * @class ns.event.gesture.Pinch
 */
(function (ns) {
	"use strict";
	
		/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.Pinch
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture,
			/**
				 * Local alias for {@link ns.event.gesture.Detector}
				 * @property {Object}
				 * @member ns.event.gesture.Pinch
				 * @private
				 * @static
				 */
				Result = gesture.Result,

				Detector = ns.event.gesture.Detector,
				eventNames = {
					start: "pinchstart",
					move: "pinchmove",
					end: "pinchend",
					cancel: "pinchcancel",
					in: "pinchin",
					out: "pinchout"
				},

				Pinch = Detector.plugin.create({
				/**
					 * Gesture name
					 * @property {string} [name="pinch"]
					 * @member ns.event.gesture.Pinch
					 */
					name: "pinch",

				/**
					 * Gesture Index
					 * @property {number} [index=300]
					 * @member ns.event.gesture.Pinch
					 */
					index: 300,

				/**
					 * Array of possible pinch events
					 * @property {Object} eventNames
					 * @member ns.event.gesture.Pinch
					 */
					eventNames: eventNames,

				/**
					 * Default values for pinch gesture
					 * @property {Object} defaults
					 * @property {number} [defaults.velocity=0.6]
					 * @property {number} [defaults.timeThreshold=400]
					 * @member ns.event.gesture.Pinch
					 */
					defaults: {
						velocity: 0.6,
						timeThreshold: 400
					},

				/**
					 * Triggered
					 * @property {boolean} [isTriggered=false]
					 * @member ns.event.gesture.Pinch
					 */
					isTriggered: false,

				/**
					 * Handler for pinch gesture
					 * @method handler
					 * @param {Event} gestureEvent gesture event
					 * @param {Object} sender event's sender
					 * @param {Object} options options
					 * @return {number}
					 * @member ns.event.gesture.Pinch
					 */
					handler: function (gestureEvent, sender, options) {
						var result = Result.PENDING,
							prevented;

						switch (gestureEvent.eventType) {
							case gesture.Event.MOVE:
								if (gestureEvent.pointers.length === 1 && gestureEvent.distance > 35) {
									result = Result.FINISHED;
								} else if (!this.isTriggered && gestureEvent.pointers.length >= 2) {
									this.isTriggered = true;
									if (sender.sendEvent(eventNames.start, gestureEvent) === false) {
										gestureEvent.preventDefault();
									}
									result = Result.RUNNING;
								} else if (this.isTriggered) {
									if ((gestureEvent.deltaTime < options.timeThreshold) &&
										(gestureEvent.velocityX > options.velocity || gestureEvent.velocityY > options.velocity)) {
										if (gestureEvent.scale < 1) {
											prevented = sender.sendEvent(eventNames.in, gestureEvent);
										} else {
											prevented = sender.sendEvent(eventNames.out, gestureEvent);
										}
										if (prevented === false) {
											gestureEvent.preventDefault();
										}
										this.isTriggered = false;
										result = Result.FINISHED | Result.BLOCK;
										return result;
									} else {
										if (sender.sendEvent(eventNames.move, gestureEvent) === false) {
											gestureEvent.preventDefault();
										}
										result = Result.RUNNING;
									}
								}
								break;
							case gesture.Event.BLOCKED:
							case gesture.Event.END:
								if (this.isTriggered) {
									if (sender.sendEvent(eventNames.end, gestureEvent) === false) {
										gestureEvent.preventDefault();
									}
									this.isTriggered = false;
									result = Result.FINISHED;
								}
								break;
							case gesture.Event.CANCEL:
								if (this.isTriggered) {
									if (sender.sendEvent(eventNames.cancel, gestureEvent) === false) {
										gestureEvent.preventDefault();
									}
									this.isTriggered = false;
									result = Result.FINISHED;
								}
								break;
						}
						return result;
					}
				});

			ns.event.gesture.Pinch = Pinch;
		}(ns));

/*global ns, window, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Gesture Plugin: longPress
 * Plugin enables long press event.
 *
 * @class ns.event.gesture.LongPress
 */
(function (ns) {
	"use strict";
	
		/**
			 * Local alias for {@link ns.event.gesture}
			 * @property {Object}
			 * @member ns.event.gesture.LongPress
			 * @private
			 * @static
			 */
			var gesture = ns.event.gesture,
			/**
				 * Local alias for {@link ns.event.gesture.Detector}
				 * @property {Object}
				 * @member ns.event.gesture.LongPress
				 * @private
				 * @static
				 */
				Detector = gesture.Detector,

				LongPress = Detector.plugin.create({
				/**
					 * Gesture name
					 * @property {string} [name="longpress"]
					 * @member ns.event.gesture.LongPress
					 */
					name: "longpress",

				/**
					 * Gesture Index
					 * @property {number} [index=200]
					 * @member ns.event.gesture.LongPress
					 */
					index: 600,

				/**
					 * Default values for longPress gesture
					 * @property {Object} defaults
					 * @property {number} [defaults.timeThreshold=400]
					 * @property {number} [defaults.longPressDistanceThreshold=15]
					 * @property {boolean} [defaults.preventClick]
					 * @member ns.event.gesture.LongPress
					 */
					defaults: {
						longPressTimeThreshold: 750,
						longPressDistanceThreshold: 20,
						preventClick: true
					},

				/**
					 * IsTriggered
					 * @property {boolean} [isTriggered=false]
					 * @member ns.event.gesture.LongPress
					 */
					isTriggered: false,

				/**
					 * longPressTimeOutId
					 * @property {number} [longPressTimeOutId=0]
					 * @member ns.event.gesture.LongPress
					 */
					longPressTimeOutId: 0,

				/**
					 * Handler for longPress gesture
					 * @method handler
					 * @param {Event} gestureEvent gesture event
					 * @param {Object} sender event's sender
					 * @param {Object} options options
					 * @return {number}
					 * @member ns.event.gesture.LongPress
					 */
					handler: function (gestureEvent, sender, options) {
						var result = gesture.Result.PENDING;

						switch (gestureEvent.eventType) {
							case gesture.Event.START:
								this.isTriggered = false;
								this.longPressTimeOutId = setTimeout(function () {
									this.isTriggered = true;
									sender.sendEvent(this.name, gestureEvent);
								}.bind(this), options.longPressTimeThreshold);
								break;

							case gesture.Event.MOVE:
								if (gestureEvent.distance > options.longPressDistanceThreshold && !this.isTriggered) {
									clearTimeout(this.longPressTimeOutId);
									result = gesture.Result.FINISHED;
								}
								break;

							case gesture.Event.END:
								if (!this.isTriggered) {
									clearTimeout(this.longPressTimeOutId);
								} else if (options.preventClick) {
									gestureEvent.preventDefault();
								}
								result = gesture.Result.FINISHED;
								break;
						}
						return result;
					}
				});

			gesture.LongPress = LongPress;

		}(ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Scroller namespace
 * Namespace contains classes and objects connected with scroller widget.
 * @class ns.widget.core.scroller
 * @internal
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window, ns) {
	"use strict";
				ns.widget.core.scroller = ns.widget.core.scroller || {};
			}(window, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #Effect namespace
 * Namespace with effects for scroller widget.
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @internal
 * @class ns.widget.core.scroller.effect
 */
(function (window, ns) {
	"use strict";
				ns.widget.core.scroller.effect = ns.widget.core.scroller.effect || {};
			}(window, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Bouncing effect
 * Bouncing effect for scroller widget.
 * @class ns.widget.core.scroller.effect.Bouncing
 * @internal
 * @since 2.3
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller
			var utilsObject = ns.util.object,
				selectors = ns.util.selectors,
				Bouncing = function (scrollerElement, options) {
					var self = this;

					self._orientation = null;
					self._maxValue = null;

					self._container = null;
					self._minEffectElement = null;
					self._maxEffectElement = null;

					self.options = utilsObject.merge({}, Bouncing.defaults, {scrollEndEffectArea: ns.getConfig("scrollEndEffectArea", Bouncing.defaults.scrollEndEffectArea)});
					/**
					 * target element for bouncing effect
					 * @property {HTMLElement} targetElement
					 * @member ns.widget.core.scroller.effect.Bouncing
					 */
					self._targetElement = null;

					self._isShow = false;
					self._isDrag = false;
					self._isShowAnimating = false;
					self._isHideAnimating = false;

					self._create(scrollerElement, options);
				},
				Orientation = {
					VERTICAL: "vertical",
					HORIZONTAL: "horizontal"
				},
				endEffectAreaType = {
					content: "content",
					screen: "screen"
				},
				defaults = {
					duration: 500,
					scrollEndEffectArea: "content"
				},
				classes = {
					bouncingEffect: "ui-scrollbar-bouncing-effect",
					page: "ui-page",
					left: "ui-left",
					right: "ui-right",
					top: "ui-top",
					bottom: "ui-bottom",
					hide: "ui-hide",
					show: "ui-show"
				};

			Bouncing.Orientation = Orientation;

			Bouncing.defaults = defaults;

			Bouncing.prototype = {
				_create: function (scrollerElement, options) {
					var self = this;

					if (self.options.scrollEndEffectArea === endEffectAreaType.content) {
						self._container = scrollerElement;
					} else {
						self._container = selectors.getClosestByClass(scrollerElement, classes.page);
					}

					self._orientation = options.orientation;
					self._maxValue = self._getValue(options.maxScrollX, options.maxScrollY);

					self._initLayout();
				},

				_initLayout: function () {
					var self = this,
						minElement = self._minEffectElement = document.createElement("DIV"),
						maxElement = self._maxEffectElement = document.createElement("DIV"),
						className = classes.bouncingEffect;

					if (self._orientation === Orientation.HORIZONTAL) {
						minElement.className = className + " " + classes.left;
						maxElement.className = className + " " + classes.right;
					} else {
						minElement.className = className + " " + classes.top;
						maxElement.className = className + " " + classes.bottom;
					}

					self._container.appendChild(minElement);
					self._container.appendChild(maxElement);

					minElement.addEventListener("animationEnd", this);
					minElement.addEventListener("webkitAnimationEnd", this);
					minElement.addEventListener("mozAnimationEnd", this);
					minElement.addEventListener("msAnimationEnd", this);
					minElement.addEventListener("oAnimationEnd", this);

					maxElement.addEventListener("animationEnd", this);
					maxElement.addEventListener("webkitAnimationEnd", this);
					maxElement.addEventListener("mozAnimationEnd", this);
					maxElement.addEventListener("msAnimationEnd", this);
					maxElement.addEventListener("oAnimationEnd", this);
				},

				/**
				 * ...
				 * @method drag
				 * @param {number} x
				 * @param {number} y
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				drag: function (x, y) {
					this._isDrag = true;
					this._checkAndShow(x, y);
				},

				/**
				 * ...
				 * @method dragEnd
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				dragEnd: function () {
					var self = this;

					if (self._isShow && !self._isShowAnimating && !self._isHideAnimating) {
						self._beginHide();
					}

					self._isDrag = false;
				},

				/**
				 * Shows effect.
				 * @method show
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				show: function () {
					var self = this;

					if (self._targetElement) {
						self._isShow = true;
						self._beginShow();
					}
				},

				/**
				 * Hides effect.
				 * @method hide
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				hide: function () {
					var self = this;

					if (self._isShow) {
						self._minEffectElement.style.display = "none";
						self._maxEffectElement.style.display = "none";
						self._targetElement.classList.remove(classes.hide);
						self._targetElement.classList.remove(classes.show);
					}
					self._isShow = false;
					self._isShowAnimating = false;
					self._isHideAnimating = false;
					self._targetElement = null;
				},

				_checkAndShow: function (x, y) {
					var self = this,
						val = self._getValue(x, y);

					if (!self._isShow) {
						if (val >= 0) {
							self._targetElement = self._minEffectElement;
							self.show();
						} else if (val <= self._maxValue) {
							self._targetElement = self._maxEffectElement;
							self.show();
						}

					} else if (self._isShow && !self._isDrag && !self._isShowAnimating && !self._isHideAnimating) {
						self._beginHide();
					}
				},

				_getValue: function (x, y) {
					return this._orientation === Orientation.HORIZONTAL ? x : y;
				},

				_beginShow: function () {
					var self = this;

					if (!self._targetElement || self._isShowAnimating) {
						return;
					}

					self._targetElement.style.display = "block";

					self._targetElement.classList.remove(classes.hide);
					self._targetElement.classList.add(classes.show);

					self._isShowAnimating = true;
					self._isHideAnimating = false;
				},

				_finishShow: function () {
					var self = this;

					self._isShowAnimating = false;
					if (!self._isDrag) {
						self._targetElement.classList.remove(classes.show);
						self._beginHide();
					}
				},

				_beginHide: function () {
					var self = this;

					if (self._isHideAnimating) {
						return;
					}

					self._targetElement.classList.remove(classes.show);
					self._targetElement.classList.add(classes.hide);

					self._isHideAnimating = true;
					self._isShowAnimating = false;
				},

				_finishHide: function () {
					var self = this;

					self._isHideAnimating = false;
					self._targetElement.classList.remove(classes.hide);
					self.hide();
					self._checkAndShow();
				},

				/**
				 * Supports events.
				 * @method handleEvent
				 * @param {Event} event
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				handleEvent: function (event) {
					if (event.type.toLowerCase().indexOf("animationend") > -1 && event.animationName.charAt(0) !== "-") {
						if (this._isShowAnimating) {
							this._finishShow();
						} else if (this._isHideAnimating) {
							this._finishHide();
						}
					}
				},

				/**
				 * Destroys effect.
				 * @method destroy
				 * @member ns.widget.core.scroller.effect.Bouncing
				 */
				destroy: function () {
					var self = this,
						maxEffectElement = this._maxEffectElement,
						minEffectElement = this._minEffectElement;

					minEffectElement.removeEventListener("animationEnd", this);
					minEffectElement.removeEventListener("webkitAnimationEnd", this);
					minEffectElement.removeEventListener("mozAnimationEnd", this);
					minEffectElement.removeEventListener("msAnimationEnd", this);
					minEffectElement.removeEventListener("oAnimationEnd", this);

					maxEffectElement.removeEventListener("animationEnd", this);
					maxEffectElement.removeEventListener("webkitAnimationEnd", this);
					maxEffectElement.removeEventListener("mozAnimationEnd", this);
					maxEffectElement.removeEventListener("msAnimationEnd", this);
					maxEffectElement.removeEventListener("oAnimationEnd", this);

					self._container.removeChild(minEffectElement);
					self._container.removeChild(maxEffectElement);

					self._container = null;
					self._minEffectElement = null;
					self._maxEffectElement = null;
					self._targetElement = null;

					self._isShow = null;
					self._orientation = null;
					self._maxValue = null;
				}
			};

			ns.widget.core.scroller.effect.Bouncing = Bouncing;
			}(window.document, ns));

/*global window, define, Event, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Scroller Widget
 * Widget creates scroller on content.
 * @class ns.widget.core.scroller.Scroller
 * @since 2.3
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller
			var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				utilsObject = ns.util.object,
				utilsEvents = ns.event,
				prototype = new BaseWidget(),
				EffectBouncing = ns.widget.core.scroller.effect.Bouncing,
				eventType = {
					/**
					 * event trigger when scroller start
					 * @event scrollstart
					 */
					START: "scrollstart",
					/**
					 * event trigger when scroller move
					 * @event scrollmove
					 */
					MOVE: "scrollmove",
					/**
					 * event trigger when scroller end
					 * @event scrollend
					 */
					END: "scrollend",
					/**
					 * event trigger when scroll is cancel
					 * @event scrollcancel
					 */
					CANCEL: "scrollcancel"
				},

				/*
				 * this option is related operation of scroll bar.
				 * the value is true, scroll bar is shown during touching screen even if content doesn't scroll.
				 * the value is false, scroll bar disappear when there is no movement of the scroll bar.
				 */
				_keepShowingScrollbarOnTouch = false,

				Scroller = function () {
				};

			Scroller.Orientation = {
				VERTICAL: "vertical",
				HORIZONTAL: "horizontal"
			};

			Scroller.EventType = eventType;

			prototype._build = function (element) {
				if (element.children.length !== 1) {
					ns.error("[Scroller] Scroller should have only one child.");
				} else {

					this.scroller = element.children[0];
					this.scrollerStyle = this.scroller.style;

					this.bouncingEffect = null;
					this.scrollbar = null;

					this.scrollerWidth = 0;
					this.scrollerHeight = 0;
					this.scrollerOffsetX = 0;
					this.scrollerOffsetY = 0;

					this.maxScrollX = 0;
					this.maxScrollY = 0;

					this.startScrollerOffsetX = 0;
					this.startScrollerOffsetY = 0;

					this.orientation = null;

					this.enabled = true;
					this.scrolled = false;
					this.dragging = false;
					this.scrollCanceled = false;
				}

				return element;
			};

			prototype._configure = function () {
				/**
				 * @property {Object} options Options for widget
				 * @property {number} [options.scrollDelay=0]
				 * @property {number} [options.threshold=10]
				 * @property {""|"bar"|"tab"} [options.scrollbar=""]
				 * @property {boolean} [options.useBouncingEffect=true]
				 * @property {"vertical"|"horizontal"} [options.orientation="vertical"]
				 * @member ns.widget.core.Scroller
				 */
				this.options = utilsObject.merge({}, this.options, {
					scrollDelay: 0,
					threshold: 30,
					scrollbar: "",
					useBouncingEffect: true,
					orientation: "vertical"	// vertical or horizontal,
				});
			};

			prototype._init = function (element) {
				var scroller = null,
					options = this.options,
					scrollerChildren = null,
					elementStyle = element.style,
					scrollerStyle = null,
					elementHalfWidth = element.offsetWidth / 2,
					elementHalfHeight = element.offsetHeight / 2;

				scroller = element.children[0];
				this.scroller = scroller;
				scrollerStyle = scroller.style,
				this.scrollerStyle = scrollerStyle;
				scrollerChildren = scroller.children;

				this.orientation = this.orientation ||
					(options.orientation === "horizontal" ? Scroller.Orientation.HORIZONTAL : Scroller.Orientation.VERTICAL);
				this.scrollerWidth = scroller.offsetWidth;
				this.scrollerHeight = scroller.offsetHeight;

				if (scrollerChildren.length) {
					this.maxScrollX = elementHalfWidth - this.scrollerWidth + scrollerChildren[scrollerChildren.length - 1].offsetWidth / 2;
					this.maxScrollY = elementHalfHeight - this.scrollerHeight + scrollerChildren[scrollerChildren.length - 1].offsetHeight / 2;
					this.minScrollX = elementHalfWidth - scrollerChildren[0].offsetWidth / 2;
					this.minScrollY = elementHalfHeight - scrollerChildren[0].offsetHeight / 2;
				} else {
					this.maxScrollY = 360;
					this.minScrollY = 0;
				}

				this.scrolled = false;
				this.touching = true;
				this.scrollCanceled = false;

				if (this.orientation === Scroller.Orientation.HORIZONTAL) {
					this.maxScrollY = 0;
				} else {
					this.maxScrollX = 0;
				}
				elementStyle.overflow = "hidden";
				elementStyle.position = "relative";
				scrollerStyle.position = "absolute";
				scrollerStyle.top = "0px";
				scrollerStyle.left = "0px";
				scrollerStyle.width = this.scrollerWidth + "px";
				scrollerStyle.height = this.scrollerHeight + "px";
				this._initScrollbar();
				this._initBouncingEffect();
				return element;
			};

			prototype._initScrollbar = function () {
				var type = this.options.scrollbar,
					scrollbarType;

				if (type) {
					scrollbarType = ns.widget.core.scroller.scrollbar.type[type];
					if (scrollbarType) {
						this.scrollbar = engine.instanceWidget(this.element, "ScrollBar", {
							type: scrollbarType,
							orientation: this.orientation
						});
					}
				}
			};

			prototype._initBouncingEffect = function () {
				var o = this.options;

				if (o.useBouncingEffect) {
					this.bouncingEffect = new EffectBouncing(this.element, {
						maxScrollX: this.maxScrollX,
						maxScrollY: this.maxScrollY,
						orientation: this.orientation
					});
				}
			};

			prototype._resetLayout = function () {
				var elementStyle = this.element.style,
					scrollerStyle = this.scrollerStyle;

				elementStyle.overflow = "";
				elementStyle.position = "";

				elementStyle.overflow = "hidden";
				elementStyle.position = "relative";

				if (scrollerStyle) {
					scrollerStyle.position = "";
					scrollerStyle.top = "";
					scrollerStyle.left = "";
					scrollerStyle.width = "";
					scrollerStyle.height = "";

					scrollerStyle["-webkit-transform"] = "";
					scrollerStyle["-moz-transition"] = "";
					scrollerStyle["-ms-transition"] = "";
					scrollerStyle["-o-transition"] = "";
					scrollerStyle["transition"] = "";
				}
			};

			prototype._bindEvents = function () {
				ns.event.enableGesture(
					this.scroller,

					new ns.event.gesture.Drag({
						threshold: this.options.threshold,
						delay: this.options.scrollDelay,
						blockVertical: this.orientation === Scroller.Orientation.HORIZONTAL,
						blockHorizontal: this.orientation === Scroller.Orientation.VERTICAL
					})
				);

				utilsEvents.on(this.scroller, "drag dragstart dragend dragcancel", this);
				window.addEventListener("resize", this);
			};

			prototype._unbindEvents = function () {
				if (this.scroller) {
					ns.event.disableGesture(this.scroller);
					utilsEvents.off(this.scroller, "drag dragstart dragend dragcancel", this);
					window.removeEventListener("resize", this);
				}
			};

			/* jshint -W086 */
			prototype.handleEvent = function (event) {
				switch (event.type) {
					case "dragstart":
						this._start(event);
						break;
					case "drag":
						this._move(event);
						break;
					case "dragend":
						this._end(event);
						break;
					case "dragcancel":
						this._cancel(event);
						break;
					case "resize":
						this.refresh();
						break;
				}
			};

			prototype._refresh = function () {
				this._unbindEvents();
				this._clear();
				this._init(this.element);
				this._bindEvents();
			};

			/**
			 * Scrolls to new position.
			 * @method scrollTo
			 * @param {number} x
			 * @param {number} y
			 * @param {number} duration
			 * @member ns.widget.core.scroller.Scroller
			 */
			prototype.scrollTo = function (x, y, duration) {
				this._translate(x, y, duration);
				this._translateScrollbar(x, y, duration);
			};

			prototype._translate = function (x, y, duration) {
				var translate,
					transition = {
						normal: "none",
						webkit: "none",
						moz: "none",
						ms: "none",
						o: "none"
					},
					scrollerStyle = this.scrollerStyle;

				if (duration) {
					transition.normal = "transform " + duration / 1000 + "s ease-out";
					transition.webkit = "-webkit-transform " + duration / 1000 + "s ease-out";
					transition.moz = "-moz-transform " + duration / 1000 + "s ease-out";
					transition.ms = "-ms-transform " + duration / 1000 + "s ease-out";
					transition.o = "-o-transform " + duration / 1000 + "s ease-out";
				}
				translate = "translate3d(" + x + "px," + y + "px, 0)";

				scrollerStyle["-webkit-transform"] =
					scrollerStyle["-moz-transform"] =
						scrollerStyle["-ms-transform"] =
							scrollerStyle["-o-transform"] =
								scrollerStyle.transform = translate;
				scrollerStyle.transition = transition.normal;
				scrollerStyle["-webkit-transition"] = transition.webkit;
				scrollerStyle["-moz-transition"] = transition.moz;
				scrollerStyle["-ms-transition"] = transition.ms;
				scrollerStyle["-o-transition"] = transition.o;

				this.scrollerOffsetX = window.parseInt(x, 10);
				this.scrollerOffsetY = window.parseInt(y, 10);
			};

			prototype._translateScrollbar = function (x, y, duration, autoHidden) {
				if (!this.scrollbar) {
					return;
				}

				this.scrollbar.translate(this.orientation === Scroller.Orientation.HORIZONTAL ? -x : -y, duration, autoHidden);
			};

			prototype._start = function () {
				var self = this;

				self.scrolled = false;
				self.dragging = true;
				self.scrollCanceled = false;
				self.startScrollerOffsetX = self.scrollerOffsetX;
				self.startScrollerOffsetY = self.scrollerOffsetY;
			};

			prototype._move = function (event) {
				var newX = this.startScrollerOffsetX,
					newY = this.startScrollerOffsetY,
					autoHide = !_keepShowingScrollbarOnTouch;

				if (!this.enabled || this.scrollCanceled || !this.dragging) {
					return;
				}

				if (this.orientation === Scroller.Orientation.HORIZONTAL) {
					newX += event.detail.estimatedDeltaX;
				} else {
					newY += event.detail.estimatedDeltaY;
				}

				if (newX > this.minScrollX || newX < this.maxScrollX) {
					newX = newX > this.minScrollX ? this.minScrollX : this.maxScrollX;
				}
				if (newY > this.minScrollY || newY < this.maxScrollY) {
					newY = newY > this.minScrollY ? this.minScrollY : this.maxScrollY;
				}

				if (newX !== this.scrollerOffsetX || newY !== this.scrollerOffsetY) {
					if (!this.scrolled) {
						this.trigger(eventType.START);
					}
					this.scrolled = true;

					this._translate(newX, newY);
					this._translateScrollbar(newX, newY, 0, autoHide);
					// TODO to dispatch move event is too expansive. it is better to use callback.
					this.trigger(eventType.MOVE);

					if (this.bouncingEffect) {
						this.bouncingEffect.hide();
					}
				} else {
					if (this.bouncingEffect) {
						this.bouncingEffect.drag(newX, newY);
					}
					this._translateScrollbar(newX, newY, 0, autoHide);
				}
			};

			prototype._end = function () {
				if (this.dragging) {

					// bouncing effect
					if (this.bouncingEffect) {
						this.bouncingEffect.dragEnd();
					}

					if (this.scrollbar) {
						this.scrollbar.end();
					}

					this._endScroll();
					this.dragging = false;
				}
			};

			prototype._endScroll = function () {
				if (this.scrolled) {
					this.trigger(eventType.END);
				}

				this.scrolled = false;
			};

			/**
			 * Cancels scroll.
			 * @method _cancel
			 * @protected
			 * @member ns.widget.core.scroller.Scroller
			 */
			prototype._cancel = function () {
				this.scrollCanceled = true;

				if (this.scrolled) {
					this._translate(this.startScrollerOffsetX, this.startScrollerOffsetY);
					this._translateScrollbar(this.startScrollerOffsetX, this.startScrollerOffsetY);
					this.trigger(eventType.CANCEL);
				}

				if (this.scrollbar) {
					this.scrollbar.end();
				}

				this.scrolled = false;
				this.dragging = false;
			};

			prototype._clear = function () {
				this.scrolled = false;
				this.scrollCanceled = false;

				this._resetLayout();
				this._clearScrollbar();
				this._clearBouncingEffect();
			};

			prototype._clearScrollbar = function () {
				if (this.scrollbar) {
					this.scrollbar.destroy();
				}
				this.scrollbar = null;
			};

			prototype._clearBouncingEffect = function () {
				if (this.bouncingEffect) {
					this.bouncingEffect.destroy();
				}
				this.bouncingEffect = null;
			};

			prototype._disable = function () {
				this.enabled = false;
			};

			prototype._enable = function () {
				this.enabled = true;
			};

			prototype._destroy = function () {
				this._unbindEvents();
				this._clear();
				this.scrollerStyle = null;
				this.scroller = null;
			};

			Scroller.prototype = prototype;

			ns.widget.core.scroller.Scroller = Scroller;

			engine.defineWidget(
				"Scroller",
				".scroller",
				["scrollTo", "cancel"],
				Scroller
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #Scrollbar namespace
 * Namespace with scrollbar for scroller widget.
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @class ns.widget.core.scroller.scrollbar
 */
(function (window, ns) {
	"use strict";
				ns.widget.core.scroller.scrollbar = ns.widget.core.scroller.scrollbar || {};
			}(window, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #type namespace
 * Namespace with types of scroll bars..
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @class ns.widget.core.scroller.scrollbar.type
 * @internal
 */
(function (window, ns) {
	"use strict";
				/** @namespace ns.widget.core */
			ns.widget.core.scroller.scrollbar.type = ns.widget.core.scroller.scrollbar.type || {};
			}(window, ns));

/*global window, define, Event, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Type Interface
 * Interface for types used in scroll bar widget.
 * @class ns.widget.core.scroller.scrollbar.type.interface
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller

			ns.widget.core.scroller.scrollbar.type.interface = {
				/**
				 * Inserts elements end decorate.
				 * @method insertAndDecorate
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.interface
				 */
				setScrollbarLayout: function (/* options */) {
				},
				/**
				 * Removes element.
				 * @method remove
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.interface
				 */
				remove: function (/* options */) {
				},
				/**
				 * ...
				 * @method start
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.interface
				 */
				start: function (/* scrollbarElement, barElement */) {
				},
				/**
				 * ...
				 * @method end
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.interface
				 */
				end: function (/* scrollbarElement, barElement */) {
				},
				/**
				 * ...
				 * @method offset
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.interface
				 */
				offset: function (/* orientation, offset  */) {
				}
			};
			}(window.document, ns));

/*global window, define, Event, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Bar Type
 * Bar type support for scroll bar widget.
 * @class ns.widget.core.scroller.scrollbar.type.bar
 * @extends ns.widget.core.scroller.scrollbar.type.interface
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller
			var utilsObject = ns.util.object,
				type = ns.widget.core.scroller.scrollbar.type,
				typeInterface = type.interface,
				Scroller = ns.widget.core.scroller.Scroller;

			type.bar = utilsObject.merge({}, typeInterface, {
				options: {
					animationDuration: 500
				},

				/**
				 * @method setScrollbar
				 * @param viewLayout
				 * @param firstChildLayout
				 * @param clipLayout
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.bar
				 */

				setScrollbar: function (viewLayout, firstChildLayout, clipLayout) {
					this._viewLayout = viewLayout;
					this._clipLayout = clipLayout;
					this._firstChildLayout = firstChildLayout;
					this._ratio = clipLayout / firstChildLayout;
				},

				/**
				 * @method getScrollbarSize
				 * @return {number} scrollbar size
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.bar
				 */
				getScrollbarSize: function () {
					return this._firstChildLayout / this._viewLayout * this._firstChildLayout * this._ratio;
				},
				/**
				 * @method offset
				 * @param {string} orientation
				 * @param {number} offset
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.bar
				 */
				offset: function (orientation, offset) {
					var x,
						y;

					offset = offset * this._clipLayout / this._viewLayout;

					if (orientation === Scroller.Orientation.VERTICAL) {
						x = 0;
						y = offset;
					} else {
						x = offset;
						y = 0;
					}

					return {
						x: x,
						y: y
					};
				},

				/**
				 * @method start
				 * @param {HTMLElement} scrollbarElement
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.bar
				 */
				start: function (scrollbarElement/*, barElement */) {
					var style = scrollbarElement.style,
						duration = this.options.animationDuration;

					style["-webkit-transition"] =
						style["-moz-transition"] =
							style["-ms-transition"] =
								style["-o-transition"] =
									style.transition = "opacity " + duration / 1000 + "s ease";
					style.opacity = 1;
				},

				/**
				 * @method end
				 * @param {HTMLElement} scrollbarElement
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.bar
				 */
				end: function (scrollbarElement) {
					var style = scrollbarElement.style,
						duration = this.options.animationDuration;

					style["-webkit-transition"] =
						style["-moz-transition"] =
							style["-ms-transition"] =
								style["-o-transition"] =
									style.transition = "opacity " + duration / 1000 + "s ease";
					style.opacity = 0;
				}
			});

			}(window.document, ns));

/*global window, define, Event, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Tab Type
 * Tab type support for scroll bar widget.
 * @class ns.widget.core.scroller.scrollbar.type.tab
 * @extends ns.widget.core.scroller.scrollbar.type.interface
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller
			var utilsObject = ns.util.object,
				type = ns.widget.core.scroller.scrollbar.type,
				typeInterface = type.interface,
				Scroller = ns.widget.core.scroller.Scroller;

			type.tab = utilsObject.merge({}, typeInterface, {
				options: {
					wrapperClass: "ui-scrollbar-tab-type",
					barClass: "ui-scrollbar-indicator",
					margin: 1
				},
				/**
				 * ...
				 * @method insertAndDecorate
				 * @param {Object} data
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.tab
				 */
				insertAndDecorate: function (data) {
					var scrollbarElement = data.wrapper,
						barElement = data.bar,
						container = data.container,
						clip = data.clip,
						sections = data.sections,
						orientation = data.orientation,
						margin = this.options.margin,
						clipWidth = clip.offsetWidth,
						clipHeight = clip.offsetHeight,
						containerWidth = container.offsetWidth,
						containerHeight = container.offsetHeight,
						clipSize = orientation === Scroller.Orientation.VERTICAL ? clipHeight : clipWidth,
						containerSize = orientation === Scroller.Orientation.VERTICAL ? containerHeight : containerWidth,
						sectionSize = clipSize / containerSize,
						height,
						barHeight,
						i,
						len;

					this.containerSize = containerWidth;
					this.maxScrollOffset = clipSize - containerSize;
					this.scrollZoomRate = containerWidth / clipSize;
					this.barSize = window.parseInt((containerWidth - margin * 2 * (sectionSize - 1)) / sectionSize);

					scrollbarElement.className = this.options.wrapperClass;
					barElement.className = this.options.barClass;

					barElement.style.width = this.barSize + "px";
					barElement.style.left = "0px";

					container.insertBefore(scrollbarElement, clip);

					// reset page container and section layout.
					barHeight = barElement.offsetHeight;
					height = clipHeight - barHeight;
					clip.style.height = height + "px";
					if (sections && sections.length) {
						for (i = 0, len = sections.length; i < len; i++) {
							sections[i].style.height = height + "px";
						}
					}
				},

				/**
				 * ...
				 * @method remove
				 * @param {Object} data
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.tab
				 */
				remove: function (data) {
					var scrollbarElement = data.wrapper,
						container = data.container;

					if (container && scrollbarElement) {
						container.removeChild(scrollbarElement);
					}
				},

				/**
				 * ...
				 * @method offset
				 * @param {string} orientation
				 * @param {number} offset
				 * @static
				 * @member ns.widget.core.scroller.scrollbar.type.tab
				 */
				offset: function (orientation, offset) {
					return {
						x: offset === 0 ? -1 :
							offset === this.maxScrollOffset ? this.containerSize - this.barSize - this.options.margin : offset * this.scrollZoomRate,
						y: 0
					};
				}

			});
			}(window.document, ns));

/*global window, define, Event, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Scroll Bar Widget
 * Widget creates scroll bar.
 * @class ns.widget.core.scroller.scrollbar.ScrollBar
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				// scroller.start event trigger when user try to move scroller
			var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				prototype = new BaseWidget(),
				utilsObject = ns.util.object,
				selectors = ns.util.selectors,
				Page = ns.widget.core.Page,
				Classes = {
					wrapperClass: "ui-scrollbar-bar-type",
					barClass: "ui-scrollbar-indicator",
					orientationClass: "ui-scrollbar-",
					page: Page.classes.uiPage
				},

				Scroller = ns.widget.core.scroller.Scroller,
				ScrollerScrollBar = function () {

					this.wrapper = null;
					this.barElement = null;

					this.container = null;
					this.view = null;

					this.options = {};
					this.type = null;

					this.maxScroll = null;
					this.started = false;
					this.displayDelayTimeoutId = null;

					this.lastScrollPosition = 0;
				};

			prototype._build = function (scrollElement) {
				this.clip = scrollElement;
				this.view = scrollElement.children[0];
				this.firstChild = this.view.children[0];
				return scrollElement;
			};

			prototype._configure = function () {
				/**
				 * @property {Object} options Options for widget
				 * @property {boolean} [options.type=false]
				 * @property {number} [options.displayDelay=700]
				 * @property {"vertical"|"horizontal"} [options.orientation="vertical"]
				 * @member ns.widget.core.scroller.scrollbar.ScrollBar
				 */
				this.options = utilsObject.merge({}, this.options, {
					type: false,
					displayDelay: 700,
					orientation: Scroller.Orientation.VERTICAL
				});
			};

			prototype._init = function (scrollElement) {
				this.clip = scrollElement;
				this.view = scrollElement.children[0];
				this.firstChild = this.view.children[0];
				this.type = this.options.type;

				if (!this.type) {
					return;
				}
				this._createScrollbar();
			};

			prototype._bindEvents = function () {
				document.addEventListener("visibilitychange", this);
			};

			prototype._createScrollbar = function () {
				var orientation = this.options.orientation,
					wrapper = document.createElement("DIV"),
					bar = document.createElement("span"),
					view = this.view,
					clip = this.clip,
					firstChild = this.firstChild,
					type = this.type;

				clip.appendChild(wrapper);
				wrapper.appendChild(bar);
				wrapper.classList.add(Classes.wrapperClass);
				bar.className = Classes.barClass;

				if (orientation === Scroller.Orientation.HORIZONTAL) {
					type.setScrollbar(view.offsetWidth, firstChild.offsetWidth, clip.offsetWidth);
					bar.style.width = type.getScrollbarSize() + "px";
					wrapper.classList.add(Classes.orientationClass + "horizontal");
				} else {
					type.setScrollbar(view.offsetHeight, firstChild.offsetHeight, clip.offsetHeight);
					bar.style.height = type.getScrollbarSize() + "px";
					wrapper.classList.add(Classes.orientationClass + "vertical");
				}

				this.wrapper = wrapper;
				this.barElement = bar;
			};

			prototype._removeScrollbar = function () {
				this.clip.removeChild(this.wrapper);

				this.wrapper = null;
				this.barElement = null;
			};

			prototype._refresh = function () {
				var self = this;

				self._clear();
				self._init();
				self.translate(self.lastScrollPosition);
			};

			/**
			 * Translates widget.
			 * @method translate
			 * @param {number} offset
			 * @param {number} duration
			 * @param {boolean} autoHidden
			 * @member ns.widget.core.scroller.scrollbar.ScrollBar
			 */
			prototype.translate = function (offset, duration, autoHidden) {
				var orientation = this.options.orientation,
					translate,
					transition = {
						normal: "none",
						webkit: "none",
						moz: "none",
						ms: "none",
						o: "none"
					},
					barStyle,
					endDelay;

				if (!this.wrapper || !this.type || this.lastScrollPosition === offset) {
					return;
				}

				autoHidden = autoHidden !== false;

				this.lastScrollPosition = offset;

				offset = this.type.offset(orientation, offset);

				barStyle = this.barElement.style;
				if (duration) {
					transition.normal = "transform " + duration / 1000 + "s ease-out";
					transition.webkit = "-webkit-transform " + duration / 1000 + "s ease-out";
					transition.moz = "-moz-transform " + duration / 1000 + "s ease-out";
					transition.ms = "-ms-transform " + duration / 1000 + "s ease-out";
					transition.o = "-o-transform " + duration / 1000 + "s ease-out";
				}

				translate = "translate3d(" + offset.x + "px," + offset.y + "px, 0)";

				barStyle["-webkit-transform"] =
					barStyle["-moz-transform"] =
						barStyle["-ms-transform"] =
							barStyle["-o-transform"] =
								barStyle.transform = translate;
				barStyle["-webkit-transition"] = transition.webkit;
				barStyle["-moz-transition"] = transition.moz;
				barStyle["-ms-transition"] = transition.ms;
				barStyle["-o-transition"] = transition.o;
				barStyle.transition = transition.normal;

				if (!this.started) {
					this._start();
				}

				if (this.displayDelayTimeoutId !== null) {
					window.clearTimeout(this.displayDelayTimeoutId);
					this.displayDelayTimeoutId = null;
				}

				if (autoHidden) {
					endDelay = (duration || 0) + this.options.displayDelay;
					this.displayDelayTimeoutId = window.setTimeout(this._end.bind(this), endDelay);
				}
			};

			prototype.end = function () {
				if (!this.displayDelayTimeoutId) {
					this.displayDelayTimeoutId = window.setTimeout(this._end.bind(this), this.options.displayDelay);
				}
			};

			prototype._start = function () {
				this.type.start(this.wrapper, this.barElement);
				this.started = true;
			};

			prototype._end = function () {
				this.started = false;
				this.displayDelayTimeoutId = null;

				if (this.type) {
					this.type.end(this.wrapper, this.barElement);
				}
			};

			/**
			 * Supports events.
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.scroller.scrollbar.ScrollBar
			 */
			prototype.handleEvent = function (event) {
				var page;

				switch (event.type) {
					case "visibilitychange":
						page = selectors.getClosestBySelector(this.clip, "." + Classes.page);
						if (document.visibilityState === "visible" && page === ns.activePage) {
							this.refresh();
						}
						break;
				}
			};

			prototype._clear = function () {
				this._removeScrollbar();

				this.started = false;
				this.type = null;
				this.barElement = null;
				this.displayDelayTimeoutId = null;
			};

			prototype._destroy = function () {
				this._clear();
				document.removeEventListener("visibilitychange", this);

				this.options = null;
				this.clip = null;
				this.view = null;
			};

			ScrollerScrollBar.prototype = prototype;

			ns.widget.core.scroller.scrollbar.ScrollBar = ScrollerScrollBar;

			engine.defineWidget(
				"ScrollBar",
				"",
				["translate"],
				ScrollerScrollBar
			);
			}(window.document, ns));

/*global window, ns, define */
/*jslint nomen: true, plusplus: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Easing Utility
 * Utility calculates time function for animations.
 * @class ns.util.easing
 */

(function (ns) {
	"use strict";
				ns.util.easing = {
				/**
				 * Performs cubit out easing calculations based on time
				 * @method cubicOut
				 * @member ns.util.easing
				 * @param {number} currentTime
				 * @param {number} startValue
				 * @param {number} changeInValue
				 * @param {number} duration
				 * @return {number}
				 * @static
				 */
				cubicOut: function (currentTime, startValue, changeInValue, duration) {
					currentTime /= duration;
					currentTime--;
					return changeInValue * (currentTime * currentTime * currentTime + 1) + startValue;
				},

				/**
				 * Performs quad easing out calculations based on time
				 * @method easeOutQuad
				 * @member ns.util.easing
				 * @param {number} currentTime
				 * @param {number} startValue
				 * @param {number} changeInValue
				 * @param {number} duration
				 * @return {number}
				 * @static
				 */
				easeOutQuad: function (currentTime, startValue, changeInValue, duration) {
					return -changeInValue * (currentTime /= duration) * (currentTime - 2) + startValue;
				},

				/**
				 * Performs sine easing out calculations based on time
				 * The easing functions can be compared on: https://easings.net
				 * @method easeOutSine
				 * @member ns.util.easing
				 * @param {number} currentTime
				 * @param {number} startValue
				 * @param {number} changeInValue
				 * @param {number} duration
				 * @return {number}
				 * @static
				 */
				easeOutSine: function (currentTime, startValue, changeInValue, duration) {
					return changeInValue * Math.sin(currentTime / duration * (Math.PI / 2)) + startValue;
				},

				/**
				 * Performs out expo easing calculations based on time
				 * @method easeOutExpo
				 * @member ns.util.easing
				 * @param {number} currentTime
				 * @param {number} startValue
				 * @param {number} changeInValue
				 * @param {number} duration
				 * @return {number}
				 * @static
				 */
				easeOutExpo: function (currentTime, startValue, changeInValue, duration) {
					return (currentTime === duration) ?
						startValue + changeInValue :
						changeInValue * (-Math.pow(2, -10 * currentTime / duration) + 1) +
					startValue;
				},
				/**
				 * Performs out linear calculations based on time
				 * @method linear
				 * @member ns.util.easing
				 * @param {number} currentTime
				 * @param {number} startValue
				 * @param {number} changeInValue
				 * @param {number} duration
				 * @return {number}
				 * @static
				 */
				linear: function (currentTime, startValue, changeInValue, duration) {
					return startValue + changeInValue * currentTime / duration;
				}
			};
			}(ns));

/*global window, define, ns*/
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # ScrollView Widget
 * Widgets allows for creating scrollable panes, lists, etc.
 *
 * ## Default selectors
 * All elements with _data-role=content attribute or _.ui-scrollview
 * css class will be changed to ScrollView widgets, unless they specify
 * _data-scroll=none attribute.
 *
 * ### HTML Examples
 *
 * #### Data attribute
 *
 *        @example
 *        <div data-role="page">
 *            <div data-role="content"><!-- this will become scrollview //-->
 *                content data
 *            </div>
 *        </div>
 *
 * #### CSS Class
 *
 *        @example
 *        <div data-role="page">
 *            <div class="ui-content"><!-- this will become scrollview //-->
 *                content data
 *            </div>
 *        </div>
 *
 * ## Manual constructor
 *
 * To create the widget manually you can use 2 different APIs, the TAU
 * API or jQuery API.
 *
 * ### Create scrollview by TAU API
 *
 *        @example
 *        <div data-role="page" id="myPage">
 *            <div data-role="content">
 *                page content
 *            </div>
 *        </div>
 *        <script>
 *            var page = tau.widget.Page(document.getElementById("myPage")),
 *                scrollview = tau.widget.Scrollview(page.ui.content);
 *        </script>
 *
 * ### Create scrollview using jQuery API
 *
 *        @example
 *        <div data-role="page" id="myPage">
 *            <div data-role="content">
 *                page content
 *            </div>
 *        </div>
 *        <script>
 *            $("#myPage > div[data-role='content']").scrollview();
 *        </script>
 *
 * ## Options for Scrollview widget
 *
 * Options can be set using data-* attributes or by passing them to
 * the constructor.
 *
 * There is also a method **option** for changing them after widget
 * creation.
 *
 * jQuery mobile format is also supported.
 *
 * ## Scroll
 *
 * This options specifies of a content element should become Scrollview
 * widget.
 *
 * You can change this by all available methods for changing options.
 *
 * ### By data-scroll attribute
 *
 *        @example
 *        <div data-role="page">
 *            <div data-role="content" data-scroll="none">
 *                content
 *            </div>
 *        </div>
 *
 * ### By config passed to constructor
 *
 *        @example
 *        <div class="myPageClass" data-role="page">
 *            <div data-role="content">
 *                content
 *            </div>
 *        </div>
 *        <script>
 *            var contentElement = document.querySelector(".myPageClass > div[data-role=content]");
 *            tau.widget.Scrollview(contentElement, {
 *				"scroll": false
 *			});
 *        </script>
 *
 * ### By using jQuery API
 *
 *        @example
 *        <div class="myPageClass" data-role="page">
 *            <div data-role="content">
 *                content
 *            </div>
 *        </div>
 *        <script>
 *            $(".myPageClass > div[data-role='content']").scrollview({
 *				"scroll": false
 *			});
 *        </script>
 *
 * ## ScrollJumps
 *
 * Scroll jumps are small buttons which allow the user to quickly
 * scroll to top or left
 *
 * You can change this by all available methods for changing options.
 *
 * ### By data-scroll-jump
 *
 *        @example
 *        <div data-role="page">
 *            <div data-role="content" data-scroll-jump="true">
 *                content
 *            </div>
 *        </div>
 *
 * ### By config passed to constructor
 *
 *        @example
 *        <div class="myPageClass" data-role="page">
 *            <div data-role="content">
 *                content
 *            </div>
 *        </div>
 *        <script>
 *            var contentElement = document.querySelector(".myPageClass > div[data-role=content]");
 *            tau.widget.Scrollview(contentElement, {
 *				"scrollJump": true
 *			});
 *        </script>
 *
 * ### By using jQuery API
 *
 *        @example
 *        <div class="myPageClass" data-role="page">
 *            <div data-role="content">
 *                content
 *            </div>
 *        </div>
 *        <script>
 *            $(".myPageClass > div[data-role='content']").scrollview({
 *				"scrollJump": true
 *			});
 *        </script>
 *
 * ## Methods
 *
 * Page methods can be called trough 2 APIs: TAU API and jQuery API
 * (jQuery mobile-like API)
 *
 * @class ns.widget.core.Scrollview
 * @extends ns.widget.BaseWidget
 *
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Grzegorz Osimowicz <g.osimowicz@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 */
/**
 * Triggered when scrolling operation starts
 * @event scrollstart
 * @member ns.widget.core.Scrollview
 */
/**
 * Triggered when scroll is being updated
 * @event scrollupdate
 * @member ns.widget.core.Scrollview
 */
/**
 * Triggered when scrolling stops
 * @event scrollstop
 * @member ns.widget.core.Scrollview
 */
(function (window, document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				util = ns.util,
				easingUtils = ns.util.easing,
				eventUtils = ns.event,
				DOMUtils = ns.util.DOM,
				selectors = ns.util.selectors,
				currentTransition = null,
				Page = ns.widget.core.Page,
				pageClass = Page.classes.uiPage,
				pageActiveClass = Page.classes.uiPageActive,
				pageEvents = Page.events,
				Scrollview = function () {
					var self = this,
						ui;
					/**
					 * @property {Object} _scrollState Scrollview internal state object
					 * @property {Function} _scrollState.currentTransition Instance transition function
					 * @readonly
					 */

					self._scrollState = {
						currentTransition: null
					};
					/**
					 * @property {number} scrollDuration The time length of the scroll animation
					 * @member ns.widget.core.Scrollview
					 */
					self.scrollDuration = 300;
					self.scrollviewSetHeight = false;
					/**
					 * Scrollview options
					 * @property {Object} options
					 * @property {string} [options.scroll='y'] Scroll direction
					 * @property {boolean} [options.scrollJump=false] Scroll jump buttons flag
					 * @member ns.widget.core.Scrollview
					 */
					self.options = {
						scroll: "y",
						scrollJump: false,
						scrollIndicator: false
					};
					/**
					 * Dictionary for holding internal DOM elements
					 * @property {Object} ui
					 * @property {HTMLElement} ui.view The main view element
					 * @property {HTMLElement} ui.page The main page element
					 * @property {HTMLElement} ui.jumpHorizontalButton Jump left button
					 * @property {HTMLElement} ui.jumpVerticalButton Jump top button
					 * @member ns.widget.core.Scrollview
					 * @readonly
					 */
					ui = self._ui || {};
					ui.view = null;
					ui.page = null;
					ui.jumpHorizontalButton = null;
					ui.jumpVerticalButton = null;
					self._ui = ui;
					/**
					 * Dictionary for holding internal listeners
					 * @property {Object} _callbacks
					 * @property {Function} _callbacks.repositionJumps Refresh jumps listener
					 * @property {Function} _callbacks.jumpTop Top jump button click callback
					 * @property {Function} _callbacks.jumpLeft Left jump button click callback
					 * @member ns.widget.core.Scrollview
					 * @protected
					 * @readonly
					 */
					self._callbacks = {
						repositionJumps: null,
						jumpTop: null,
						jumpBottom: null
					};

					self._timers = {
						scrollIndicatorHide: null
					};
				},
				/**
				 * Dictionary for scrollview css classes
				 * @property {Object} classes
				 * @property {string} [classes.view='ui-scrollview-view'] View main class
				 * @property {string} [classes.clip='ui-scrollview-clip'] Clip main class
				 * @property {string} [classes.jumpTop='ui-scroll-jump-top-bg'] Jump top button background
				 * @property {string} [classes.jumpLeft='ui-scroll-jump-left-bg'] Jump bottom button background
				 * @member ns.widget.core.Scrollview
				 * @static
				 * @readonly
				 */
				classes = {
					view: "ui-scrollview-view",
					clip: "ui-scrollview-clip",
					jumpTop: "ui-scroll-jump-top-bg",
					jumpLeft: "ui-scroll-jump-left-bg",
					indicatorTop: "ui-overflow-indicator-top",
					indicatorBottom: "ui-overflow-indicator-bottom",
					indicatorTopShown: "ui-scrollindicator-top",
					indicatorBottomShown: "ui-scrollindicator-bottom",
					indicatorLeftShown: "ui-scrollindicator-left",
					indicatorRightShown: "ui-scrollindicator-right"
				};

			// Changes static position to relative
			// @param {HTMLElement} view
			function makePositioned(view) {
				if (DOMUtils.getCSSProperty(view, "position") === "static") {
					view.style.position = "relative";
				} else {
					view.style.position = "absolute";
				}
			}

			// Translation animation loop
			// @param {Object} state Scrollview instance state
			// @param {HTMLElement} element
			// @param {number} startTime
			// @param {number} startX
			// @param {number} startY
			// @param {number} translateX
			// @param {number} translateY
			// @param {number} endX
			// @param {number} endY
			// @param {number} duration
			function translateTransition(state, element, startTime, startX, startY, translateX, translateY, endX, endY, duration) {
				var timestamp = (new Date()).getTime() - startTime,
					newX = parseInt(easingUtils.cubicOut(timestamp, startX, translateX, duration), 10),
					newY = parseInt(easingUtils.cubicOut(timestamp, startY, translateY, duration), 10);

				if (element.scrollLeft !== endX) {
					element.scrollLeft = newX;
				}
				if (element.scrollTop !== endY) {
					element.scrollTop = newY;
				}

				if ((newX !== endX || newY !== endY) &&
					(newX >= 0 && newY >= 0) &&
					state.currentTransition) {
					util.requestAnimationFrame(state.currentTransition);
				} else {
					state.currentTransition = null;
				}
			}

			// Translates scroll position directly or with an animation
			// if duration is specified
			// @param {Object} state Scrollview instance state
			// @param {HTMLElement} element
			// @param {number} x
			// @param {number} y
			// @param {number=} [duration]
			function translate(state, element, x, y, duration) {
				if (duration) {
					state.currentTransition = translateTransition.bind(
						null,
						state,
						element,
						(new Date()).getTime(),
						element.scrollLeft,
						element.scrollTop,
						x,
						y,
						element.scrollLeft + x,
						element.scrollTop + y,
						duration
					);
					util.requestAnimationFrame(state.currentTransition);
				} else {
					if (x) {
						element.scrollLeft = element.scrollLeft + x;
					}
					if (y) {
						element.scrollTop = element.scrollTop + y;
					}
				}
			}

			// Refresh jumpTop jumpLeft buttons
			// @param {ns.widget.core.Scrollview} self
			function repositionJumps(self) {
				var ui = self._ui,
					horizontalJumpButton = ui.jumpHorizontalButton,
					verticalJumpButton = ui.jumpVerticalButton,
					offsets = horizontalJumpButton || verticalJumpButton ? DOMUtils.getElementOffset(self.element) : null; // don't calc when not used

				if (horizontalJumpButton) {
					horizontalJumpButton.style.left = offsets.left + "px";
				}

				if (verticalJumpButton) {
					verticalJumpButton.style.top = offsets.top + "px";
				}
			}

			Scrollview.classes = classes;

			Scrollview.prototype = new BaseWidget();

			/**
			 * Builds the widget
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @method _build
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._build = function (element) {
				//@TODO wrap element's content with external function
				var self = this,
					ui = self._ui,
					view = selectors.getChildrenByClass(element, classes.view)[0] || document.createElement("div"),
					clipStyle = element.style,
					node,
					child = element.firstChild,
					options = self.options,
					direction = options.scroll,
					jumpButton,
					jumpBackground;

				view.className = classes.view;

				while (child) {
					node = child;
					child = child.nextSibling;
					if (view !== node) {
						view.appendChild(node);
					}
				}

				if (view.parentNode !== element) {
					element.appendChild(view);
				}

				// setting view style
				makePositioned(view);

				element.classList.add(classes.clip);

				// Adding ui-content class for the proper styling with CE
				element.classList.add("ui-content");

				switch (direction) {
					case "x":
						clipStyle.overflowX = "scroll";
						break;
					case "xy":
						clipStyle.overflow = "scroll";
						break;
					default:
						clipStyle.overflowY = "auto";
						break;
				}

				if (options.scrollJump) {
					if (direction.indexOf("x") > -1) {
						jumpBackground = document.createElement("div");
						jumpBackground.className = classes.jumpLeft;
						jumpButton = document.createElement("div");

						jumpBackground.appendChild(jumpButton);
						element.appendChild(jumpBackground);
						engine.instanceWidget(
							jumpButton,
							"Button",
							{
								"icon": "scrollleft",
								"style": "box"
							}
						);
						ui.jumpHorizontalButton = jumpBackground;
					}

					if (direction.indexOf("y") > -1) {
						jumpBackground = document.createElement("div");
						jumpBackground.className = classes.jumpTop;
						jumpButton = document.createElement("div");

						jumpBackground.appendChild(jumpButton);
						element.appendChild(jumpBackground);
						engine.instanceWidget(
							jumpButton,
							"Button",
							{
								"icon": "scrolltop",
								"style": "box"
							}
						);
						ui.jumpVerticalButton = jumpBackground;
					}
				}

				ui.view = view;

				// add scroll indicators
				if (options.scrollIndicator) {
					self._addOverflowIndicator(element);
				}

				return element;
			};

			/**
			 * Inits widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._init = function (element) {
				var ui = this._ui,
					page = ui.page;

				if (!ui.view) {
					ui.view = selectors.getChildrenByClass(element, classes.view)[0];
				}

				if (!page) {
					page = selectors.getClosestByClass(element, pageClass);
					if (page) {
						ui.page = page;
						if (page.classList.contains(pageActiveClass) && this.options.scrollJump) {
							repositionJumps(this);
						}
					}
				}
			};

			/**
			 * Adds overflow indicators
			 * @param {HTMLElement} clip
			 * @method _addOverflowIndicator
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._addOverflowIndicator = function (clip) {
				clip.insertAdjacentHTML("beforeend",
					"<div class='" + classes.indicatorTop + "'></div><div class='" + classes.indicatorBottom + "'></div>");
			};

			/**
			 * Clear classes and styles of indicators
			 * @param {HTMLElement} element
			 * @method clearIndicator
			 * @private
			 * @member ns.widget.core.Scrollview
			 */
			function clearIndicator(element) {
				var clipClasses = element.classList,
					topIndicator = selectors.getChildrenByClass(element, classes.indicatorTop)[0],
					bottomIndicator = selectors.getChildrenByClass(element, classes.indicatorBottom)[0];

				clipClasses.remove(classes.indicatorTopShown);
				clipClasses.remove(classes.indicatorBottomShown);
				clipClasses.remove(classes.indicatorRightShown);
				clipClasses.remove(classes.indicatorLeftShown);
				topIndicator.style = "";
				bottomIndicator.style = "";
			}

			/**
			 * Set top and bottom indicators
			 * @param {HTMLElement} clip
			 * @param {Object} options
			 * @method setTopAndBottomIndicators
			 * @private
			 * @member ns.widget.core.Scrollview
			 */
			function setTopAndBottomIndicators(clip, options) {
				var topIndicator = selectors.getChildrenByClass(clip, classes.indicatorTop)[0],
					bottomIndicator = selectors.getChildrenByClass(clip, classes.indicatorBottom)[0],
					style;

				// set top indicator
				if (topIndicator) {
					style = topIndicator.style;
					style.width = options.width + "px";
					style.top = options.clipTop + "px";
					style.backgroundColor = options.color;
				}
				if (bottomIndicator) {
					// set bottom indicator
					style = bottomIndicator.style;
					style.width = options.width + "px";
					style.top = options.clipTop + options.clipHeight - DOMUtils.getElementHeight(bottomIndicator) + "px";
					style.backgroundColor = options.color;
				}
			}

			/**
			 * Show scroll indicators.
			 * @method _showScrollIndicator
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._showScrollIndicator = function () {
				var self = this,
					clip = self.element,
					view = self._ui.view,
					scrollTop = clip.scrollTop,
					clipHeight = DOMUtils.getElementHeight(clip),
					clipOffset = DOMUtils.getElementOffset(clip),
					viewHeight = DOMUtils.getElementHeight(view),
					viewWidth = DOMUtils.getElementWidth(view),
					viewOffset = DOMUtils.getElementOffset(view);

				clearIndicator(clip);

				switch (self.options.scroll) {
					case "x":
					case "xy":
						// @todo
						break;
					default:
						setTopAndBottomIndicators(clip, {
							clipTop: clipOffset.top,
							clipHeight: clipHeight,
							width: viewWidth,
							color: window.getComputedStyle(clip).backgroundColor
						});
						if (viewOffset.top - scrollTop < clipOffset.top) {
							// the top is not visible
							clip.classList.add(classes.indicatorTopShown);
						}
						if (viewOffset.top - scrollTop + viewHeight > clipOffset.top + clipHeight) {
							// the bottom is not visible
							clip.classList.add(classes.indicatorBottomShown);
						}
				}
			};

			/**
			 * Hide scroll indicators.
			 * @method _hideScrollIndicator
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._hideScrollIndicator = function () {
				var self = this,
					timers = self._timers,
					timer = timers.scrollIndicatorHide;

				if (timer) {
					window.clearTimeout(timer);
				}
				timers.scrollIndicatorHide = window.setTimeout(function () {
					clearIndicator(self.element);
				}, 1500);
			};

			/**
			 * Scrolls to specified position
			 *
			 * This method give possibility to scroll on Scrollview widget form JS interface of widget.
			 *
			 * <mobile>
			 * On mobile profile you can use method in jQuery style.
			 * </mobile>
			 *
			 * If duration is set then scroll will be animated in given time period.
			 *
			 * <wearable>
			 * On wearable profile Scrollview widget isn't build automatically. Before using method scrollTo, you need
			 * create widget on content of page.
			 * </wearable>
			 * ### Example usage with TAU API
			 *
			 *		@example mobile wearable
			 *		<div class="myPageClass" data-role="page">
			 *			<div data-role="content" data-scroll="y">
			 *				content
			 *			</div>
			 *		</div>
			 *		<script>
			 *			var scrollview = tau.widget.Scrollview(document.querySelector(".myPageClass > div[data-role=content]"));
			 *			scrollview.scrollTo(0, 200, 1000); // scroll to 200px vertical with 1s animation
			 *		</script>
			 *
			 * ### Example usage with jQuery API
			 *
			 *		@example mobile
			 *		<div class="myPageClass" data-role="page">
			 *			<div data-role="content" data-scroll="y">
			 *				content
			 *			</div>
			 *		</div>
			 *		<script>
			 *			var element = $(".myPageClass > div[data-role=content]"));
			 *			element.scrollview();
			 *			element.scrollview("scrollTo", 0, 200, 1000); // scroll to 200px vertical with 1s animation
			 *		</script>
			 *
			 * @param {number} x
			 * @param {number} y
			 * @param {number=} [duration]
			 * @method scrollTo
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype.scrollTo = function (x, y, duration) {
				var element = this.element;

				this.translateTo(x - element.scrollLeft, y - element.scrollTop, duration);
			};

			/**
			 * Translates the scroll to specified position
			 *
			 * ### Example usage with TAU API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var scrollview = tau.widget.Scrollview(document.querySelector(".myPageClass > div[data-role=content]"));
			 *            scrollview.translateTo(0, 200, 1000); // scroll forward 200px in vertical direction with 1s animation
			 *        </script>
			 *
			 * ### Example usage with jQuery API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var element = $(".myPageClass > div[data-role=content]"));
			 *            element.scrollview();
			 *            element.scrollview("translateTo", 0, 200, 1000); // scroll forward 200px in vertical direction with 1s animation
			 *        </script>
			 *
			 * @param {number} x
			 * @param {number} y
			 * @param {number=} [duration]
			 * @method translateTo
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype.translateTo = function (x, y, duration) {
				translate(this._scrollState, this.element, x, y, duration);
			};

			/**
			 * Ensures that specified element is visible in the
			 * clip area
			 *
			 * ### Example usage with TAU API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *                <div class="testElementClass">some data</div>
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var scrollview = tau.widget.Scrollview(document.querySelector(".myPageClass > div[data-role=content]")),
			 *                testElement = document.querySelector(".testElementClass");
			 *            scrollview.ensureElementIsVisible(testElement);
			 *        </script>
			 *
			 * ### Example usage with jQuery API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *                <div class="testElementClass">some data</div>
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var element = $(".myPageClass > div[data-role=content]")),
			 *                testElement = $(".testElementClass");
			 *            element.scrollview();
			 *            element.scrollview("ensureElementIsVisible", testElement);
			 *        </script>
			 *
			 * @param {HTMLElement} element
			 * @method ensureElementIsVisible
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype.ensureElementIsVisible = function (element) {
				var clip = this.element,
					clipHeight = DOMUtils.getElementHeight(clip),
					clipWidth = DOMUtils.getElementWidth(clip),
					clipTop = 0,
					clipBottom = clipHeight,
					elementHeight = DOMUtils.getElementHeight(element),
					elementWidth = DOMUtils.getElementWidth(element),
					elementTop = 0,
					elementBottom,
					elementFits = clipHeight >= elementHeight && clipWidth >= elementWidth,
					anchor,
					anchorPositionX,
					anchorPositionY,
					parent,
					findPositionAnchor = function (input) {
						var id = input.getAttribute("id"),
							tagName = input.tagName.toLowerCase();

						if (id && ["input", "textarea", "button"].indexOf(tagName) > -1) {
							return input.parentNode.querySelector("label[for=" + id + "]");
						}
					},
					_true = true;

				parent = element.parentNode;
				while (parent && parent !== clip) {
					elementTop += parent.offsetTop;
					//elementLeft += parent.offsetLeft;
					parent = parent.parentNode;
				}
				elementBottom = elementTop + elementHeight;
				//elementRight = elementLeft + elementWidth;

				/* C1) element fits in view is inside clip area
				 * C2) element visible only at top; eg. partly visible textarea
				 * C3) element visible only at bottom
				 * C4) element fits in view but its visible only at top
				 * C5) element fits in view but its visible only at bottom
				 */
				switch (_true) {
					case elementFits && clipTop < elementTop && clipBottom > elementBottom:
					case clipTop < elementTop && elementTop < clipBottom && clipBottom < elementBottom:
					case clipTop > elementTop && clipBottom > elementBottom:
						// (1) pass, element position is ok
						// (2, 3) pass, we cant do anything, if we move the scroll the user could lost view of
						// something he scrolled to
						break;
					case elementFits && clipTop < elementTop && clipBottom < elementBottom:
					case elementFits && clipTop > elementTop && clipBottom > elementBottom:
					case elementFits: // element fits in view but is not visible
						this.centerToElement(element);
						break;
					default: // element is not visible
						anchor = findPositionAnchor(element);
						if (!anchor) {
							anchor = element;
						}
						anchorPositionX = anchor.offsetLeft + DOMUtils.getCSSProperty(anchor, "margin-left", 0, "integer");
						anchorPositionY = anchor.offsetTop + DOMUtils.getCSSProperty(anchor, "margin-top", 0, "integer");
						parent = anchor.parentNode;
						while (parent && parent !== clip) {
							anchorPositionX += parent.offsetLeft;
							anchorPositionY += parent.offsetTop;
							parent = parent.parentNode;
						}
						this.scrollTo(anchorPositionX, anchorPositionY, this.scrollDuration);
						break;
				}
			};

			/**
			 * Centers specified element in the clip area
			 *
			 * ### Example usage with TAU API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *                <div class="testElementClass">some data</div>
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var scrollview = tau.widget.Scrollview(document.querySelector(".myPageClass > div[data-role=content]")),
			 *                testElement = document.querySelector(".testElementClass");
			 *            scrollview.centerToElement(testElement);
			 *        </script>
			 *
			 * ### Example usage with jQuery API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *                <div class="testElementClass">some data</div>
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var element = $(".myPageClass > div[data-role=content]")),
			 *                testElement = $(".testElementClass");
			 *            element.scrollview();
			 *            element.scrollview("centerToElement", testElement);
			 *        </script>
			 *
			 * @param {HTMLElement} element
			 * @method centerToElement
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype.centerToElement = function (element) {
				var clip = this.element,
					deltaX = parseInt(DOMUtils.getElementWidth(clip) / 2 - DOMUtils.getElementWidth(element) / 2, 10),
					deltaY = parseInt(DOMUtils.getElementHeight(clip) / 2 - DOMUtils.getElementHeight(element) / 2, 10),
					elementPositionX = element.offsetLeft,
					elementPositionY = element.offsetTop,
					parent = element.parentNode;

				while (parent && parent !== clip) {
					elementPositionX += parent.offsetLeft + DOMUtils.getCSSProperty(parent, "margin-left", 0, "integer");
					elementPositionY += parent.offsetTop + DOMUtils.getCSSProperty(parent, "margin-top", 0, "integer");
					parent = parent.parentNode;
				}
				this.scrollTo(elementPositionX - deltaX, elementPositionY - deltaY, this.scrollDuration);
			};

			/**
			 * Returns scroll current position
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var scrollview = tau.widget.Scrollview(document.querySelector(".myPageClass > div[data-role=content]")),
			 *                currentPosition = scrollview.getScrollPosition();
			 *        </script>
			 *
			 * ### Example usage with jQuery API
			 *
			 *        @example
			 *        <div class="myPageClass" data-role="page">
			 *            <div data-role="content" data-scroll="y">
			 *                content
			 *            </div>
			 *        </div>
			 *        <script>
			 *            var element = $(".myPageClass > div[data-role=content]")),
			 *                position;
			 *            element.scrollview();
			 *            position = element.scrollview("getScrollPosition");
			 *        </script>
			 *
			 * @return {Object}
			 * @method getScrollPosition
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype.getScrollPosition = function () {
				var element = this.element;

				return {
					"x": element.scrollLeft,
					"y": element.scrollTop
				};
			};

			/**
			 * Binds scrollview events
			 * @method _bindEvents
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Scrollview
			 */
			Scrollview.prototype._bindEvents = function (element) {
				var scrollTimer = null,
					notifyScrolled = function () {
						eventUtils.trigger(element, "scrollstop");
						window.clearTimeout(scrollTimer);
						scrollTimer = null;
					},
					self = this,
					//FIXME there should be some other way to get parent container
					ui = self._ui,
					page = ui.page,
					jumpTop = ui.jumpVerticalButton,
					jumpLeft = ui.jumpHorizontalButton,
					repositionJumpsCallback,
					jumpTopCallback,
					jumpLeftCallback,
					callbacks = self._callbacks;

				if (page) {
					if (this.options.scrollJump) {
						repositionJumpsCallback = repositionJumps.bind(null, this);
						jumpTopCallback = function () {
							self.scrollTo(element.scrollLeft, 0, 250);
						};
						jumpLeftCallback = function () {
							self.scrollTo(0, element.scrollTop, 250);
						};
						page.addEventListener(pageEvents.SHOW, repositionJumpsCallback, false);
						if (jumpTop) {
							jumpTop.firstChild.addEventListener("vclick", jumpTopCallback, false);
						}
						if (jumpLeft) {
							jumpLeft.firstChild.addEventListener("vclick", jumpLeftCallback, false);
						}

						callbacks.repositionJumps = repositionJumpsCallback;
						callbacks.jumpTop = jumpTopCallback;
						callbacks.jumpLeft = jumpLeftCallback;
					}

					element.addEventListener("scroll", function () {
						if (scrollTimer) {
							window.clearTimeout(scrollTimer);
						} else {
							eventUtils.trigger(element, "scrollstart");
						}
						scrollTimer = window.setTimeout(notifyScrolled, 100);
						eventUtils.trigger(element, "scrollupdate");
					}, false);

					document.addEventListener("vmousedown", function () {
						if (currentTransition) {
							currentTransition = null;
						}
					}, false);

					if (self.options.scrollIndicator) {
						callbacks.scrollUpdate = self._showScrollIndicator.bind(self);
						element.addEventListener("scrollupdate", callbacks.scrollUpdate, false);
						callbacks.scrollStop = self._hideScrollIndicator.bind(self);
						element.addEventListener("scrollstop", callbacks.scrollStop, false);
					}

				}
			};

			Scrollview.prototype._destroy = function () {
				var self = this,
					element = self.element,
					ui = self._ui,
					page = ui.page,
					scrollJump = this.options.scrollJump,
					jumpTop = ui.jumpVerticalButton,
					jumpLeft = ui.jumpHorizontalButton,
					callbacks = self._callbacks,
					repositionJumpsCallback = callbacks.repositionJumps,
					jumpTopCallback = callbacks.jumpTop,
					jumpLeftCallback = callbacks.jumpLeft;

				if (scrollJump) {
					if (page && repositionJumpsCallback) {
						page.removeEventListener(pageEvents.SHOW, repositionJumpsCallback, false);
					}
					if (jumpTop && jumpTopCallback) {
						jumpTop.firstChild.removeEventListener("vclick", jumpTopCallback, false);
					}
					if (jumpLeft && jumpLeftCallback) {
						jumpLeft.firstChild.removeEventListener("vclick", jumpLeftCallback, false);
					}
				}

				if (self.options.scrollIndicator) {
					element.removeEventListener("scrollupdate", callbacks.scrollUpdate, false);
				}

				if (self._timers.scrollIndicatorHide) {
					window.clearTimeout(self._timers.scrollIndicatorHide);
				}

			};

			ns.widget.core.Scrollview = Scrollview;
			}(window, window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * @class ns.widget.mobile.Tab
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				events = ns.event,
				Tab = function () {
				},
				/**
				 * Object with class dictionary
				 * @property {Object} classes
				 * @static
				 * @member ns.widget.mobile.Tab
				 * @readonly
				 */
				classes = {},
				CustomEvent = {
					TAB_CHANGE: "tabchange"
				},
				prototype = new BaseWidget();

			Tab.prototype = prototype;
			Tab.classes = classes;

			/**
			 * Set the active tab
			 * @method setActive
			 * @param {number} index of the tab
			 * @public
			 * @member ns.widget.mobile.Tab
			 */
			prototype._setActive = function (index) {
				var element = this.element;

				events.trigger(element, CustomEvent.TAB_CHANGE, {
					active: index
				});
			};
			/**
			 * Set the active tab
			 * @method setActive
			 * @param {number} index of the tab
			 * @public
			 * @member ns.widget.mobile.Tab
			 */
			prototype.setActive = function (index) {
				this._setActive(index);
			};

			/**
			 * Get the active tab
			 * @method setActive
			 * @public
			 * @member ns.widget.mobile.Tab
			 */
			prototype._getActive = function () {
				return this.options.active;
			};

			/**
			 * Get the active tab
			 * @method setActive
			 * @public
			 * @member ns.widget.mobile.Tab
			 */
			prototype.getActive = function () {
				return this._getActive();
			};

			ns.widget.core.Tab = Tab;
			engine.defineWidget(
				"Tab",
				"",
				["setActive", "getActive"],
				Tab,
				"tizen"
			);
			}(window.document, ns));

/*global window, ns, define, Event, console */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #TabIndicator Widget
 * Widget create tabs indicator.
 * @class ns.widget.core.TabIndicator
 * @since 2.3
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var Tab = ns.widget.core.Tab,
				engine = ns.engine,
				object = ns.util.object,

				TabIndicator = function () {
					this.tabSize = 0;
					this.width = 0;
				},

				TabPrototype = Tab.prototype,
				prototype = new Tab();

			TabIndicator.prototype = prototype;

			prototype._init = function (element) {
				var o = this.options;

				this.width = element.offsetWidth;
				element.classList.add(o.wrapperClass);
			};

			prototype._configure = function () {
				/**
				 * @property {Object} options Options for widget
				 * @property {number} [options.margin=2]
				 * @property {boolean} [options.triggerEvent=false]
				 * @property {string} [options.wrapperClass="ui-tab-indicator]
				 * @property {string} [options.itemClass="ui-tab-item"]
				 * @property {string} [options.activeClass="ui-tab-active"]
				 * @member ns.widget.core.TabIndicator
				 */
				object.merge(this.options, {
					margin: 4,
					triggerEvent: false,
					wrapperClass: "ui-tab-indicator",
					itemClass: "ui-tab-item",
					activeClass: "ui-tab-active",
					active: 0
				});
			};

			prototype._createIndicator = function () {
				var o = this.options,
					wrap = document.createDocumentFragment(),
					widthTable = [],
					margin = o.margin,
					i = 0,
					len = this.tabSize,
					width = this.width - margin * (len - 1),
					std = Math.floor(width / len),
					remain = width % len,
					span,
					offset = 0;

				for (i = 0; i < len; i++) {
					widthTable[i] = std;
				}

				for (i = Math.floor((len - remain) / 2); remain > 0; i++, remain--) {
					widthTable[i] += 1;
				}

				for (i = 0; i < len; i++) {
					span = document.createElement("span");
					span.classList.add(o.itemClass);
					span.style.width = widthTable[i] + "px";
					span.style.left = offset + "px";
					offset += widthTable[i] + margin;

					if (i === o.active) {
						span.classList.add(o.activeClass);
					}
					wrap.appendChild(span);
				}

				this.element.appendChild(wrap);
			};

			prototype._removeIndicator = function () {
				this.element.innerHTML = "";
			};

			prototype._refresh = function () {
				this._removeIndicator();
				this._createIndicator();
			};

			/**
			 * @method setActive
			 * @param {number} index
			 * @member ns.widget.core.TabIndicator
			 */
			prototype._setActive = function (index) {
				var o = this.options,
					nodes = this.element.children;

				o.active = index;

				[].forEach.call(nodes, function (element) {
					element.classList.remove(o.activeClass);
				});

				if (index < nodes.length) {
					nodes[index].classList.add(o.activeClass);

					TabPrototype._setActive.call(this, index);
				}
			};

			/**
			 * @method setSize
			 * @param {number} size
			 * @member ns.widget.core.TabIndicator
			 */
			prototype.setSize = function (size) {
				var needRefresh = this.tabSize !== size;

				this.tabSize = size;
				if (needRefresh) {
					this.refresh();
				}
			};

			prototype._destroy = function () {
				var o = this.options;

				this._removeIndicator();

				this.element.classList.remove(o.wrapperClass);
			};

			ns.widget.core.TabIndicator = TabIndicator;

			engine.defineWidget(
				"TabIndicator",
				".ui-tab",
				["setActive", "getActive", "setSize"],
				TabIndicator
			);
			}(window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, define, ns*/
/**
 * # Section Changer
 * Section changer component provides an application architecture, which has multiple sections on one page.
 *
 * The section changer widget provides an application architecture, which has
 * multiple sections on a page and enables scrolling through the *section* elements.
 *
 * ## Manual constructor
 *
 *      @example template tau-section-changer
 *         <div class="ui-section-changer">
 *             <div>
 *                 <section style="text-align:center"><span>${1:Section 1}</span></section>
 *                 <section style="text-align:center"><span>${2:Section 2}</span></section>
 *             </div>
 *         </div>
 *
 *
 *      @example
 *         <div id="hasSectionchangerPage" class="ui-page">
 *             <header class="ui-header">
 *                 <h2 class="ui-title">SectionChanger</h2>
 *             </header>
 *             <div id="sectionchanger" class="ui-content">
 *                 <!--Section changer has only one child-->
 *                 <div>
 *                     <section>
 *                         <h3>LEFT1 PAGE</h3>
 *                     </section>
 *                     <section class="ui-section-active">
 *                         <h3>MAIN PAGE</h3>
 *                     </section>
 *                     <section>
 *                         <h3>RIGHT1 PAGE</h3>
 *                     </section>
 *                 </div>
 *             </div>
 *         </div>
 *         <script>
 *             (function () {
 *                 var page = document.getElementById("hasSectionchangerPage"),
 *                     element = document.getElementById("sectionchanger"),
 *                     sectionChanger;
 *
 *                 page.addEventListener("pageshow", function () {
 *                     // Create the SectionChanger object
 *                     sectionChanger = new tau.SectionChanger(element, {
 *                         circular: true,
 *                         orientation: "horizontal",
 *                         useBouncingEffect: true
 *                     });
 *                 });
 *
 *                 page.addEventListener("pagehide", function () {
 *                     // Release the object
 *                     sectionChanger.destroy();
 *                 });
 *             })();
 *         </script>
 *
 * ## Handling Events
 *
 * To handle section changer events, use the following code:
 *
 *      @example
 *         <script>
 *             (function () {
 *                 var changer = document.getElementById("sectionchanger");
 *                 changer.addEventListener("sectionchange", function (event) {
 *                     console.debug(event.detail.active + " section is active.");
 *                 });
 *             })();
 *         </script>
 *
 * @class ns.widget.core.SectionChanger
 * @since 2.4
 * @component-selector [data-role="section-changer"], .ui-section-changer
 * @component-type container-component
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var Scroller = ns.widget.core.scroller.Scroller,
				gesture = ns.event.gesture,
				Orientation = gesture.Orientation,
				engine = ns.engine,
				utilsObject = ns.util.object,
				utilsEvents = ns.event,
				eventType = ns.util.object.merge({
					/**
					 * Triggered when the section is changed.
					 * @event sectionchange
					 * @member ns.widget.core.SectionChanger
					 */
					CHANGE: "sectionchange"
				}, Scroller.EventType),
				classes = {
					uiSectionChanger: "ui-section-changer"
				};

			function SectionChanger() {
				this.options = {};
			}

			function calculateCustomLayout(direction, elements, lastIndex) {
				var elementsLength = elements.length,
					length = lastIndex !== undefined ? lastIndex : elementsLength,
					result = 0,
					i = 0;

				if (length > elementsLength) {
					length = elementsLength;
				}

				for (i; i < length; i++) {
					result += direction === Orientation.HORIZONTAL ? elements[i].offsetWidth : elements[i].offsetHeight;
				}
				return result;
			}

			function calculateCenter(direction, elements, index) {
				var result = calculateCustomLayout(direction, elements, index + 1),
					element = elements[index];

				if (element) {
					result -= direction === Orientation.HORIZONTAL ? element.offsetWidth / 2 : element.offsetHeight / 2;
				}

				return result;
			}

			utilsObject.inherit(SectionChanger, Scroller, {
				_build: function (element) {
					var self = this,
						options = self.options,
						offsetHeight;

					self.tabIndicatorElement = null;
					self.tabIndicator = null;

					self.sections = null;
					self.sectionPositions = [];

					self.activeIndex = 0;
					self.beforeIndex = 0;

					self._super(element);

					element.classList.add(classes.uiSectionChanger);

					self.scroller.style.position = "absolute";
					offsetHeight = element.offsetHeight;

					if (offsetHeight === 0) {
						offsetHeight = element.parentNode.offsetHeight;
						element.style.height = offsetHeight + "px";
					}

					self._sectionChangerWidth = element.offsetWidth;
					self._sectionChangerHeight = offsetHeight;
					self._sectionChangerHalfWidth = self._sectionChangerWidth / 2;
					self._sectionChangerHalfHeight = self._sectionChangerHeight / 2;
					self.orientation = options.orientation === "horizontal" ? Orientation.HORIZONTAL : Orientation.VERTICAL;

					return element;
				},

				_configure: function () {
					this._super();
					/**
					 * Options for widget
					 * @property {Object} options
					 * @property {"horizontal"|"vertical"} [options.orientation="horizontal"] Sets the section changer orientation:
					 * @property {boolean} [options.circular=false] Presents the sections in a circular scroll fashion.
					 * @property {boolean} [options.useBouncingEffect=false] Shows a scroll end effect on the scroll edge.
					 * @property {string} [options.items="section"] Defines the section element selector.
					 * @property {string} [options.activeClass="ui-section-active"] Specifies the CSS classes which define the active section element. Add the specified class (ui-section-active) to a *section* element to indicate which section must be shown first. By default, the first section is shown first.
					 * @property {boolean} [options.fillContent=true] declare to section tag width to fill content or not.
					 * @member ns.widget.core.SectionChanger
					 */
					this.options = utilsObject.merge(this.options, {
						items: "section",
						activeClass: "ui-section-active",
						circular: false,
						animate: true,
						animateDuration: 100,
						orientation: "horizontal",
						changeThreshold: -1,
						useTab: false,
						fillContent: true
					});
				},

				_init: function (element) {
					var self = this,
						options = self.options,
						scroller = self.scroller,
						sectionLength,
						i,
						className;

					if (options.scrollbar === "tab") {
						options.scrollbar = false;
						options.useTab = true;
					}

					self.sections = typeof options.items === "string" ?
						scroller.querySelectorAll(options.items) :
						options.items;
					sectionLength = self.sections.length;

					if (options.circular && sectionLength < 3) {
						ns.error("[SectionChanger] if you use circular option, you must have at least three sections.");
					} else {
						for (i = 0; i < sectionLength; i++) {
							className = self.sections[i].className;
							if (className && className.indexOf(options.activeClass) > -1) {
								self.activeIndex = i;
							}

							self.sectionPositions[i] = i;
						}

						self._prepareLayout();
						self._initLayout();
						self._super(element);
						self._repositionSections(true);
						self.setActiveSection(self.activeIndex);

						// set correct options values.
						if (!options.animate) {
							options.animateDuration = 0;
						}
						if (options.changeThreshold < 0) {
							options.changeThreshold = self._sectionChangerHalfWidth;
						}
					}

					return element;
				},

				_prepareLayout: function () {
					var o = this.options,
						sectionLength = this.sections.length,
						width = this._sectionChangerWidth,
						height = this._sectionChangerHeight,
						orientation = this.orientation,
						scrollerStyle = this.scroller.style,
						tabHeight;

					if (o.useTab) {
						this._initTabIndicator();
						tabHeight = this.tabIndicatorElement.offsetHeight;
						height -= tabHeight;
						this._sectionChangerHalfHeight = height / 2;
						this.element.style.height = height + "px";
						this._sectionChangerHeight = height;
					}

					if (orientation === Orientation.HORIZONTAL) {
						scrollerStyle.width = (o.fillContent ? width * sectionLength : calculateCustomLayout(orientation, this.sections)) + "px";
						scrollerStyle.height = height + "px"; //set Scroller width
					} else {
						scrollerStyle.width = width + "px"; //set Scroller width
						scrollerStyle.height = (o.fillContent ? height * sectionLength : calculateCustomLayout(orientation, this.sections)) + "px";
					}

				},

				_initLayout: function () {
					var sectionStyle,
						left = 0,
						top = 0,
						i,
						sectionLength;

					//section element has absolute position
					for (i = 0, sectionLength = this.sections.length; i < sectionLength; i++) {
						//Each section set initialize left position
						sectionStyle = this.sections[i].style;
						sectionStyle.position = "absolute";
						if (this.options.fillContent) {
							sectionStyle.width = this._sectionChangerWidth + "px";
							sectionStyle.height = this._sectionChangerHeight + "px";
						}

						if (this.orientation === Orientation.HORIZONTAL) {
							top = 0;
							left = calculateCustomLayout(this.orientation, this.sections, i);
						} else {
							top = calculateCustomLayout(this.orientation, this.sections, i);
							left = 0;
						}

						sectionStyle.top = top + "px";
						sectionStyle.left = left + "px";
					}

				},

				_initBouncingEffect: function () {
					var o = this.options;

					if (!o.circular) {
						this._super();
					}
				},

				_translateScrollbar: function (x, y, duration, autoHidden) {
					var self = this,
						offset,
						scrollbar = self.scrollbar;

					if (scrollbar) {
						if (self.orientation === Orientation.HORIZONTAL) {
							offset = -x + self.minScrollX;
						} else {
							offset = -y + self.minScrollY;
						}

						scrollbar.translate(offset, duration, autoHidden);
					}
				},

				_translateScrollbarWithPageIndex: function (pageIndex, duration) {
					var offset;

					if (!this.scrollbar) {
						return;
					}

					offset = calculateCustomLayout(this.orientation, this.sections, this.activeIndex);

					this.scrollbar.translate(offset, duration);
				},

				_initTabIndicator: function () {
					var self = this,
						tabElement = document.createElement("div"),
						element = self.element,
						tabIndicator = null;

					self.tabIndicatorElement = tabElement;

					element.parentNode.insertBefore(tabElement, element);

					tabIndicator = new engine.instanceWidget(tabElement, "TabIndicator");
					self.tabIndicator = tabIndicator;
					tabIndicator.setSize(self.sections.length);
					tabIndicator.setActive(self.activeIndex);
					self.tabIndicatorHandler = function (event) {
						this.tabIndicator.setActive(event.detail.active);
					}.bind(self);
					element.addEventListener(eventType.CHANGE, self.tabIndicatorHandler, false);
				},

				_clearTabIndicator: function () {
					if (this.tabIndicator) {
						this.element.parentNode.removeChild(this.tabIndicatorElement);
						this.element.removeEventListener(eventType.CHANGE, this.tabIndicatorHandler, false);
						this.tabIndicator.destroy();
						this.tabIndicator = null;
						this.tabIndicatorElement = null;
						this.tabIndicatorHandler = null;
					}
				},

				_resetLayout: function () {
					var //scrollerStyle = this.scroller.style,
						sectionStyle,
						i,
						sectionLength;

					//scrollerStyle.width = "";
					//scrollerStyle.height = "";
					//this.scroller || this.scroller._resetLayout();

					for (i = 0, sectionLength = this.sections.length; i < sectionLength; i++) {
						sectionStyle = this.sections[i].style;

						sectionStyle.position = "";
						sectionStyle.width = "";
						sectionStyle.height = "";
						sectionStyle.top = "";
						sectionStyle.left = "";
					}

					this._super();
				},

				_bindEvents: function () {
					var self = this;

					self._super();

					ns.event.enableGesture(
						self.scroller,

						new ns.event.gesture.Swipe({
							orientation: self.orientation === Orientation.HORIZONTAL ?
								gesture.Orientation.HORIZONTAL :
								gesture.Orientation.VERTICAL
						})
					);

					utilsEvents.on(self.scroller,
						"swipe transitionEnd webkitTransitionEnd mozTransitionEnd msTransitionEnd oTransitionEnd", self);

					document.addEventListener("rotarydetent", self, true);
				},

				_unbindEvents: function () {
					var self = this;

					self._super();

					if (self.scroller) {
						ns.event.disableGesture(self.scroller);
						utilsEvents.off(self.scroller,
							"swipe transitionEnd webkitTransitionEnd mozTransitionEnd msTransitionEnd oTransitionEnd", self);
					}

					document.removeEventListener("rotarydetent", self, true);
				},

				/**
				 * This method manages events.
				 * @method handleEvent
				 * @param {Event} event
				 * @member ns.widget.core.SectionChanger
				 */
				handleEvent: function (event) {
					this._super(event);

					switch (event.type) {
						case "swipe":
						case "rotarydetent" :
							this._change(event);
							break;
						case "webkitTransitionEnd":
						case "mozTransitionEnd":
						case "msTransitionEnd":
						case "oTransitionEnd":
						case "transitionEnd":
							if (event.target === this.scroller) {
								this._endScroll();
							}
							break;
					}
				},

				_notifyChangedSection: function (index) {
					var activeClass = this.options.activeClass,
						sectionLength = this.sections.length,
						i = 0,
						section;

					for (i = 0; i < sectionLength; i++) {
						section = this.sections[i];
						section.classList.remove(activeClass);
						if (i === this.activeIndex) {
							section.classList.add(activeClass);
						}
					}

					this.trigger(eventType.CHANGE, {
						active: index
					});
				},

				/**
				 * Changes the currently active section element.
				 * @method setActiveSection
				 * @param {number} index
				 * @param {number} duration For smooth scrolling,
				 * the duration parameter must be in milliseconds.
				 * @param {number} [direct=false]
				 * @member ns.widget.core.SectionChanger
				 */
				setActiveSection: function (index, duration, direct) {
					var position = this.sectionPositions[index],
						scrollbarDuration = duration,
						oldActiveIndex = this.activeIndex,
						newX = 0,
						newY = 0;

					if (this.orientation === Orientation.HORIZONTAL) {
						newX = this._sectionChangerHalfWidth - calculateCenter(this.orientation, this.sections, position);

					} else {
						newY = this._sectionChangerHalfHeight - calculateCenter(this.orientation, this.sections, position);
					}

					if (this.beforeIndex - index > 1 || this.beforeIndex - index < -1) {
						scrollbarDuration = 0;
					}

					this.activeIndex = index;
					this.beforeIndex = this.activeIndex;

					if (newX !== this.scrollerOffsetX || newY !== this.scrollerOffsetY) {
						if (direct !== false) {
							this.trigger(eventType.START);
							this.scrolled = true;
						}

						this._translate(newX, newY, duration);
						this._translateScrollbarWithPageIndex(index, scrollbarDuration);
					} else {
						this._endScroll();
					}

					// notify changed section.
					if (this.activeIndex !== oldActiveIndex) {
						this._notifyChangedSection(this.activeIndex);
					}
				},

				/**
				 * Gets the currently active section element's index.
				 * @method getActiveSectionIndex
				 * @return {number}
				 * @member ns.widget.core.SectionChanger
				 */
				getActiveSectionIndex: function () {
					return this.activeIndex;
				},

				_start: function (e) {
					this._super(e);

					this.beforeIndex = this.activeIndex;
				},

				_move: function (event) {
					var self = this,
						changeThreshold = self.options.changeThreshold,
						delta = self.orientation === Orientation.HORIZONTAL ? event.detail.deltaX : event.detail.deltaY,
						oldActiveIndex = self.activeIndex,
						beforeIndex = self.beforeIndex;

					self._super(event);

					if (self.scrolled) {
						if (delta > changeThreshold) {
							self.activeIndex = self._calculateIndex(beforeIndex - 1);
						} else if (delta < -changeThreshold) {
							self.activeIndex = self._calculateIndex(beforeIndex + 1);
						} else {
							self.activeIndex = beforeIndex;
						}

						// notify changed section.
						if (self.activeIndex !== oldActiveIndex) {
							self._notifyChangedSection(self.activeIndex);
						}
					}
				},

				_end: function () {
					var self = this;

					if (self.scrollbar) {
						self.scrollbar.end();
					}

					if (self.enabled && !self.scrollCanceled && self.dragging) {
						// bouncing effect
						if (self.bouncingEffect) {
							self.bouncingEffect.dragEnd();
						}

						self.setActiveSection(self.activeIndex, self.options.animateDuration, false);
						self.dragging = false;
					}
				},

				_change: function (event) {
					var self = this,
						direction = event.detail.direction,
						offset = direction === gesture.Direction.UP || direction === gesture.Direction.LEFT || direction === "CW" ? 1 : -1,
						newIndex = self._calculateIndex(self.beforeIndex + offset);

					if (self.enabled && !self.scrollCanceled) {
						// bouncing effect
						if (self.bouncingEffect) {
							self.bouncingEffect.dragEnd();
						}

						if (self.activeIndex !== newIndex) {
							self.activeIndex = newIndex;
							self._notifyChangedSection(newIndex);
						}

						self.setActiveSection(newIndex, self.options.animateDuration, false);
						self.dragging = false;
					}
				},

				_endScroll: function () {
					if (!this.enabled || !this.scrolled || this.scrollCanceled) {
						return;
					}

					this._repositionSections();
					this._super();
				},

				_repositionSections: function (init) {
					// if developer set circular option is true, this method used when webkitTransitionEnd event fired
					var self = this,
						sections = self.sections,
						activeIndex = self.activeIndex,
						orientation = self.orientation,
						isHorizontal = orientation === Orientation.HORIZONTAL,
						sectionLength = sections.length,
						curPosition = self.sectionPositions[activeIndex],
						centerPosition = Math.floor(sectionLength / 2),
						circular = self.options.circular,
						centerX = 0,
						centerY = 0,
						i,
						sectionStyle,
						sIdx,
						top,
						left,
						newX,
						newY;

					if (isHorizontal) {
						newX = -(calculateCenter(orientation, sections, (circular ? centerPosition : activeIndex)));
						newY = 0;
					} else {
						newX = 0;
						newY = -(calculateCenter(orientation, sections, (circular ? centerPosition : activeIndex)));
					}

					self._translateScrollbarWithPageIndex(activeIndex);

					if (init || (curPosition === 0 || curPosition === sectionLength - 1)) {
						if (isHorizontal) {
							centerX = self._sectionChangerHalfWidth + newX;
						} else {
							centerY = self._sectionChangerHalfHeight + newY;
						}
						self._translate(centerX, centerY);

						if (circular) {
							for (i = 0; i < sectionLength; i++) {
								sIdx = (sectionLength + activeIndex - centerPosition + i) % sectionLength;
								sectionStyle = sections[sIdx].style;

								self.sectionPositions[sIdx] = i;

								if (isHorizontal) {
									top = 0;
									left = calculateCustomLayout(orientation, sections, i);
								} else {
									top = calculateCustomLayout(orientation, sections, i);
									left = 0;
								}

								sectionStyle.top = top + "px";
								sectionStyle.left = left + "px";
							}
						}
					}
				},

				_calculateIndex: function (newIndex) {
					var sectionLength = this.sections.length;

					if (this.options.circular) {
						newIndex = (sectionLength + newIndex) % sectionLength;
					} else {
						newIndex = newIndex < 0 ? 0 : (newIndex > sectionLength - 1 ? sectionLength - 1 : newIndex);
					}

					return newIndex;
				},

				_clear: function () {
					this._clearTabIndicator();
					this._super();
					this.sectionPositions.length = 0;
				},

				_destroy: function () {
					var element = this.element;

					// clear dimensions set in _build
					element.style.height = null;
					element.style.width = null;
					this._super();
				}
			});

			ns.widget.core.SectionChanger = SectionChanger;

			engine.defineWidget(
				"SectionChanger",
				"[data-role='section-changer'], .ui-section-changer",
				["getActiveSectionIndex", "setActiveSection"],
				SectionChanger
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Checkbox
 * Checkbox component changes the default browser checkboxes to a form more adapted to the mobile
 * environment.
 *
 * @since 2.4
 * @class ns.widget.core.Checkbox
 * @extends ns.widget.BaseWidget
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Checkbox
 *
 * ## HTML examples
 *
 * ### Basic use
 *      @example template
 *      <input type="checkbox"/>
 *
 * ### Checkbox with label
 *      @example tau-checkbox
 *      <input type="checkbox" name="${5:mycheck}" id="${3:check-test}" checked="${2:checked}"/>\n<label for="${4:check-test}">${1:Checkbox}</label>
 *
 * @class ns.widget.core.Checkbox
 * @component-selector input[type="checkbox"]:not(.ui-slider-switch-input):not([data-role="toggleswitch"]):not(.ui-toggleswitch):not(.ui-switch-input), input.ui-checkbox
 * @component-type standalone-component
 * @component-attachable true
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				Checkbox = function () {
					this.element = null;
				},
				classes = {
					checkbox: "ui-checkbox"
				},
				prototype = new BaseWidget();

			Checkbox.prototype = prototype;

			/**
			 * Build Checkbox widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Checkbox
			 * @instance
			 */
			prototype._build = function (element) {
				if (element.getAttribute("type") === "checkbox") {
					element.classList.add(classes.checkbox);
				}
				return element;
			};

			/**
			 * Returns the value of checkbox
			 * @method _getValue
			 * @member ns.widget.core.Checkbox
			 * @return {?string}
			 * @protected
			 */
			prototype._getValue = function () {
				return this.element.value;
			};

			/**
			 * Set value to the checkbox
			 * @method _setValue
			 * @param {string} value
			 * @member ns.widget.core.Checkbox
			 * @return {ns.widget.core.Checkbox}
			 * @protected
			 */
			prototype._setValue = function (value) {
				this.element.value = value;
			};

			// definition
			ns.widget.core.Checkbox = Checkbox;
			engine.defineWidget(
				"Checkbox",
				"input[type='checkbox']:not(.ui-slider-switch-input):not([data-role='toggleswitch'])" +
				":not(.ui-toggleswitch):not(.ui-toggle-switch), input.ui-checkbox",
				[],
				Checkbox,
				"core",
				false,
				false,
				HTMLInputElement
			);

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Radio
 *
 *     @example template tau-radio
 *         <input type="radio"/>
 *
 * @class ns.widget.core.Radio
 * @component-selector input[type=radio]
 * @component-type standalone-component
 * @since 2.4
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				Radio = function () {
					this.element = null;
				},
				classes = {
					radio: "ui-radio"
				},
				prototype = new BaseWidget();

			Radio.prototype = prototype;

			/**
			 * Build Radio widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.Radio
			 * @instance
			 */
			prototype._build = function (element) {
				if (element.getAttribute("type") === "radio") {
					element.classList.add(classes.radio);
				}

				return element;
			};

			/**
			 * Returns the value of radio
			 * @method _getValue
			 * @member ns.widget.Radio
			 * @return {?string}
			 * @protected
			 */
			prototype._getValue = function () {
				return this.element.value;
			};

			/**
			 * Set value to the radio
			 * @method _setValue
			 * @param {string} value
			 * @member ns.widget.Radio
			 * @return {ns.widget.Radio}
			 * @protected
			 */
			prototype._setValue = function (value) {
				this.element.value = value;
			};

			// definition
			ns.widget.core.Radio = Radio;
			engine.defineWidget(
				"Radio",
				"input[type='radio'], input.ui-radio",
				[],
				Radio,
				"core",
				false,
				false,
				HTMLInputElement
			);
			}(window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Panel
 * Panel is component that can have header, content, footer, listview and so on like the page component.
 *
 * Panel has been made that developer can implement to multi panel in one page.
 * But, Panel don't need to implement in one html file. Panel can be existed other html files.
 * PanelChanger controlled Panel lifecycle so If you implement to Panel in PanelChanger, you can experience UX that multi page existed in one page.
 *
 * @class ns.widget.core.Panel
 * @extends ns.widget.BaseWidget
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				events = ns.event,
				classes = {
					PANEL: "ui-panel",
					ACTIVE_PANEL: "ui-panel-active"
				},
				EVENT_TYPE = {
					BEFORE_CREATE: "panelbeforecreate",
					CREATE: "panelcreate",
					BEFORE_SHOW: "panelbeforeshow",
					SHOW: "panelshow",
					BEFORE_HIDE: "panelbeforehide",
					HIDE: "panelhide",
					CHANGE: "panelchange"
				},
				Panel = function () {
				},
				prototype = new BaseWidget();

			Panel.eventType = EVENT_TYPE;
			Panel.classes = classes;
			Panel.prototype = prototype;

			/**
			 * Build Panel component
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement} Returns built element
			 * @member ns.widget.core.Panel
			 * @protected
			 */
			prototype._build = function (element) {
				var routePanel = ns.router.Router.getInstance().getRoute("panel");

				element.classList.add(classes.PANEL);
				routePanel.setActive(element);

				return element;
			};

			/**
			 * Destroy Panel component
			 * @method _destroy
			 * @param {HTMLElement} element
			 * @return {HTMLElement} Returns built element
			 * @member ns.widget.core.Panel
			 * @protected
			 */
			prototype._destroy = function (element) {
				events.trigger(element, EVENT_TYPE.HIDE);
			};
			// definition
			ns.widget.core.Panel = Panel;

			engine.defineWidget(
				"Panel",
				"[data-role='panel'], .ui-panel",
				[],
				Panel,
				"core"
			);
			}(window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Panel Changer
 * Panel changer and panel component provide multi page layout in a page component.
 *
 * PanelChanger managed panel life-cycle and routing. So, If you want to use panel likes page,
 * you should wrap the pages as PanelChanger component.
 *
 * @since 2.4
 * @class ns.widget.core.PanelChanger
 * @extends ns.widget.BaseWidget
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				selectors = ns.util.selectors,
				object = ns.util.object,
				engine = ns.engine,
				page = ns.widget.core.Page,
				panel = ns.widget.core.Panel,
				events = ns.event,
				classes = {
					PANEL_CHANGER: "ui-panel-changer",
					PAGE: page.classes.uiPage,
					PANEL: panel.classes.PANEL,
					ACTIVE_PANEL: panel.classes.ACTIVE_PANEL,
					HEADER: "ui-header",
					FOOTER: "ui-footer",
					PRE_IN: "pre-in",
					IN: "-in",
					OUT: "-out"
				},
				PanelChanger = function () {
					var self = this;

					self._ui = {};
					self.options = {};
					self.eventType = {};
					self._animating = false;
					self._animationClasses = {};
					self.history = [];
				},
				DEFAULT = {
					ANIMATE: "slide",
					STORAGE_NAME: "panelhistory"
				},
				prototype = new BaseWidget();

			PanelChanger.default = DEFAULT;
			PanelChanger.classes = classes;
			PanelChanger.prototype = prototype;

			/**
			 * Configure PanelChanger component
			 * @method _configure
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._configure = function () {
				var self = this;

				object.merge(self.options, {
					animationType: DEFAULT.ANIMATE,
					manageHistory: true
				});
				object.merge(self.eventType, panel.eventType);
			};

			/**
			 * Build PanelChanger component
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._build = function (element) {
				element.classList.add(classes.PANEL_CHANGER);

				return element;
			};

			/**
			 * Init PanelChanger component
			 * @method _init
			 * @param {HTMLElement} element
			 * @return {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui;

				ui.page = selectors.getClosestByClass(element, classes.PAGE);
				ui.header = ui.page.querySelector("." + classes.HEADER);
				ui.footer = ui.page.querySelector("." + classes.FOOTER);
				ui.activePanel = ui.page.querySelector("." + classes.ACTIVE_PANEL);
				if (!ui.activePanel) {
					ui.activePanel = ui.page.querySelector("[data-role='panel'], .ui-panel");
					ui.activePanel.classList.add(classes.ACTIVE_PANEL);
				}
				ui.activePanel.style.display = "block";
				self._direction = "forward";
				localStorage[DEFAULT.STORAGE_NAME] = [];
				self.history.push(ui.activePanel.id);
				localStorage[DEFAULT.STORAGE_NAME] = JSON.stringify(self.history);
				self._animationType = self.options.animationType;
				this._initLayout();
				return element;
			};

			/**
			 * InitLayout PanelChanger component
			 * @method _initLayout
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._initLayout = function () {
				var self = this,
					element = self.element,
					ui = self._ui,
					pageOffsetHeight = ui.page ? ui.page.offsetHeight : 0,
					headerOffsetHeight = ui.header ? ui.header.offsetHeight : 0,
					footerOffsetHeight = ui.footer ? ui.footer.offsetHeight : 0;

				element.style.height = pageOffsetHeight - headerOffsetHeight - footerOffsetHeight + "px";
			};
			/**
			 * Bind events on PanelChanger component
			 * @method _bindEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._bindEvents = function (element) {
				bindEvents.call(this, element);
			};

			/**
			 * Change panel
			 * @method _changePanel
			 * @param {string} address
			 * @param {string} animationType
			 * @param {string} direction
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._changePanel = function (address, animationType, direction) {
				var self = this,
					request = new XMLHttpRequest(),
					url = address ? address.split(/[#|?]+/)[0] : null;

				if (animationType) {
					self._animationType = animationType;
				}
				self._direction = direction;
				request.responseType = "document";
				request.open("GET", url);
				request.addEventListener("error", self._loadError);
				request.addEventListener("load", function (event) {
					var request = event.target;

					if (request.readyState === 4) {
						if (request.status === 200 || (request.status === 0 && request.responseXML)) {
							self._loadSuccess(address, request.responseXML, direction);
						} else {
							self._loadError();
						}
					}
				});
				request.send();
			};

			/**
			 * AJAX loadsuccess event handler
			 * @method _loadSuccess
			 * @param {string} href address string
			 * @param {XML} xml element
			 * @param {string} direction
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._loadSuccess = function (href, xml, direction) {
				var self = this,
					element = self.element,
					id = href.substring(href.lastIndexOf("#")),
					eventType = self.eventType,
					ui = self._ui,
					panel = id.length > 1 ? element.querySelector(id) : null,
					panelStyle,
					i,
					len,
					transformCacheValue;

				if (!panel && id.length > 1) {
					panel = xml.querySelector(id) || xml.querySelector("[data-role='panel'], .ui-panel");
				}


				if (!panel) {
					ns.warn("Panel is not existed");
					return;
				}

				panelStyle = panel.style;
				panelStyle.display = "block";
				transformCacheValue = panelStyle.transform;
				panelStyle.transform = "translate(-9999px, -9999px)";

				element.appendChild(panel);
				ui.toPanel = panel;
				events.trigger(panel, eventType.BEFORE_CREATE);
				engine.createWidgets(element);
				events.trigger(panel, eventType.CREATE);
				events.trigger(panel, eventType.BEFORE_SHOW);
				events.trigger(ui.activePanel, eventType.BEFORE_HIDE);
				panel.classList.add(classes.PRE_IN);
				panelStyle.display = "none";
				panelStyle.transform = transformCacheValue;

				self.history = JSON.parse(localStorage[DEFAULT.STORAGE_NAME] || "[]");
				if (direction === "forward") {
					self.history.push(panel.getAttribute("id"));
					localStorage[DEFAULT.STORAGE_NAME] = JSON.stringify(self.history);
				} else {
					len = self.history.length - 1;
					for (i = self.history.indexOf(panel.id); i < len; i++) {
						self.history.pop();
					}
					localStorage[DEFAULT.STORAGE_NAME] = JSON.stringify(self.history);
				}

				self._show();
			};

			/**
			 * Show next panel component
			 * @method _show
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._show = function () {
				var self = this,
					toPanel = self._ui.toPanel,
					fromPanel = self._ui.activePanel,
					type = self._animationType,
					animationClasses = self._animationClasses;

				self._animating = true;
				fromPanel.classList.remove(classes.ACTIVE_PANEL);
				toPanel.style.display = "block";
				animationClasses.IN = type + classes.IN;
				animationClasses.OUT = type + classes.OUT;

				fromPanel.classList.add(animationClasses.OUT);
				toPanel.classList.add(animationClasses.IN);

				if (type === "none") {
					self._onAnimationEnd();
				}
			};

			/**
			 * Loaderror event handler
			 * @method _loadError
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._loadError = function () {
				ns.warn("We can't load AJAX")
			};

			/**
			 * Bind events on this component
			 * @method bindEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @private
			 */
			function bindEvents(element) {
				var self = this;

				events.on(element, "vclick", self, false);
				events.prefixedFastOn(element, "animationEnd", self, false);
			}

			/**
			 * Bind events on PanelChanger component
			 * @method _bindEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._bindEvents = function (element) {
				bindEvents.call(this, element);
			};

			/**
			 * Click event handler
			 * @method _onClick
			 * @param {Event} event
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._onClick = function (event) {
				var self = this,
					link = event.target.tagName.toLowerCase() === "a" ? event.target : selectors.getClosestByTag(event.target, "A"),
					href;

				if (link && !self._animating && !link.getAttribute("data-rel")) {
					href = link.getAttribute("href");
					self._changePanel(href, self.options.animationType, "forward");
					event.preventDefault();
				}
			};

			/**
			 * animationEnd event handler
			 * @method _onAnimationEnd
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._onAnimationEnd = function () {
				var self = this,
					element = self.element,
					toPanel = self._ui.toPanel,
					activePanel = self._ui.activePanel,
					animationClasses = self._animationClasses;

				if (!self._animating) {
					return;
				}
				activePanel.style.display = "none";
				activePanel.classList.remove(animationClasses.OUT);
				toPanel.classList.add(classes.ACTIVE_PANEL);
				toPanel.classList.remove(classes.PRE_IN);
				toPanel.classList.remove(animationClasses.IN);

				events.trigger(activePanel, self.eventType.HIDE);
				events.trigger(toPanel, self.eventType.SHOW);
				events.trigger(element, self.eventType.CHANGE, {
					fromPanel: activePanel,
					toPanel: toPanel,
					direction: self._direction
				});
				self._ui.activePanel = toPanel;
				self._animating = false;
			};

			/**
			 * Bind pageBeforeShow event
			 * @method _onPagebeforeshow
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._onPagebeforeshow = function () {
				var routePanel = ns.router.Router.getInstance().getRoute("panel");

				routePanel.setActive(this._ui._activePanel);
			};

			/**
			 * Unbind events on this component
			 * @method unBindEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.PanelChanger
			 * @private
			 */
			function unBindEvents(element) {
				var self = this;

				events.off(element, "vclick", self, false);
				events.prefixedFastOff(element, "animationEnd", self, false);
			}

			/**
			 * handleEvent
			 * @method bindEvents
			 * @param {Event} event
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "vclick":
						self._onClick(event);
						break;
					case "webkitAnimationEnd":
					case "mozAnimationEnd":
					case "msAnimationEnd":
					case "oAnimationEnd":
					case "animationend":
						self._onAnimationEnd(event);
						break;
					case "pagebeforeshow":
						self._onPagebeforeshow(event);
						break;
				}
			};

			/**
			 * Change panel method
			 * @method changePanel
			 * @param {string} address
			 * @param {string} animationType
			 * @param {string} direction
			 * @member ns.widget.core.PanelChanger
			 * @public
			 */
			prototype.changePanel = function (address, animationType, direction) {
				this._changePanel(address, animationType, direction);
			};

			/**
			 * Destroy panel component
			 * @method _destroy
			 * @member ns.widget.core.PanelChanger
			 * @protected
			 */
			prototype._destroy = function () {
				var self = this;

				self._ui = null;
				self.options = null;
				self._eventType = null;
				unBindEvents(self.element);
			};
			// definition
			ns.widget.core.PanelChanger = PanelChanger;

			engine.defineWidget(
				"PanelChanger",
				"[data-role='panel-changer'], .ui-panel-changer",
				["changePanel"],
				PanelChanger,
				"core"
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	 http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Page Indicator
 * PageIndicator component presents as a dot-typed indicator.
 *
 * @since 2.4
 * @class ns.widget.core.PageIndicator
 * @component-selector [data-role="page-indicator"], .ui-page-indicator
 * @component-type standalone-component
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,

				PageIndicator = function () {
					var self = this;

					self._activeIndex = null;
					self.options = {};
				},
				classes = {
					indicator: "ui-page-indicator",
					indicatorActive: "ui-page-indicator-active",
					indicatorItem: "ui-page-indicator-item",
					indicatorDashed: "ui-page-indicator-dashed",
					linearIndicator: "ui-page-indicator-linear",
					circularIndicator: "ui-page-indicator-circular"
				},
				maxDots = {
					IN_CIRCLE: 60,
					IN_LINEAR: 5
				},
				layoutType = {
					LINEAR: "linear",
					CIRCULAR: "circular"
				},
				DISTANCE_FROM_EDGE = 8,

				prototype = new BaseWidget();

			PageIndicator.classes = classes;

			prototype._configure = function () {
				/**
				 * Options for widget.
				 * @property {Object} options
				 * @property {number} [options.maxPage=null] Maximum number of dots(pages) in indicator.
				 * @property {number} [options.numberOfPages=null] Number of pages to be linked to PageIndicator.
				 * @property {string} [options.layout="linear"] Layout type of page indicator.
				 * @property {number} [options.intervalAngle=6] angle between each dot in page indicator.
				 * @property {string} [options.style="dashed"] style of the page indicator "dotted" "dashed"
				 * @member ns.widget.core.PageIndicator
				 */
				this.options = {
					maxPage: null,
					numberOfPages: null,
					layout: "linear",
					intervalAngle: 6,
					style: "dashed"
				};
			};
			/**
			 * Build PageIndicator
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._build = function (element) {
				var self = this,
					options = self.options;

				self._createIndicator(element);
				if (options.layout === layoutType.CIRCULAR) {
					self._circularPositioning(element);
				}
				if (options.style === "dashed") {
					element.classList.add(classes.indicatorDashed);
				}
				return element;
			};

			/**
			 * Create HTML elements for PageIndicator
			 * @method _createIndicator
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._createIndicator = function (element) {
				var self = this,
					i,
					len,
					maxPage,
					span,
					numberOfPages = self.options.numberOfPages;

				if (numberOfPages === null) {
					ns.error("build error: numberOfPages is null");
					return;
				}

				self.options.layout = self.options.layout.toLowerCase();

				if (self.options.layout === layoutType.CIRCULAR) {
					element.classList.remove(classes.linearIndicator);
					element.classList.add(classes.circularIndicator);
				} else {
					element.classList.remove(classes.circularIndicator);
					element.classList.add(classes.linearIndicator);
				}

				maxPage = self._getMaxPage();

				len = numberOfPages < maxPage ? numberOfPages : maxPage;

				for (i = 0; i < len; i++) {
					span = document.createElement("span");
					span.classList.add(classes.indicatorItem);

					element.appendChild(span);
				}
			};

			/**
			 * Make circular positioned indicator
			 * @method _circularPositioning
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._circularPositioning = function (element) {
				var self = this,
					items = element.children,
					numberOfDots = items.length,
					intervalAngle = self.options.intervalAngle - "0",
					translatePixel,
					style,
					i;

				translatePixel = element.offsetWidth / 2 - DISTANCE_FROM_EDGE;

				for (i = 0; i < numberOfDots; i++) {
					style = "rotate(" + (i * intervalAngle - 90 - (numberOfDots - 1) * intervalAngle * 0.5) + "deg) translate(" +
						translatePixel + "px) ";

					items[i].style.transform = style;
				}

			};

			/**
			 * Return maximum number of dots(pages) in indicator
			 * @method _getMaxPage
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._getMaxPage = function () {
				var self = this,
					options = self.options,
					maxPage;

				if (options.layout === layoutType.CIRCULAR) {
					maxPage = options.maxPage || maxDots.IN_CIRCLE;
				} else {
					maxPage = options.maxPage || maxDots.IN_LINEAR;
				}
				return maxPage;
			};

			/**
			 * Remove contents of HTML elements for PageIndicator
			 * @method _removeIndicator
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._removeIndicator = function (element) {
				element.textContent = "";
			};

			/**
			 * This method sets a dot to active state.
			 * @method setActive
			 * @param {number} position index to be active state.
			 * @member ns.widget.core.PageIndicator
			 */
			prototype.setActive = function (position) {
				var self = this,
					dotIndex = position,
					elPageIndicatorItems = self.element.children,
					maxPage,
					numberOfPages = parseInt(self.options.numberOfPages, 10),
					middle,
					numberOfCentralDotPages = 0,
					indicatorActive = classes.indicatorActive,
					previousActive;

				if (position === null || position === undefined) {
					return;
				}

				self._activeIndex = position;
				maxPage = self._getMaxPage();
				middle = window.parseInt(maxPage / 2, 10);

				if (numberOfPages > maxPage) {
					numberOfCentralDotPages = numberOfPages - maxPage;
				} else if (isNaN(numberOfPages)) {
					ns.error("setActive error: numberOfPages is not a number");
					return;
				} else if (numberOfPages === 0) {
					return;
				}

				previousActive = self.element.querySelector("." + indicatorActive);
				if (previousActive) {
					previousActive.classList.remove(indicatorActive);
				}

				if ((middle < position) && (position <= (middle + numberOfCentralDotPages))) {
					dotIndex = middle;
				} else if (position > (middle + numberOfCentralDotPages)) {
					dotIndex = position - numberOfCentralDotPages;
				}

				elPageIndicatorItems[dotIndex].classList.add(indicatorActive);
			};

			/**
			 * Refresh widget structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._refresh = function () {
				var self = this,
					element = self.element;

				self._removeIndicator(element);
				self._createIndicator(element);
				if (self.options.layout === layoutType.CIRCULAR) {
					self._circularPositioning(element);
				}
			};

			/**
			 * Destroy widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.PageIndicator
			 */
			prototype._destroy = function () {
				this._removeIndicator(this.element);
			};

			PageIndicator.prototype = prototype;

			ns.widget.core.PageIndicator = PageIndicator;

			engine.defineWidget(
				"PageIndicator",
				"[data-role='page-indicator'], .ui-page-indicator",
				["setActive"],
				PageIndicator,
				"core"
			);
			}(window.document, ns));

/*global window, define, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Slider
 * Slider component changes the range-type browser input to sliders.
 *
 * ##Default selectors
 * In default all **INPUT** tags with type equals _range_  and _data-role=slider_ are changed to TAU sliders.
 *
 * ###HTML Examples
 *
 *         @example
 *              <input type="range" name="slider-1" id="slider" value="60" min="0" max="100">
 *
 * ###Manual constructor
 * For manual creation of slider widget you can use constructor of widget
 *
 *         @example
 *              <input id="slider">
 *              <script>
 *                  var sliderElement = document.getElementById("slider"),
 *                      slider;
 *
 *                  slider = tau.widget.Slider(sliderElement);
 *
 *                  // You can make slider component for TizenSlider component name,
 *                  // for example, tau.widget.TizenSlider(sliderElement).
 *                  // But, TizenSlider component name will be deprecated since tizen 2.4
 *                  // because we don't recommend this method.
 *              </script>
 *
 * @since 2.0
 * @class ns.widget.core.Slider
 * @extends ns.widget.BaseWidget
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				/**
			 * @property {Object} Widget Alias for {@link ns.widget.BaseWidget}
			 * @member ns.widget.core.Drawer
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				selectors = ns.util.selectors,
				utilDOM = ns.util.DOM,
				events = ns.event,
				Gesture = ns.event.gesture,
				COLORS = {
					BACKGROUND: "rgba(145, 145, 145, 0.7)",
					ACTIVE: "rgba(61, 185, 204, 1)",
					WARNING_BG: "rgba(201, 133, 133, 1)",
					WARNING: "rgba(255, 25, 25, 1)"
				},
				DEFAULT = {
					HORIZONTAL: "horizontal"
				},
				Slider = function () {
					var self = this;
					/**
					 * Widget options
					 * @property {boolean} [options.type="normal"] Slider type. 'normal', 'center' or 'circle'
					 * @property {string} [options.orientation="horizontal"] Slider orientation. horizontal or vertical
					 * @property {boolean} [options.expand=false] Slider expand mode. true or false
					 **/

					self.options = {
						type: "normal",
						orientation: DEFAULT.HORIZONTAL,
						expand: false,
						warning: false,
						warningLevel: 0,
						disabled: false
					};

					self._ui = {};
				},
				classes = {
					SLIDER: "ui-slider",
					SLIDER_HORIZONTAL: "ui-slider-horizontal",
					SLIDER_VERTICAL: "ui-slider-vertical",
					SLIDER_VALUE: "ui-slider-value",
					SLIDER_HANDLER: "ui-slider-handler",
					SLIDER_HANDLER_EXPAND: "ui-slider-handler-expand",
					SLIDER_CENTER: "ui-slider-center",
					SLIDER_HANDLER_ACTIVE: "ui-slider-handler-active",
					SLIDER_WARNING: "ui-slider-warning",
					SLIDER_DISABLED: "ui-disabled",
					SLIDER_HANDLER_VALUE: "ui-slider-handler-value",
					SLIDER_HANDLER_SMALL: "ui-slider-handler-small"
				},
				prototype = new BaseWidget();

			Slider.prototype = prototype;
			Slider.classes = classes;

			/**
			 * Bind events
			 * @method bindEvents
			 * @param {Object} self
			 * @member ns.widget.core.Slider
			 * @private
			 * @static
			 */
			function bindEvents(self) {
				var element = self._ui.barElement;

				events.enableGesture(
					element,

					new Gesture.Drag({
						orientation: self.options.orientation,
						threshold: 0
					})
				);
				events.on(element, "dragstart drag dragend dragcancel", self, false);
			}

			/**
			 * unBind events
			 * @method unbindEvents
			 * @param {Object} self
			 * @member ns.widget.core.Slider
			 * @private
			 * @static
			 */
			function unbindEvents(self) {
				var element = self._ui.barElement;

				events.disableGesture(element);
				events.off(element, "dragstart drag dragend dragcancel", self, false);
			}

			/**
			 * Build structure of Slider component
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement} Returns built element
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					barElement = document.createElement("div"),
					valueElement = document.createElement("div"),
					handlerElement = document.createElement("div");

				element.style.display = "none";
				barElement.classList.add(classes.SLIDER);

				valueElement.classList.add(classes.SLIDER_VALUE);
				barElement.appendChild(valueElement);
				handlerElement.classList.add(classes.SLIDER_HANDLER);
				barElement.appendChild(handlerElement);

				element.parentNode.appendChild(barElement);
				ui.valueElement = valueElement;
				ui.handlerElement = handlerElement;
				ui.barElement = barElement;

				return element;
			};

			/**
			 * init Slider component
			 * @method _init
			 * @param {HTMLElement} element
			 * @return {HTMLElement} Returns built element
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this,
					attrMin = parseFloat(element.getAttribute("min")),
					attrMax = parseFloat(element.getAttribute("max")),
					attrValue = parseFloat(element.getAttribute("value"));

				self._min = attrMin ? attrMin : 0;
				self._max = attrMax ? attrMax : 100;
				self._minValue = self._min;
				self._maxValue = self._max;
				self._value = attrValue ? attrValue : parseFloat(self.element.value);
				self._interval = self._max - self._min;
				self._previousValue = self._value;
				self._warningLevel = parseInt(self.options.warningLevel, 10);
				self._setDisabled(element);

				self._initLayout();
				return element;
			};

			/**
			 * init layout of Slider component
			 * @method _initLayout
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._initLayout = function () {
				var self = this,
					options = self.options,
					ui = self._ui,
					barElement = ui.barElement,
					handlerElement = ui.handlerElement;

				if (options.orientation === DEFAULT.HORIZONTAL) {
					barElement.classList.remove(classes.SLIDER_VERTICAL);
					barElement.classList.add(classes.SLIDER_HORIZONTAL);
				} else {
					barElement.classList.remove(classes.SLIDER_HORIZONTAL);
					barElement.classList.add(classes.SLIDER_VERTICAL);
				}

				options.type === "center" ? barElement.classList.add(classes.SLIDER_CENTER) : barElement.classList.remove(classes.SLIDER_CENTER);

				options.expand ? handlerElement.classList.add(classes.SLIDER_HANDLER_EXPAND) : handlerElement.classList.remove(classes.SLIDER_HANDLER_EXPAND);


				self._barElementWidth = ui.barElement.offsetWidth;
				if (self.options.orientation !== DEFAULT.HORIZONTAL) {
					self._barElementHeight = ui.barElement.offsetHeight;
				}
				self._setValue(self._value);
				self._setSliderColors(self._value);
			};

			/**
			 * Set value of Slider center mode
			 * @method _setCenterValue
			 * @param {number} value
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._setCenterValue = function (value) {
				var self = this,
					ui = self._ui,
					validValue,
					valueElementValidStyle,
					barElementLength,
					center,
					validStyle,
					inValidStyle;

				if (self.options.orientation === DEFAULT.HORIZONTAL) {
					barElementLength = self._barElementWidth;
					center = barElementLength / 2;
					validValue = barElementLength * (value - self._min) / self._interval;
					validStyle = validValue < center ? "right" : "left";
					inValidStyle = validValue < center ? "left" : "right";
					valueElementValidStyle = "width";
					ui.handlerElement.style["left"] = validValue + "px";
				} else {
					barElementLength = self._barElementHeight;
					center = barElementLength / 2;
					validValue = barElementLength * (value - self._min) / self._interval;
					validStyle = validValue < center ? "top" : "bottom";
					inValidStyle = validValue < center ? "bottom" : "top";
					valueElementValidStyle = "height";
					ui.handlerElement.style["top"] = (barElementLength - validValue) + "px";
				}

				ui.valueElement.style[validStyle] = "50%";
				ui.valueElement.style[inValidStyle] = "initial";

				ui.valueElement.style[valueElementValidStyle] = Math.abs(center - validValue) + "px";
			};

			/**
			 * Set value of Slider normal mode
			 * @method _setNormalValue
			 * @param {number} value
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._setNormalValue = function (value) {
				var self = this,
					ui = self._ui,
					options = self.options,
					barElementLength,
					validValue;

				if (options.orientation === DEFAULT.HORIZONTAL) {
					barElementLength = self._barElementWidth;
					validValue = barElementLength * (value - self._min) / self._interval;
					ui.valueElement.style["width"] = validValue + "px";
					ui.handlerElement.style["left"] = validValue + "px";
				} else {
					barElementLength = self._barElementHeight;
					validValue = barElementLength * (value - self._min) / self._interval;
					ui.valueElement.style["height"] = validValue + "px";
					ui.handlerElement.style["top"] = (barElementLength - validValue) + "px";
				}
			};

			/**
			 * Set value of Slider
			 * @method _setValue
			 * @param {number} value
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._setValue = function (value) {
				var self = this,
					ui = self._ui,
					options = self.options,
					element = self.element,
					floatValue,
					expendedClasses;

				self._previousValue = self.element.value;

				if (value < self._min) {
					value = self._min;
				} else if (value > self._max) {
					value = self._max;
				}

				floatValue = parseFloat(value);

				if (options.type === "center") {
					self._setCenterValue(value);
				} else if (options.type === "normal") {
					self._setNormalValue(value);
				}

				self._setHandlerStyle(value);
				self._updateSliderColors(value);

				if (self.options.expand) {
					expendedClasses = classes.SLIDER_HANDLER_VALUE;
					if (floatValue > 99 || floatValue < -10) {
						expendedClasses += " " + classes.SLIDER_HANDLER_SMALL;
					}
					ui.handlerElement.innerHTML = "<span class=" + expendedClasses + ">" + floatValue + "</span>";
				}

				if (element.value - 0 !== floatValue) {
					element.setAttribute("value", floatValue);
					element.value = floatValue;
					self._value = floatValue;
					events.trigger(element, "input");
				}
			};

			/**
			 * Set background as a gradient
			 * @param {HTMLElement} element
			 * @param {string} orientation
			 * @param {string} reverseOrientation
			 * @param {string} color1
			 * @param {string} level1
			 * @param {string} color2
			 * @param {string} level2
			 * @param {string} currentValue This param is added only because gradients do not work in proper way on Tizen
			 * @private
			 */
			function setBackground(element, orientation, reverseOrientation, color1, level1, color2, level2, currentValue) {
				// gradients on Tizen do not work in proper way, so this condition is workaround
				// if gradients work properly, this should be removed!
				if (parseFloat(currentValue) > parseFloat(level1)) {
					element.style.background = "-webkit-linear-gradient(" + reverseOrientation + "," +
						color1 + " " + level1 + ", " + color2 + " " + level2 + ")";
				} else {
					element.style.background = color1;
				}
			}

			/**
			 * Set warning level for slider
			 * @param {number} value
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._setSliderColors = function (value) {
				var self = this,
					ui = self._ui,
					barElement = ui.barElement,
					sliderValueElement = ui.valueElement,
					orientation,
					reverseOrientation,
					barLength,
					warningLevel,
					level;

				if (self.options.type === "normal" && self.options.warning && value >= self._min && value <= self._max) {
					if (self.options.orientation === DEFAULT.HORIZONTAL) {
						orientation = "right";
						reverseOrientation = "left";
						barLength = self._barElementWidth;
					} else {
						orientation = "top";
						reverseOrientation = "bottom";
						barLength = self._barElementHeight;
					}
					warningLevel = barLength * self._warningLevel / (self._max - self._min) + "px";
					level = barLength * value / (self._max - self._min) + "px";

					// set background for value bar and slider bar
					setBackground(sliderValueElement, orientation, reverseOrientation, COLORS.ACTIVE, warningLevel, COLORS.WARNING, warningLevel, level);
					setBackground(barElement, orientation, reverseOrientation, COLORS.BACKGROUND, warningLevel, COLORS.WARNING_BG, warningLevel,
						parseInt(warningLevel, 10) + 2);
				} else {
					// gradients on Tizen do not work in proper way, so this is workaround
					// if gradients work properly, this should be removed!
					sliderValueElement.style.background = COLORS.ACTIVE;
					barElement.style.background = COLORS.BACKGROUND;
				}
			};

			// gradients on Tizen do not work in proper way, so this is workaround
			// if gradients work properly, this should be removed!
			prototype._updateSliderColors = function (value) {
				this._setSliderColors(value);
			};

			/**
			 * Set style for handler
			 * @param {number} value
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._setHandlerStyle = function (value) {
				var self = this;

				if (self.options.warning) {
					if (value >= self._warningLevel) {
						self._ui.handlerElement.classList.add(classes.SLIDER_WARNING);
					} else {
						self._ui.handlerElement.classList.remove(classes.SLIDER_WARNING);
					}
				}
			};

			prototype._setDisabled = function (element) {
				var self = this,
					options = self.options;

				if (options.disabled === true || element.disabled) {
					self._disable(element);
				} else {
					self._enable(element);
				}
			};

			prototype._enable = function (element) {
				if (element) {
					this.options.disabled = false;
					if (this._ui.barElement) {
						this._ui.barElement.classList.remove(classes.SLIDER_DISABLED);
					}
				}
			};

			prototype._disable = function (element) {
				if (element) {
					this.options.disabled = true;
					if (this._ui.barElement) {
						this._ui.barElement.classList.add(classes.SLIDER_DISABLED);
					}
				}
			};

			/**
			 * Bind events to Slider
			 * @method _bindEvents
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._bindEvents = function () {
				bindEvents(this);
			};

			/**
			 * Bind event handlers
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				if (!this.options.disabled) {
					switch (event.type) {
						case "dragstart":
							self._onDragstart(event);
							break;
						case "dragend":
						case "dragcancel":
							self._onDragend(event);
							break;
						case "drag":
							self._onDrag(event);
							break;
					}
				}
			};

			/**
			 * Drag event handler
			 * @method _onDrag
			 * @param {Event} event
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._onDrag = function (event) {
				var self = this,
					ui = self._ui,
					validPosition,
					value;

				if (self._active) {
					validPosition = self.options.orientation === DEFAULT.HORIZONTAL ?
						event.detail.estimatedX - ui.barElement.offsetLeft :
						self._barElementHeight -
					(event.detail.estimatedY - utilDOM.getElementOffset(ui.barElement).top + selectors.getScrollableParent(self.element).scrollTop),

					value = self.options.orientation === DEFAULT.HORIZONTAL ?
						self._interval * validPosition / self._barElementWidth :
						self._interval * validPosition / self._barElementHeight;

					value += self._min;
					self._setValue(value);
				}
			};

			/**
			 * DragStart event handler
			 * @method _onDragstart
			 * @param {Event} event
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._onDragstart = function (event) {
				var self = this,
					ui = self._ui,
					validPosition = self.options.orientation === DEFAULT.HORIZONTAL ?
						event.detail.estimatedX - ui.barElement.offsetLeft :
						self._barElementHeight -
					(event.detail.estimatedY - utilDOM.getElementOffset(ui.barElement).top + selectors.getScrollableParent(self.element).scrollTop),
					value = self.options.orientation === DEFAULT.HORIZONTAL ?
						self._interval * validPosition / self._barElementWidth :
						self._interval * validPosition / self._barElementHeight;

				ui.handlerElement.classList.add(classes.SLIDER_HANDLER_ACTIVE);
				value += self._min;
				self._setValue(value);
				self._active = true;
			};

			/**
			 * DragEnd event handler
			 * @method _onDragend
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._onDragend = function () {
				var self = this,
					ui = self._ui;

				ui.handlerElement.classList.remove(classes.SLIDER_HANDLER_ACTIVE);
				self._active = false;
				if (self._previousValue !== self.element.value) {
					events.trigger(self.element, "change");
				}
				self._previousValue = self.element.value;
			};

			/**
			 * Refresh to Slider component
			 * @method refresh
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype.refresh = function () {
				this._setDisabled(this.element);
				this._initLayout();
			};

			/**
			 * Destroy Slider component
			 * @method _destroy
			 * @member ns.widget.core.Slider
			 * @protected
			 */
			prototype._destroy = function () {
				var self = this,
					barElement = self._ui.barElement;

				unbindEvents(self);
				barElement.parentNode.removeChild(barElement);
				self._ui = null;
				self._options = null;
			};
			ns.widget.core.Slider = Slider;
			engine.defineWidget(
				"Slider",
				"input[data-role='slider'], input[type='range'], input[data-type='range']",
				[
					"value"
				],
				Slider,
				"core"
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * #Progress namespace
 * Namespace with progress.
 * @author Heeju Joo <heeju.joo@samsung.com>
 */
(function (document, ns) {
	"use strict";
				ns.widget.core.progress = ns.widget.core.progress || {};
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * #type namespace
 * Namespace with types of Progress
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @class ns.widget.core.progress.type
 * @internal
 */
(function (window, ns) {
	"use strict";
				/** @namespace ns.widget.core */
			ns.widget.core.progress.type = ns.widget.core.progress.type || {};
			}(window, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Progress type Interface
 * Interface for type of progress
 * @internal
 * @class ns.widget.core.progress.type.interface
 */
(function (document, ns) {
	"use strict";
	
			ns.widget.core.progress.type.interface = {
				/**
				 * Init DOM for progress
				 * @method build
				 * @static
				 * @member ns.widget.core.progress.type.interface
				 */
				build: function (/*Progress*/) {
				},
				/**
				 * Init Style for progress
				 * @method init
				 * @static
				 * @member ns.widget.core.progress.type.interface
				 */
				init: function (/*Progress*/) {
				},
				/**
				 * Init Style for progress
				 * @method refresh
				 * @static
				 * @member ns.widget.core.progress.type.interface
				 */
				refresh: function (/*Progress*/) {

				},
				/**
				 * Init Style for progress
				 * @method changeValue
				 * @static
				 * @member ns.widget.core.progress.type.interface
				 */
				changeValue: function (/*Progress*/) {

				},
				/**
				 * Init Style for progress
				 * @method destroy
				 * @static
				 * @member ns.widget.core.progress.type.interface
				 */
				destroy: function (/*Progress*/) {

				}
			};
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Activity Bar Type
 * activityBar type support for Progress widget.
 * @internal
 * @class ns.widget.core.progress.type.activitybar
 * @extends ns.widget.core.progress.type.interface
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				type = ns.widget.core.progress.type,
				typeInterface = type.interface,
				classes = {
					uiActivitybar: "ui-activity-bar",
					uiActivitybarActivity: "ui-activity-bar-activity"
				};

			function paintProgressStyle(progress) {
				var ui = progress._ui,
					options = progress.options,
					percentValue = (options.value * 100) / (options.max - options.min);

				ui.activityBarElement.style.width = percentValue + "%";
			}

			type.activitybar = utilsObject.merge({}, typeInterface, {
				build: function (progress, element) {
					var ui = {},
						activityElement = element,
						activityBarElement;

					activityBarElement = document.createElement("div");

					activityElement.classList.add(classes.uiActivitybar);
					activityBarElement.classList.add(classes.uiActivitybarActivity);

					activityElement.appendChild(activityBarElement);

					ui.activityBarElement = activityBarElement;

					progress._ui = ui;

					return activityElement;
				},

				init: function (progress, element) {
					var ui = progress._ui,
						activityElement = element;

					ui.activityBarElement = ui.activityBarElement || activityElement.querySelector("." + classes.uiActivitybarActivity);

					paintProgressStyle(progress);
				},

				refresh: function (progress) {
					paintProgressStyle(progress);
				},

				changeValue: function (progress) {
					paintProgressStyle(progress);
				}
			});

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #activityCircle Type
 * activityCircle type support for Progress widget.
 * @internal
 * @class ns.widget.core.progress.type.activitycircle
 * @extends ns.widget.core.progress.type.interface
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				type = ns.widget.core.progress.type,
				typeInterface = type.interface,
				classes = {
					uiActivityCircle: "ui-activity-circle",
					uiActivityCircleSmall: "ui-activity-circle-small",
					uiActivityCircleMedium: "ui-activity-circle-medium",
					uiActivityCircleLarge: "ui-activity-circle-large",
					uiActivityCircle1: "ui-activity-circle1",
					uiActivityCircle2: "ui-activity-circle2",
					uiActivityCircle3: "ui-activity-circle3",
					uiCircle1Svg: "ui-circle1-svg",
					uiCircle2Svg: "ui-circle2-svg",
					uiCircle3Svg: "ui-circle3-svg",
					uiCircle1In: "ui-circle1-in",
					uiCircle2In: "ui-circle2-in",
					uiCircle3In: "ui-circle3-in"
				};

			function setSVGInnerAttribute(ui) {
				ui.animationCircle1SVGElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
				ui.animationCircle2SVGElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
				ui.animationCircle3SVGElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
				ui.animationCircle1SVGElement.setAttribute("viewBox", "0 0 360 360");
				ui.animationCircle2SVGElement.setAttribute("viewBox", "0 0 360 360");
				ui.animationCircle3SVGElement.setAttribute("viewBox", "0 0 360 360");
				ui.animationCircle1SVGInnerElement.setAttribute("cx", "334");
				ui.animationCircle1SVGInnerElement.setAttribute("cy", "180");
				ui.animationCircle1SVGInnerElement.setAttribute("r", "15");
				ui.animationCircle2SVGInnerElement.setAttribute("cx", "180");
				ui.animationCircle2SVGInnerElement.setAttribute("cy", "180");
				ui.animationCircle2SVGInnerElement.setAttribute("r", "154.333");
				ui.animationCircle3SVGInnerElement.setAttribute("cx", "180");
				ui.animationCircle3SVGInnerElement.setAttribute("cy", "180");
				ui.animationCircle3SVGInnerElement.setAttribute("r", "154.333");

			}

			function resetActivityCircleClasses(element, optionSize) {
				if (!element.classList.contains(classes.uiActivityCircle)) {
					element.classList.add(classes.uiActivityCircle);
				}

				element.classList.remove(classes.uiActivityCircleSmall);
				element.classList.remove(classes.uiActivityCircleMedium);
				element.classList.remove(classes.uiActivityCircleLarge);

				switch (optionSize) {
					case "small":
						element.classList.add(classes.uiActivityCircleSmall);
						break;
					case "medium":
						element.classList.add(classes.uiActivityCircleMedium);
						break;
					case "large":
						element.classList.add(classes.uiActivityCircleLarge);
						break;
					default:
						element.classList.add(classes.uiActivityCircleMedium);
				}
			}

			type.activitycircle = utilsObject.merge({}, typeInterface, {
				build: function (progress, element) {
					var ui = {},
						activityElement = element,
						animationCircle1Element,
						animationCircle2Element,
						animationCircle3Element,
						animationCircle1SVGElement,
						animationCircle2SVGElement,
						animationCircle3SVGElement,
						animationCircle1SVGInnerElement,
						animationCircle2SVGInnerElement,
						animationCircle3SVGInnerElement,
						svgNS = "http://www.w3.org/2000/svg",
						svgName = "svg",
						svgInnerName = "circle";

					animationCircle1Element = document.createElement("div");
					animationCircle2Element = document.createElement("div");
					animationCircle3Element = document.createElement("div");
					animationCircle1Element.classList.add(classes.uiActivityCircle1);
					animationCircle2Element.classList.add(classes.uiActivityCircle2);
					animationCircle3Element.classList.add(classes.uiActivityCircle3);

					/* CreateSVG Element for animationCircle1 */
					animationCircle1SVGElement = document.createElementNS(svgNS, svgName);
					animationCircle1SVGElement.setAttribute("class", classes.uiCircle1Svg);
					animationCircle1SVGInnerElement = document.createElementNS(svgNS, svgInnerName);
					animationCircle1SVGInnerElement.setAttribute("class", classes.uiCircle1In);
					animationCircle1SVGElement.appendChild(animationCircle1SVGInnerElement);

					/* CreateSVG Element for animationCircle2 */
					animationCircle2SVGElement = document.createElementNS(svgNS, svgName);
					animationCircle2SVGElement.setAttribute("class", classes.uiCircle2Svg);
					animationCircle2SVGInnerElement = document.createElementNS(svgNS, svgInnerName);
					animationCircle2SVGInnerElement.setAttribute("class", classes.uiCircle2In);
					animationCircle2SVGElement.appendChild(animationCircle2SVGInnerElement);

					/* CreateSVG Element for animationCircle3 */
					animationCircle3SVGElement = document.createElementNS(svgNS, svgName);
					animationCircle3SVGElement.setAttribute("class", classes.uiCircle3Svg);
					animationCircle3SVGInnerElement = document.createElementNS(svgNS, svgInnerName);
					animationCircle3SVGInnerElement.setAttribute("class", classes.uiCircle3In);
					animationCircle3SVGElement.appendChild(animationCircle3SVGInnerElement);

					animationCircle1Element.appendChild(animationCircle1SVGElement);
					animationCircle2Element.appendChild(animationCircle2SVGElement);
					animationCircle3Element.appendChild(animationCircle3SVGElement);

					activityElement.appendChild(animationCircle1Element);
					activityElement.appendChild(animationCircle2Element);
					activityElement.appendChild(animationCircle3Element);

					ui.animationCircle1Element = animationCircle1Element;
					ui.animationCircle2Element = animationCircle2Element;
					ui.animationCircle3Element = animationCircle3Element;
					ui.animationCircle1SVGElement = animationCircle1SVGElement;
					ui.animationCircle2SVGElement = animationCircle2SVGElement;
					ui.animationCircle3SVGElement = animationCircle3SVGElement;
					ui.animationCircle1SVGInnerElement = animationCircle1SVGInnerElement;
					ui.animationCircle2SVGInnerElement = animationCircle2SVGInnerElement;
					ui.animationCircle3SVGInnerElement = animationCircle3SVGInnerElement;

					progress._ui = ui;
					return element;
				},

				init: function (progress, element) {
					var options = progress.options,
						activityCircleSize = options.size;

					setSVGInnerAttribute(progress._ui);
					resetActivityCircleClasses(element, activityCircleSize);
				},
				refresh: function (progress) {
					var element = progress.element,
						activityCircleSize = progress.options.size;

					resetActivityCircleClasses(element, activityCircleSize);

				}
			});

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #progressBar Type
 * progressBar type support for Progress widget.
 * @internal
 * @class ns.widget.core.progress.type.progressbar
 * @extends ns.widget.core.progress.type.interface
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				type = ns.widget.core.progress.type,
				typeInterface = type.interface,

				classes = {
					uiProgressbar: "ui-progress-bar",
					uiProgressbarValue: "ui-progress-bar-value",
					uiProgressbarDecorator: "ui-progress-bar-decorator"
				},

				createAnimationFrame = function (duration, call1, call2, call3) {
					return [
						{
							start: 0,
							end: 800,
							callback: function (t, end) {
								call1.call(this, t, end);
							}
						},
						{
							start: 800,
							end: 1350,
							callback: function (t, end) {
								call2.call(this, t, end);
							}
						},
						{
							start: 1550,
							end: 1850,
							callback: function (t, end) {
								call3.call(this, t, end);
							}
						}
					];
				};

			function setAriaValues(element, options) {
				//set Aria value
				element.setAttribute("aria-valuenow", options.value);
				element.setAttribute("aria-valuemin", options.min);
				element.setAttribute("aria-valuemax", options.max);
			}

			function paintProgressStyle(progress) {
				var ui = progress._ui,
					options = progress.options,
					element = progress.element,
					elementHeightPercentValue = (element.offsetHeight * 100) / element.offsetWidth,
					percentValue = (options.value * 100) / (options.max - options.min);

				ui.progressBarValueElement.style.width = percentValue + "%";
				ui.progressBarDecoratorElement.style.width = elementHeightPercentValue + "%";
				ui.progressBarDecoratorElement.style.left = (percentValue - elementHeightPercentValue) + "%";
			}

			type.bar = utilsObject.merge({}, typeInterface, {
				build: function (progress, element) {
					var ui = {},
						progressBarValueElement,
						progressBarDecoratorElement;

					element.classList.add(classes.uiProgressbar);

					progressBarValueElement = document.createElement("div");
					progressBarValueElement.classList.add(classes.uiProgressbarValue);
					progressBarDecoratorElement = document.createElement("div");
					progressBarDecoratorElement.classList.add(classes.uiProgressbarDecorator);

					element.appendChild(progressBarValueElement);
					element.appendChild(progressBarDecoratorElement);

					ui.progressBarValueElement = progressBarValueElement;
					ui.progressBarDecoratorElement = progressBarDecoratorElement;

					progress._ui = ui;
					return element;
				},

				init: function (progress, element) {
					var ui = progress._ui,
						options = progress.options;

					ui.progressBarValueElement = ui.progressBarValueElement || element.querySelector("." + classes.uiProgressbarValue);
					ui.progressBarDecoratorElement = ui.progressBarDecoratorElement || element.querySelector("." + classes.progressBarDecoratorElement);

					ui.progressBarDecoratorElement.style.left = 0;

					setAriaValues(element, options);
					paintProgressStyle(progress);
				},

				refresh: function (progress) {
					setAriaValues(progress.element, progress.options);
					paintProgressStyle(progress);
				},

				changeValue: function (progress, oldValue, newValue) {
					var duration = 1850,
						element = progress.element,
						valueElement = progress._ui.progressBarValueElement,
						decoElement = progress._ui.progressBarDecoratorElement,
						decoElementOldLeft = parseFloat(decoElement.style.left),
						decoElementOldWidth = (decoElement.offsetWidth * 100) / element.offsetWidth,
						elementHeightPercentValue = (element.offsetHeight * 100) / element.offsetWidth,
						oldPercentValue = (oldValue * 100) / (progress.options.max - progress.options.min),
						newPercentValue = (newValue * 100) / (progress.options.max - progress.options.min),
						animationFrames;

					if (!progress._isAnimating) {
						decoElement.style.opacity = 1;
						animationFrames = createAnimationFrame(duration, function (t, end) {
							valueElement.style.width = oldPercentValue + ((newPercentValue - oldPercentValue) * t / end) + "%";
							if (newValue > oldValue) {
								decoElement.style.width = decoElementOldWidth + ((newPercentValue - oldPercentValue) * t / end) + "%";
							} else {
								decoElement.style.width = decoElementOldWidth + ((oldPercentValue - newPercentValue) * t / end) + "%";
								decoElement.style.left = oldPercentValue - parseFloat(decoElement.style.width) + "%";
								decoElementOldLeft = parseFloat(decoElement.style.left);
							}
						}, function (t, end) {
							if (newValue > oldValue) {
								decoElement.style.left = decoElementOldLeft + ((newPercentValue - decoElementOldLeft - elementHeightPercentValue) * t / end) + "%";
								decoElement.style.width = (decoElementOldWidth + newPercentValue - oldPercentValue) - ((decoElementOldWidth + newPercentValue - oldPercentValue - elementHeightPercentValue) * t / end) + "%";
							} else {
								decoElement.style.width = (decoElementOldWidth + (oldPercentValue - newPercentValue)) - ((oldPercentValue - newPercentValue) * t / end) + "%";
							}
						}, function (t, end) {
							if (newValue >= progress.options.max) {
								decoElement.style.opacity = 1 - t / end;
							}
						});
						progress._animate(duration, function (t) {
							animationFrames.forEach(function (animation) {
								if (t >= animation.start && t <= animation.end) {
									animation.callback(t - animation.start, animation.end - animation.start);
								} else if (t > animation.end) {
									animation.callback(1, 1);
								}
							});
						}, function () {
							if (progress.options.value !== newValue) {
								type.bar.changeValue(progress, newValue, progress.options.value);
							}
						});
					}
				}
			});
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #progressCircle Type
 * progressCircle type support for Progress widget.
 * @internal
 * @class ns.widget.core.progress.type.progresscircle
 * @extends ns.widget.core.progress.type.interface
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				doms = ns.util.DOM,
				type = ns.widget.core.progress.type,
				typeInterface = type.interface,
				classes = {
					uiProgressCircle: "ui-progress-circle",
					uiProgressCircleBg: "ui-progress-circle-bg",
					uiProgressCircleValue: "ui-progress-circle-value",
					uiProgressCircleValueLeft: "ui-progress-circle-value-left",
					uiProgressCircleValueRight: "ui-progress-circle-value-right",
					uiProgressCircleHalf: "ui-progress-circle-half"
				};

			/* make widget refresh with new value */
			function paintProgressCircle(self) {
				var options = self.options,
					percentValue = (options.value * 100) / (options.max - options.min),
					rotateValue,
					ui = self._ui;

				if (percentValue >= 50) {
					ui.progressValue.classList.add(classes.uiProgressCircleHalf);
				} else {
					ui.progressValue.classList.remove(classes.uiProgressCircleHalf);
				}

				rotateValue = 360 * (percentValue / 100);

				ui.progressValueLeft.style.webkitTransform = "rotate3d(0.0, 0.0, 1.0, " + rotateValue + "deg)";
			}

			function setProgressBarSize(self) {
				var progressSize = self.options.size,
					sizeToNumber = parseFloat(progressSize),
					ui = self._ui;

				if (!isNaN(sizeToNumber)) {
					ui.progressContainer.style.fontSize = progressSize + "px";
					ui.progressContainer.style.width = progressSize + "px";
					ui.progressContainer.style.height = progressSize + "px";
				} else {
					switch (progressSize) {
						case "full":
						case "large":
						case "medium":
						case "small":
							ui.progressContainer.classList.add("ui-progress-circle-" + progressSize);
							break;
					}
					ui.progressContainer.style.fontSize = doms.getCSSProperty(ui.progressContainer, "width", 0, "float") + "px";
				}
			}

			function resetDOM(ui) {
				ui.progressValue.classList.remove(classes.uiProgressbarHalf);
				ui.progressValueLeft.style.webkitTransform = "";
			}

			type.circle = utilsObject.merge({}, typeInterface, {
				build: function (progress, element) {
					var ui = {},
						progressElement = element,
						progresscircleBg,
						progresscircleValue,
						progresscircleValueLeft,
						progresscircleValueRight;

					ui.progressContainer = progressElement;
					ui.progressValueBg = progresscircleBg = document.createElement("div");
					ui.progressValue = progresscircleValue = document.createElement("div");
					ui.progressValueLeft = progresscircleValueLeft = document.createElement("div");
					ui.progressValueRight = progresscircleValueRight = document.createElement("div");

					// set classNames of progresscircle DOMs.
					progressElement.className = classes.uiProgressCircle;
					progresscircleBg.className = classes.uiProgressCircleBg;
					progresscircleValue.className = classes.uiProgressCircleValue;
					progresscircleValueLeft.className = classes.uiProgressCircleValueLeft;
					progresscircleValueRight.className = classes.uiProgressCircleValueRight;

					progresscircleValue.appendChild(progresscircleValueLeft);
					progresscircleValue.appendChild(progresscircleValueRight);
					progressElement.appendChild(progresscircleValue);
					progressElement.appendChild(progresscircleBg);

					progress._ui = ui;
					return element;
				},

				init: function (progress, element) {
					var ui = progress._ui;

					ui.progressContainer = ui.progressContainer || element;
					ui.progressValueBg = ui.progressValueBg || element.querySelector("." + classes.uiProgressCircleBg);
					ui.progressValue = ui.progressValue || element.querySelector("." + classes.uiProgressCircleValue);
					ui.progressValueLeft = ui.progressValueLeft || element.querySelector("." + classes.uiProgressCircleValueLeft);
					ui.progressValueRight = ui.progressValueRight || element.querySelector("." + classes.uiProgressCircleValueRight);

					setProgressBarSize(progress);
					paintProgressCircle(progress);
				},

				refresh: function (progress) {
					resetDOM(progress._ui);
					setProgressBarSize(progress);
					paintProgressCircle(progress);
				},

				changeValue: function (progress) {
					paintProgressCircle(progress);
				}
			});

			}(window.document, ns));

/*global window, define, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Progress
 * Progress component shows that an operation is in progress.
 *
 * #Crete widget
 *
 * Shows a control that indicates the progress percentage of an on-going operation by circular shape.
 *
 *     @example template tau-progress
 *         <progress class="ui-circle-progress" max="${1:100}" value="${2:20}"></progress>
 *
 *
 * ##Set and Get the value
 * You can set or get the value with the value() method
 *
 * @since 2.0
 * @class ns.widget.core.progress.Progress
 * @component-selector .ui-circle-progress, .ui-progress
 * @extends ns.widget.BaseWidget
 * @author Heeju Joo <heeju.joo@samsung.com>
 */
(function (document, ns) {
	"use strict";
	
			var BaseWidget = ns.widget.BaseWidget,
				events = ns.event,
				engine = ns.engine,
				util = ns.util,
				selectors = ns.util.selectors,
				utilsObject = ns.util.object,
				Page = ns.widget.core.Page,
				eventType = {
					/**
					 * Event is triggered when value of widget is changing.
					 * @event change
					 * @member ns.widget.mobile.ProgressBar
					 */
					CHANGE: "change"
				},

				progressType = {
					PROGRESS_BAR: "bar",
					PROGRESS_CIRCLE: "circle",
					ACTIVITY_BAR: "activitybar",
					ACTIVITY_CIRCLE: "activitycircle"
				},

				circleSize = {
					SMALL: "small",
					MEDIUM: "medium",
					LARGE: "large",
					FULL: "full"
				},
				/**
				 * Progress constructor
				 * @method Progress
				 */
				Progress = function () {
					var self = this;

					self.options = utilsObject.merge({}, Progress.defaults);
					self._ui = {};
					self._type = null;
					self._progress = null;
					self._isAnimating = false;
					self._callbacks = {};
				},
				/**
				 * Dictionary object containing commonly used widget classes
				 * @property {Object} classes
				 * @member ns.widget.core.progress.Progress
				 * @private
				 * @static
				 * @readonly
				 */
				classes = {
					uiProgress: "ui-progress"
				},
				defaults = {
					type: progressType.PROGRESS_BAR,
					size: circleSize.MEDIUM,
					value: 100,
					min: 0,
					max: 100
				},

				prototype = new BaseWidget();

			Progress.prototype = prototype;
			Progress.classes = classes;
			Progress.events = eventType;
			Progress.defaults = defaults;

			/**
			 * Build structure of Progress component
			 * @method _build
			 * @param {HTMLElement} element
			 * @member ns.widget.core.progress.Progress
			 * @protected
			 */
			prototype._build = function (element) {
				var self = this,
					options = self.options;

				self._type = options.type;

				element.classList.add(classes.uiProgress);
				self._progress = ns.widget.core.progress.type[options.type];
				self._progress.build(self, element);

				return element;
			};

			/**
			 * Initialization of Progress component
			 * @method _init
			 * @param {HTMLElement} element
			 * @member ns.widget.core.progress.Progress
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this;

				self._progress.init(self, element);
				element.setAttribute("value", self.options.value);

				return element;
			};

			/**
			 * Refresh of Progress
			 * @method _refresh
			 * @param {HTMLElement} element
			 * @member ns.widget.core.progress.Progress
			 * @protected
			 */
			prototype._refresh = function (element) {
				var self = this,
					options = self.options;

				if (self._type !== options.type) {
					self._destroy();
					return ns.widget.Progress(element, {type: options.type});
				} else {
					self._progress.refresh(self);
					self._setValue(self.options.value);
				}

				return element;
			};

			prototype._setValue = function (value) {
				var self = this,
					options = self.options,
					element = self.element;

				self._oldValue = options.value;


				if (typeof value === "number") {
					value = Math.min(options.max, Math.max(options.min, value));
					// value changed
					if (value !== self._oldValue) {
						options.value = value;
						if (!self.isCustomElement) {
							element.setAttribute("data-value", value);
						}
						element.setAttribute("value", value);
						events.trigger(element, eventType.CHANGE);
						self._progress.changeValue(self, self._oldValue, value);
					}
					return true;
				}
				return false;
			};

			/**
			 * Return value of progress
			 * @return {number}
			 * @private
			 */
			prototype._getValue = function () {
				return parseInt(this.element.getAttribute("value"), 10);
			};

			prototype._animate = function (duration, progressCallback, finishCallback) {
				var self = this,
					startTime = null;

				self._isAnimating = true;

				util.requestAnimationFrame(function step(timeStamp) {
					var currentTimeGap = 0;

					if (startTime === null) {
						startTime = timeStamp;
					}
					currentTimeGap = timeStamp - startTime;

					progressCallback(currentTimeGap);

					if (self._isAnimating && duration > currentTimeGap) {
						util.requestAnimationFrame(step);
					} else {
						self._isAnimating = false;
						finishCallback();
					}
				});
			};

			/**
			 * Callback on event pagebeforeshow
			 * @method pageBeforeShow
			 * @param {ns.widget.core.progress.Progress} self
			 * @private
			 * @member ns.widget.core.progress.Progress
			 */
			function pageBeforeShow(self) {
				self.refresh();
			}

			/**
			 * Bind events to Progress
			 * @method _bindEvents
			 * @member ns.widget.core.progress.Progress
			 * @protected
			 */
			prototype._bindEvents = function () {
				var self = this,
					element = self.element,
					page = selectors.getClosestByClass(element, Page.classes.uiPage);

				self._callbacks.onPageBeforeShow = pageBeforeShow.bind(null, self);
				page.addEventListener(Page.events.BEFORE_SHOW, self._callbacks.onPageBeforeShow, false);
			};

			/**
			 * Destroys Progress component
			 * @method _destroy
			 * @member ns.widget.core.progress.Progress
			 * @protected
			 */
			prototype._destroy = function () {
				var self = this,
					element = self.element,
					page;

				while (element.firstChild) {
					element.removeChild(element.firstChild);
				}

				self._ui = null;
				self._oldValue = null;

				if (self._callbacks.onPageBeforeShow) {
					page = selectors.getClosestByClass(element, Page.classes.uiPage);
					page.removeEventListener(Page.events.BEFORE_SHOW, self._callbacks.onPageBeforeShow, false);
				}

				return element;
			};

			ns.widget.core.progress.Progress = Progress;

			engine.defineWidget(
				"Progress",
				"[data-role='progress'], .ui-progress",
				[],
				Progress,
				"core"
			);

			return Progress;
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Popup
 * Popup component supports 2 pop-ups: the position-to-window pop-up (like a system pop-up), and the context pop-up.
 *
 * @since 2.0
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @class ns.widget.mobile.Popup
 * @extends ns.widget.core.BaseWidget
 */
(function () {
	"use strict";
				/**
			 * Alias for {@link ns.widget.BaseWidget}
			 * @property {Function} BaseWidget
			 * @member ns.widget.core.Popup
			 * @private
			 */
			var BaseWidget = ns.widget.BaseWidget,
				/**
				 * Alias for class ns.engine
				 * @property {ns.engine} engine
				 * @member ns.widget.core.Popup
				 * @private
				 */
				engine = ns.engine,
				/**
				 * Alias for class ns.util.object
				 * @property {Object} objectUtils
				 * @member ns.widget.core.Popup
				 * @private
				 */
				objectUtils = ns.util.object,
				/**
				 * Alias for class ns.util.deferred
				 * @property {Object} UtilDeferred
				 * @member ns.widget.core.Popup
				 * @private
				 */
				UtilDeferred = ns.util.deferred,
				/**
				 * Alias for class ns.util.selectors
				 * @property {Object} utilSelector
				 * @member ns.widget.core.Popup
				 * @private
				 */
				utilSelector = ns.util.selectors,
				/**
				 * Alias for class ns.event
				 * @property {Object} eventUtils
				 * @member ns.widget.core.Popup
				 * @private
				 */
				eventUtils = ns.event,
				/**
				 * Alias for Router, loose requirement
				 * @property {ns.router.Router} Router
				 * @member ns.widget.core.Popup
				 * @private
				 */
				Router = ns.router && ns.router.Router,

				/**
				 * Alias for class ns.widget.core.Page
				 * @property {ns.router.Router} Router
				 * @member ns.widget.core.Popup
				 * @private
				 */
				Page = ns.widget.core.Page,

				POPUP_SELECTOR = "[data-role='popup'], .ui-popup",

				Popup = function () {
					var self = this,
						ui = {};

					self.selectors = selectors;
					self.options = objectUtils.merge({}, Popup.defaults);
					self.storedOptions = null;
					/**
					 * Popup state flag
					 * @property {0|1|2|3} [state=null]
					 * @member ns.widget.core.Popup
					 * @private
					 */
					self.state = states.CLOSED;

					ui.overlay = null;
					ui.header = null;
					ui.footer = null;
					ui.content = null;
					ui.container = null;
					ui.wrapper = null;
					self._ui = ui;

					// event callbacks
					self._callbacks = {};
				},
				/**
				 * Object with default options
				 * @property {Object} defaults
				 * @property {string} [options.transition="none"] Sets the default transition for the popup.
				 * @property {string} [options.positionTo="window"] Sets the element relative to which the popup will be centered.
				 * @property {boolean} [options.dismissible=true] Sets whether to close popup when a popup is open to support the back button.
				 * @property {boolean} [options.overlay=true] Sets whether to show overlay when a popup is open.
				 * @property {boolean|string} [options.header=false] Sets content of header.
				 * @property {boolean|string} [options.footer=false] Sets content of footer.
				 * @property {string} [options.content=null] Sets content of popup.
				 * @property {string} [options.overlayClass=""] Sets the custom class for the popup background, which covers the entire window.
				 * @property {string} [options.closeLinkSelector="a[data-rel='back']"] Sets selector for close buttons in popup.
				 * @property {boolean} [options.history=true] Sets whether to alter the url when a popup is open to support the back button.
				 * @member ns.widget.core.Popup
				 * @static
				 */
				defaults = {
					transition: "none",
					dismissible: true,
					overlay: true,
					header: false,
					footer: false,
					content: null,
					overlayClass: "",
					closeLinkSelector: "[data-rel='back']",
					history: null,
					closeAfter: null
				},
				states = {
					DURING_OPENING: 0,
					OPENED: 1,
					DURING_CLOSING: 2,
					CLOSED: 3
				},
				CLASSES_PREFIX = "ui-popup",
				/**
				 * Dictionary for popup related css class names
				 * @property {Object} classes
				 * @member ns.widget.core.Popup
				 * @static
				 */
				/**
				 * Toast style of popup
				 * @style ui-popup-toast
				 * @member ns.widget.core.Popup
				 * @wearable
				 */
				/**
				 * Toast style of popup with graphic
				 * @style ui-popup-toast-graphic
				 * @member ns.widget.core.Popup
				 * @wearable
				 */
				classes = {
					popup: CLASSES_PREFIX,
					active: CLASSES_PREFIX + "-active",
					overlay: CLASSES_PREFIX + "-overlay",
					header: CLASSES_PREFIX + "-header",
					footer: CLASSES_PREFIX + "-footer",
					content: CLASSES_PREFIX + "-content",
					wrapper: CLASSES_PREFIX + "-wrapper",
					toast: CLASSES_PREFIX + "-toast",
					toastSmall: CLASSES_PREFIX + "-toast-small",
					build: "ui-build",
					overlayShown: CLASSES_PREFIX + "-overlay-shown"
				},
				/**
				 * Dictionary for popup related selectors
				 * @property {Object} selectors
				 * @member ns.widget.core.Popup
				 * @static
				 */
				selectors = {
					header: "." + classes.header,
					content: "." + classes.content,
					footer: "." + classes.footer
				},
				EVENTS_PREFIX = "popup",
				/**
				 * Dictionary for popup related events
				 * @property {Object} events
				 * @member ns.widget.core.Popup
				 * @static
				 */
				events = {
					/**
					 * Triggered when the popup has been created in the DOM (via ajax or other) but before all widgets have had an opportunity to enhance the contained markup.
					 * @event popupshow
					 * @member ns.widget.core.Popup
					 */
					show: EVENTS_PREFIX + "show",
					/**
					 * Triggered on the popup after the transition animation has completed.
					 * @event popuphide
					 * @member ns.widget.core.Popup
					 */
					hide: EVENTS_PREFIX + "hide",
					/**
					 * Triggered on the popup we are transitioning to, before the actual transition animation is kicked off.
					 * @event popupbeforeshow
					 * @member ns.widget.core.Popup
					 */
					/* eslint-disable camelcase */
					// we can't change this in this moment because this is part of API
					before_show: EVENTS_PREFIX + "beforeshow",
					/**
					 * Triggered on the popup we are transitioning to, before the actual transition animation is kicked off, animation has started.
					 * @event popuptransitionstart
					 * @member ns.widget.core.Popup
					 */
					transition_start: EVENTS_PREFIX + "transitionstart",
					/**
					 * Triggered on the popup we are transitioning away from, before the actual transition animation is kicked off.
					 * @event popupbeforehide
					 * @member ns.widget.core.Popup
					 */
					before_hide: EVENTS_PREFIX + "beforehide"
					/* eslint-enable camelcase */
				},

				prototype = new BaseWidget();

			Popup.classes = classes;
			Popup.events = events;
			Popup.defaults = defaults;
			Popup.selector = POPUP_SELECTOR;

			/**
			 * Build the content of popup
			 * @method _buildContent
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._buildContent = function (element) {
				var self = this,
					ui = self._ui,
					selectors = self.selectors,
					options = self.options,
					content = ui.content || element.querySelector(selectors.content),
					footer = ui.footer || element.querySelector(selectors.footer),
					elementChildren = [].slice.call(element.childNodes),
					elementChildrenLength = elementChildren.length,
					i,
					node;

				if (!content) {
					content = document.createElement("div");
					content.className = classes.content;
					for (i = 0; i < elementChildrenLength; ++i) {
						node = elementChildren[i];
						if (node !== ui.footer && node !== ui.header) {
							content.appendChild(node);
						}
					}
					if (typeof options.content === "string") {
						content.innerHTML = options.content;
					}
					element.insertBefore(content, footer);
				}
				content.classList.add(classes.content);
				ui.content = content;
			};

			/**
			 * Build the header of popup
			 * @method _buildHeader
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._buildHeader = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options,
					selectors = self.selectors,
					content = ui.content || element.querySelector(selectors.content),
					header = ui.header || element.querySelector(selectors.header);

				if (!header && options.header !== false) {
					header = document.createElement("div");
					header.className = classes.header;
					if (typeof options.header !== "boolean") {
						header.innerHTML = options.header;
					}
					element.insertBefore(header, content);
				}
				if (header) {
					header.classList.add(classes.header);
				}
				ui.header = header;
			};

			/**
			 * Set the header of popup.
			 * This function is called by function "option" when the option "header" is set.
			 * @method _setHeader
			 * @param {HTMLElement} element
			 * @param {boolean|string} value
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._setHeader = function (element, value) {
				var self = this,
					ui = self._ui,
					header = ui.header;

				if (header) {
					header.parentNode.removeChild(header);
					ui.header = null;
				}
				self.options.header = value;
				self._buildHeader(ui.container);
			};

			/**
			 * Build the footer of popup
			 * @method _buildFooter
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._buildFooter = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options,
					footer = ui.footer || element.querySelector(self.selectors.footer);

				if (!footer && options.footer !== false) {
					footer = document.createElement("div");
					footer.className = classes.footer;
					if (typeof options.footer !== "boolean") {
						footer.innerHTML = options.footer;
					}
					element.appendChild(footer);
				}
				if (footer) {
					footer.classList.add(classes.footer);
				}
				ui.footer = footer;
			};

			/**
			 * Set the footer of popup.
			 * This function is called by function "option" when the option "footer" is set.
			 * @method _setFooter
			 * @param {HTMLElement} element
			 * @param {boolean|string} value
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._setFooter = function (element, value) {
				var self = this,
					ui = self._ui,
					footer = ui.footer;

				if (footer) {
					footer.parentNode.removeChild(footer);
					ui.footer = null;
				}
				self.options.footer = value;
				self._buildFooter(ui.container);
			};

			/**
			 * Build structure of Popup widget
			 * @method _build
			 * @param {HTMLElement} element of popup
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.Popup
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					wrapper,
					child = element.firstChild,
					elementClassList = element.classList;

				// set class for element
				elementClassList.add(classes.popup);

				if (elementClassList.contains(classes.toastSmall)) {
					elementClassList.add(classes.toast);
				}

				// create wrapper
				wrapper = document.createElement("div");
				wrapper.classList.add(classes.wrapper);
				ui.wrapper = wrapper;
				ui.container = wrapper;
				// move all children to wrapper
				while (child) {
					wrapper.appendChild(child);
					child = element.firstChild;
				}
				// add wrapper and arrow to popup element
				element.appendChild(wrapper);

				// build header, footer and content
				self._buildHeader(ui.container);
				self._buildFooter(ui.container);
				self._buildContent(ui.container);

				// set overlay
				self._setOverlay(element, self.options.overlay);

				return element;
			};

			/**
			 * Set overlay
			 * @method _setOverlay
			 * @param {HTMLElement} element
			 * @param {boolean} enable
			 * @protected
			 * @member ns.widget.Popup
			 */
			prototype._setOverlay = function (element, enable) {
				var self = this,
					overlayClass = self.options.overlayClass,
					ui = self._ui,
					overlay = ui.overlay;

				// if this popup is not connected with slider,
				// we create overlay, which is invisible when
				// the value of option overlay is false
				/// @TODO: get class from widget
				if (!element.classList.contains("ui-slider-popup") && !element.classList.contains(classes.toast)) {
					// create overlay
					if (!overlay) {
						overlay = document.createElement("div");

						if (element.parentNode) {
							element.parentNode.insertBefore(overlay, element);
						} else {
							ns.warn("Popup is creating on element outside DOM");
						}

						ui.overlay = overlay;
					}
					overlay.className = classes.overlay + (overlayClass ? " " + overlayClass : "");
					if (enable) {
						overlay.style.opacity = "";
					} else {
						// if option is set on "false", the overlay is not visible
						overlay.style.opacity = 0;
					}
				}
			};

			/**
			 * Returns the state of the popup
			 * @method _isActive
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._isActive = function () {
				var state = this.state;

				return state === states.DURING_OPENING || state === states.OPENED;
			};

			/**
			 * Returns true if popup is already opened and visible
			 * @method _isActive
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._isOpened = function () {
				return this.state === states.OPENED;
			};

			/**
			 * Init widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._init = function (element) {
				var self = this,
					selectors = self.selectors,
					ui = self._ui,
					options = self.options,
					elementClassList = self.element.classList;

				ui.header = ui.header || element.querySelector(selectors.header);
				ui.footer = ui.footer || element.querySelector(selectors.footer);
				ui.content = ui.content || element.querySelector(selectors.content);
				ui.wrapper = ui.wrapper || element.querySelector("." + classes.wrapper);
				ui.container = ui.wrapper || element;

				ui.page = utilSelector.getClosestByClass(element, Page.classes.uiPage) || window;
				ui.pageContent = (typeof ui.page.querySelector === "function") ?
					ui.page.querySelector("." + Page.classes.uiContent) : null;

				if (elementClassList.contains(classes.toast)) {
					options.closeAfter = options.closeAfter || 2000;
				}
				// if option history is not set in constructor or in HTML
				if (options.history === null) {
					// for toast we set false for other true
					options.history = !elementClassList.contains(classes.toast);
				}
			};

			/**
			 * Set the state of the popup
			 * @method _setActive
			 * @param {boolean} active
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._setActive = function (active) {
				var self = this,
					activeClass = classes.active,
					elementClassList = self.element.classList,
					route = Router && Router.getInstance().getRoute("popup"),
					options;

				// NOTE: popup's options object is stored in window.history at the router module,
				// and this window.history can't store DOM element object.
				options = objectUtils.merge({}, self.options, {positionTo: null, link: null});

				// set state of popup and add proper class
				if (active) {
					// set global variable
					if (route) {
						route.setActive(self, options);
					}
					// add proper class
					elementClassList.add(activeClass);
					// set state of popup 	358
					self.state = states.OPENED;
				} else {
					// no popup is opened, so set global variable on "null"
					if (route) {
						route.setActive(null, options);
					}
					// remove proper class
					elementClassList.remove(activeClass);
					// set state of popup
					self.state = states.CLOSED;
				}
			};

			/**
			 * Bind events
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._bindEvents = function () {
				var self = this;

				eventUtils.on(self._ui.page, "pagebeforehide", self, false);
				eventUtils.on(window, "resize", self, false);
				eventUtils.on(document, "vclick", self, false);
			};


			/**
			 * Unbind events
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._unbindEvents = function () {
				var self = this;

				eventUtils.off(self._ui.page, "pagebeforehide", self, false);
				eventUtils.off(window, "resize", self, false);
				eventUtils.off(document, "vclick", self, false);
			};

			/**
			 * Layouting popup structure
			 * @method layout
			 * @member ns.widget.core.Popup
			 */
			prototype._layout = function () {
			};

			/**
			 * Open the popup
			 * @method open
			 * @param {Object=} [options]
			 * @param {string=} [options.transition] options.transition
			 * @member ns.widget.core.Popup
			 */
			prototype.open = function (options) {
				var self = this,
					newOptions,
					onClose = self.close.bind(self);


				if (!self._isActive()) {
					/*
					 * Some passed options on open need to be kept until popup closing.
					 * For example, transition parameter should be kept for closing animation.
					 * On the other hand, fromHashChange or x, y parameter should be removed.
					 * We store options and restore them on popup closing.
					 */
					self._storeOpenOptions(options);

					newOptions = objectUtils.merge(self.options, options);
					if (!newOptions.dismissible) {
						ns.router.Router.getInstance().lock();
					}


					if (newOptions.closeAfter > 0) {
						if (self.element.classList.contains(classes.toast)) {
							newOptions.transition = "fade";
						}
						self._show(newOptions);

						self._closeTimeout = window.setTimeout(onClose, newOptions.closeAfter);
					} else {
						self._show(newOptions);
					}
				}
			};


			/**
			 * Close the popup
			 * @method close
			 * @param {Object=} [options]
			 * @param {string=} [options.transition]
			 * @member ns.widget.core.Popup
			 */
			prototype.close = function (options) {
				var self = this,
					newOptions = objectUtils.merge(self.options, options);

				if (self._isActive()) {
					clearTimeout(self._closeTimeout);
					if (!newOptions.dismissible) {
						ns.router.Router.getInstance().unlock();
					}
					self._hide(newOptions);
				}
			};

			/**
			 * Store Open options.
			 * @method _storeOpenOptions
			 * @param {Object} options
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._storeOpenOptions = function (options) {
				var self = this,
					oldOptions = self.options,
					storedOptions = {},
					key;

				for (key in options) {
					if (options.hasOwnProperty(key)) {
						storedOptions[key] = oldOptions[key];
					}
				}

				self.storedOptions = storedOptions;
			};

			/**
			 * Restore Open options and remove some unnecessary ones.
			 * @method _storeOpenOptions
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._restoreOpenOptions = function () {
				var self = this,
					options = self.options,
					propertiesToRemove = ["x", "y", "fromHashChange"];

				// we restore opening values of all options
				options = objectUtils.merge(options, self.storedOptions);
				// and remove all values which should not be stored
				objectUtils.removeProperties(options, propertiesToRemove);
			};

			/**
			 * Show popup.
			 * @method _show
			 * @param {Object} options
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._show = function (options) {
				var self = this,
					transitionOptions = objectUtils.merge({}, options),
					overlay = self._ui.overlay,
					pageContent = self._ui.pageContent;

				// set layout
				self._layout(self.element);

				// change state of popup
				self.state = states.DURING_OPENING;
				// set transition
				transitionOptions.ext = " in ";

				self.trigger(events.before_show);
				// show overlay
				if (overlay) {
					overlay.classList.toggle(classes.overlayShown, true);
				}

				// disable page pointer events
				if (pageContent) {
					pageContent.classList.toggle(Page.classes.uiContentUnderPopup, true);
				}

				// start opening animation
				self._transition(transitionOptions, self._onShow.bind(self));

				// animation has started
				self.trigger(events.transition_start);
			};

			/**
			 * Show popup
			 * @method _onShow
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._onShow = function () {
				var self = this;

				self._setActive(true);
				self.trigger(events.show);
			};

			/**
			 * Hide popup
			 * @method _hide
			 * @param {Object} options
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._hide = function (options) {
				var self = this,
					isOpened = self._isOpened(),
					callbacks = self._callbacks,
					pageContent = self._ui.pageContent;

				// change state of popup
				self.state = states.DURING_CLOSING;

				self.trigger(events.before_hide);

				// enable page pointer events
				if (pageContent) {
					pageContent.classList.toggle(Page.classes.uiContentUnderPopup, false);
				}

				if (isOpened) {
					// popup is opened, so we start closing animation
					options.ext = " out ";
					self._transition(options, self._onHide.bind(self));
				} else {
					// popup is active, but not opened yet (DURING_OPENING), so
					// we stop opening animation
					if (callbacks.transitionDeferred) {
						callbacks.transitionDeferred.reject();
					}
					if (callbacks.animationEnd) {
						callbacks.animationEnd();
					}
					// and set popup as inactive
					self._onHide();
				}
			};

			/**
			 * Hide popup
			 * @method _onHide
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._onHide = function () {
				var self = this,
					overlay = self._ui.overlay;

				self._setActive(false);

				if (overlay) {
					overlay.classList.toggle(classes.overlayShown, false);
				}
				self._restoreOpenOptions();
				self.trigger(events.hide);
			};

			/**
			 * Handle events
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.Popup
			 */
			prototype.handleEvent = function (event) {
				var self = this,
					router = ns.router.Router.getInstance();

				switch (event.type) {
					case "pagebeforehide":
						// we need close active popup if exists
						router.close(null, {transition: "none", rel: "popup"});
						break;
					case "resize":
						self._onResize(event);
						break;
					case "vclick":
						if (event.target === self._ui.overlay) {
							self._onClickOverlay(event);
						}
						break;
				}
			};

			/**
			 * Refresh structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._refresh = function () {
				var self = this;

				self._setOverlay(self.element, self.options.overlay);
			};

			/**
			 * Callback function fires after clicking on overlay.
			 * @method _onClickOverlay
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._onClickOverlay = function (event) {
				var options = this.options;

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

				if (options.dismissible) {
					ns.router.Router.getInstance().close(null, {rel: "popup"});
				}
			};

			/**
			 * Callback function fires on resizing
			 * @method _onResize
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._onResize = function () {
				if (this._isOpened()) {
					this._refresh();
				}
			};

			function clearAnimation(self, transitionClass, deferred) {
				var element = self.element,
					elementClassList = element.classList,
					overlay = self._ui.overlay,
					animationEndCallback = self._callbacks.animationEnd;

				// remove callbacks on animation events
				element.removeEventListener("animationend", animationEndCallback, false);
				element.removeEventListener("webkitAnimationEnd", animationEndCallback, false);
				element.removeEventListener("mozAnimationEnd", animationEndCallback, false);
				element.removeEventListener("oAnimationEnd", animationEndCallback, false);
				element.removeEventListener("msAnimationEnd", animationEndCallback, false);

				// clear classes
				transitionClass.split(" ").forEach(function (currentClass) {
					currentClass = currentClass.trim();
					if (currentClass.length > 0) {
						elementClassList.remove(currentClass);
						if (overlay) {
							overlay.classList.remove(currentClass);
						}
					}
				});
				if (deferred.state() === "pending") {
					// we resolve only pending (not rejected) deferred
					deferred.resolve();
				}
			}

			function setTransitionDeferred(self, resolve) {
				var deferred = new UtilDeferred();

				deferred.then(function () {
					if (deferred === self._callbacks.transitionDeferred) {
						resolve();
					}
				});

				self._callbacks.transitionDeferred = deferred;
				return deferred;
			}

			/**
			 * Animate popup opening/closing
			 * @method _transition
			 * @protected
			 * @param {Object} [options]
			 * @param {string=} [options.transition]
			 * @param {string=} [options.ext]
			 * @param {?Function} [resolve]
			 * @member ns.widget.core.Popup
			 */
			prototype._transition = function (options, resolve) {
				var self = this,
					transition = options.transition || self.options.transition || "none",
					transitionClass = transition + options.ext,
					element = self.element,
					elementClassList = element.classList,
					overlayClassList,
					deferred,
					animationEndCallback;

				if (self._ui.overlay) {
					overlayClassList = self._ui.overlay.classList;
				}

				deferred = setTransitionDeferred(self, resolve);

				if (transition !== "none") {
					// set animationEnd callback
					animationEndCallback = clearAnimation.bind(null, self, transitionClass, deferred);
					self._callbacks.animationEnd = animationEndCallback;

					// add animation callbacks
					element.addEventListener("animationend", animationEndCallback, false);
					element.addEventListener("webkitAnimationEnd", animationEndCallback, false);
					element.addEventListener("mozAnimationEnd", animationEndCallback, false);
					element.addEventListener("oAnimationEnd", animationEndCallback, false);
					element.addEventListener("msAnimationEnd", animationEndCallback, false);
					// add transition classes
					transitionClass.split(" ").forEach(function (currentClass) {
						currentClass = currentClass.trim();
						if (currentClass.length > 0) {
							elementClassList.add(currentClass);

							if (overlayClassList) {
								overlayClassList.add(currentClass);
							}

						}
					});
				} else {
					if (!ns.getConfig("noAsync", false)) {
						window.setTimeout(deferred.resolve, 0);
					} else {
						deferred.resolve();
					}
				}
				return deferred;
			};

			/**
			 * Destroy popup
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._destroy = function () {
				var self = this,
					element = self.element,
					ui = self._ui,
					wrapper = ui.wrapper,
					child;

				if (wrapper) {
					// restore all children from wrapper
					child = wrapper.firstChild;
					while (child) {
						element.appendChild(child);
						child = wrapper.firstChild;
					}

					if (wrapper.parentNode) {
						wrapper.parentNode.removeChild(wrapper);
					}
				}

				self._unbindEvents(element);
				self._setOverlay(element, false);

				ui.wrapper = null;
			};

			Popup.prototype = prototype;

			ns.widget.core.Popup = Popup;

			engine.defineWidget(
				"Popup",
				POPUP_SELECTOR,
				[
					"open",
					"close",
					"reposition"
				],
				Popup,
				"core"
			);
			}());

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */

/**
 * # Popup Widget
 * Shows a pop-up window.
 *
 * The popup widget shows in the middle of the screen a list of items in a pop-up window.
 * It automatically optimizes the pop-up window size within the screen. The following table
 * describes the supported popup classes.
 *
 * ## Default selectors
 * All elements with class *ui-popup* will be become popup widgets.
 *
 * The pop-up window can contain a header, content, and footer area like the page element.
 *
 * To open a pop-up window from a link, use the data-rel attribute in HTML markup as in the
 * following code:
 *
 *      @example
 *      <a href="#popup" class="ui-btn" data-rel="popup">Open popup when clicking this element.</a>
 *
 * The following table shows examples of various types of popups.
 *
 * The popup contains header, content and footer area
 *
 * ###HTML Examples
 *
 * #### Basic popup with header, content, footer
 *
 *        @example
 *        <div class="ui-page">
 *            <div class="ui-popup">
 *                <div class="ui-popup-header">Power saving mode</div>
 *                <div class="ui-popup-content">
 *                    Turning on Power
 *                    saving mode will
 *                    limit the maximum
 *                    per
 *                </div>
 *                <div class="ui-popup-footer">
 *                    <button id="cancel" class="ui-btn">Cancel</button>
 *                </div>
 *            </div>
 *        </div>
 *
 * #### Popup with 2 buttons in the footer
 *
 *      @example
 *         <div id="2btnPopup" class="ui-popup">
 *             <div class="ui-popup-header">Delete</div>
 *             <div class="ui-popup-content">
 *                 Delete the image?
 *             </div>
 *             <div class="ui-popup-footer ui-grid-col-2">
 *                 <button id="2btnPopup-cancel" class="ui-btn">Cancel</button>
 *                 <button id="2btnPopup-ok" class="ui-btn">OK</button>
 *             </div>
 *         </div>
 *
 * #### Popup with checkbox/radio
 *
 * If you want make popup with list checkbox(or radio) just include checkbox (radio) to popup and
 * add class *ui-popup-checkbox-label* to popup element.
 *
 *        @example
 *         <div id="listBoxPopup" class="ui-popup">
 *             <div class="ui-popup-header">When?</div>
 *             <div class="ui-popup-content" style="height:243px; overflow-y:scroll">
 *                 <ul class="ui-listview">
 *                     <li>
 *                         <label for="check-1" class="ui-popup-checkbox-label">Yesterday</label>
 *                         <input type="checkbox" name="checkSet" id="check-1" />
 *                     </li>
 *                     <li>
 *                         <label for="check-2" class="ui-popup-checkbox-label">Today</label>
 *                         <input type="checkbox" name="checkSet" id="check-2" />
 *                     </li>
 *                     <li>
 *                         <label for="check-3" class="ui-popup-checkbox-label">Tomorrow</label>
 *                         <input type="checkbox" name="checkSet" id="check-3" />
 *                     </li>
 *                 </ul>
 *                 <ul class="ui-listview">
 *                     <li>
 *                         <label for="radio-1" class="ui-popup-radio-label">Mandatory</label>
 *                         <input type="radio" name="radioSet" id="radio-1" />
 *                     </li>
 *                     <li>
 *                         <label for="radio-2" class="ui-popup-radio-label">Optional</label>
 *                         <input type="radio" name="radioSet" id="radio-2" />
 *                     </li>
 *                 </ul>
 *             </div>
 *             <div class="ui-popup-footer">
 *                 <button id="listBoxPopup-close" class="ui-btn">Close</button>
 *             </div>
 *         </div>
 *     </div>
 *
 * #### Popup with no header and footer
 *
 *      @example
 *         <div id="listNoTitleNoBtnPopup" class="ui-popup">
 *             <div class="ui-popup-content" style="height:294px; overflow-y:scroll">
 *                 <ul class="ui-listview">
 *                     <li><a href="">Ringtones 1</a></li>
 *                     <li><a href="">Ringtones 2</a></li>
 *                     <li><a href="">Ringtones 3</a></li>
 *                 </ul>
 *             </div>
 *         </div>
 *
 * #### Toast popup
 *
 *      @example
 *         <div id="PopupToast" class="ui-popup ui-popup-toast">
 *             <div class="ui-popup-content">Saving contacts to sim on Samsung</div>
 *         </div>
 *
 * ### Create Option popup
 *
 * Popup inherits value of option positionTo from property data-position-to set in link.
 *
 *        @example
 *        <!--definition of link, which opens popup and sets its position-->
 *        <a href="#popupOptionText" data-rel="popup"  data-position-to="origin">Text</a>
 *        <!--definition of popup, which inherits property position from link-->
 *        <div id="popupOptionText" class="ui-popup">
 *            <div class="ui-popup-content">
 *                <ul class="ui-listview">
 *                <li><a href="#">Option 1</a></li>
 *                <li><a href="#">Option 2</a></li>
 *                <li><a href="#">Option 3</a></li>
 *                <li><a href="#">Option 4</a></li>
 *                </ul>
 *            </div>
 *        </div>
 *
 * ### Opening and closing popup
 *
 * To open popup from "a" link using html markup, use the following code:
 *
 *        @example
 *      <div class="ui-page">
 *          <header class="ui-header">
 *              <h2 class="ui-title">Call menu</h2>
 *          </header>
 *          <div class="ui-content">
 *              <a href="#popup" class="ui-btn" data-rel="popup" >Open Popup</a>
 *          </div>
 *
 *          <div id="popup" class="ui-popup">
 *               <div class="ui-popup-header">Power saving mode</div>
 *                   <div class="ui-popup-content">
 *                       Turning on Power
 *                       saving mode will
 *                       limit the maximum
 *                       per
 *                   </div>
 *               <div class="ui-popup-footer">
 *               <button id="cancel" class="ui-btn">Cancel</button>
 *           </div>
 *       </div>
 *
 *  To open the popup widget from JavaScript use method *tau.openPopup(to)*
 *
 *          @example
 *          tau.openPopup("popup")
 *
 *  To close the popup widget from JavaScript use method *tau.openPopup(to)*
 *
 *          @example
 *          tau.closePopup("popup")
 *
 * To find the currently active popup, use the ui-popup-active class.
 *
 * To bind the popup to a button, use the following code:
 *
 *      @example
 *         <!--HTML code-->
 *         <div id="1btnPopup" class="ui-popup">
 *             <div class="ui-popup-header">Power saving mode</div>
 *             <div class="ui-popup-content">
 *             </div>
 *             <div class="ui-popup-footer">
 *                 <button id="1btnPopup-cancel" class="ui-btn">Cancel</button>
 *             </div>
 *         </div>
 *         <script>
 *             // Popup opens with button click
 *             var button = document.getElementById("button");
 *             button.addEventListener("click", function() {
 *                 tau.openPopup("#1btnPopup");
 *             });
 *
 *             // Popup closes with Cancel button click
 *             document.getElementById("1btnPopup-cancel").addEventListener("click", function() {
 *                 tau.closePopup();
 *             });
 *         </script>
 *
 * ## Manual constructor
 * For manual creation of popup widget you can use constructor of widget from **tau** namespace:
 *
 *        @example
 *        var popupElement = document.getElementById("popup"),
 *            popup = tau.widget.popup(buttonElement);
 *
 * Constructor has one require parameter **element** which are base **HTMLElement** to create
 * widget. We recommend get this element by method *document.getElementById*.
 *
 * ## Options for Popup Widget
 *
 * Options for widget can be defined as _data-..._ attributes or give as parameter in constructor.
 *
 * You can change option for widget using method **option**.
 *
 * ## Methods
 *
 * To call method on widget you can use tau API:
 *
 *        @example
 *        var popupElement = document.getElementById("popup"),
 *            popup = tau.widget.popup(buttonElement);
 *
 *        popup.methodName(methodArgument1, methodArgument2, ...);
 *
 * ## Transitions
 *
 * By default, the framework doesn't apply transition. To set a custom transition effect, add the
 * data-transition attribute to the link.
 *
 *        @example
 *        <a href="index.html" data-rel="popup" data-transition="slideup">I will slide up</a>
 *
 * Global configuration:
 *
 *        @example
 *        gear.ui.defaults.popupTransition = "slideup";
 *
 * ### Transitions list
 *
 * - **none** Default value, no transition.
 * - **slideup** Makes the content of the pop-up slide up.
 *
 * ## Handling Popup Events
 *
 * To use popup events, use the following code:
 *
 *      @example
 *         <!--Popup html code-->
 *         <div id="popup" class="ui-popup">
 *             <div class="ui-popup-header"></div>
 *             <div class="ui-popup-content"></div>
 *         </div>
 *         </div>
 *         <script>
 *             // Use popup events
 *             var popup = document.getElementById("popup");
 *             popup.addEventListener("popupbeforecreate", function() {
 *                 // Implement code for popupbeforecreate event
 *             });
 *         </script>
 *
 * Full list of available events is in [events list section](#events-list).
 *
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @class ns.widget.core.ContextPopup
 * @extends ns.widget.core.Popup
 * @component-selector [data-role="popup"], .ui-popup
 */
(function (window, document, ns) {
	"use strict";
	
			var Popup = ns.widget.core.Popup,

				PopupPrototype = Popup.prototype,

				engine = ns.engine,

				objectUtils = ns.util.object,

				domUtils = ns.util.DOM,

				/**
				 * Object with default options
				 * @property {Object} defaults
				 * @property {string} [options.transition="none"] Sets the default transition for the popup.
				 * @property {string} [options.positionTo="window"] Sets the element relative to which the
				 * popup will be centered.
				 * @property {boolean} [options.dismissible=true] Sets whether to close popup when a popup
				 * is open to support the back button.
				 * @property {boolean} [options.overlay=true] Sets whether to show overlay when a popup is
				 * open.
				 * @property {string} [overlayClass=""] Sets the custom class for the popup background,
				 * which covers the entire window.
				 * @property {boolean} [options.history=true] Sets whether to alter the url when a popup
				 * is open to support the back button.
				 * @property {string} [options.arrow="l,t,r,b"] Sets directions of popup's arrow by
				 * priority ("l" for left, "t" for top,
				 * "r" for right, and "b" for bottom). The first one has the highest priority, the last one
				 * - the lowest. If you set arrow="t",
				 * then arrow will be placed at the top of popup container and the whole popup will be
				 * placed under clicked element.
				 * @property {string} [options.positionTo="window"] Sets the element relative to which
				 * the popup will be centered.
				 * @property {number} [options.distance=0] Sets the extra distance in px from clicked
				 * element.
				 * @property {HTMLElement|string} [options.link=null] Set the element or its id, under
				 * which popup should be placed.
				 * It only works with option positionTo="origin".
				 * @member ns.widget.core.ContextPopup
				 * @static
				 * @private
				 */
				defaults = {
					arrow: "l,b,r,t",
					positionTo: "window",
					positionOriginCenter: false,
					distance: 0,
					link: null
				},

				ContextPopup = function () {
					var self = this,
						ui;

					Popup.call(self);

					// set options
					self.options = objectUtils.merge(self.options, defaults);

					// set ui
					ui = self._ui || {};
					ui.arrow = null;
					self._ui = ui;
				},

				/**
				 * @property {Object} classes Dictionary for popup related css class names
				 * @member ns.widget.core.Popup
				 * @static
				 */
				CLASSES_PREFIX = "ui-popup",
				classes = objectUtils.merge({}, Popup.classes, {
					context: "ui-ctxpopup",
					contextOverlay: "ui-ctxpopup-overlay",
					arrow: "ui-arrow",
					arrowDir: CLASSES_PREFIX + "-arrow-"
				}),

				/**
				 * @property {Object} events Dictionary for popup related events
				 * @member ns.widget.core.Popup
				 * @static
				 */
				/* eslint-disable camelcase */
				// we can't change this in this moment because this is part of API
				events = objectUtils.merge({}, Popup.events, {
					before_position: "beforeposition"
				}),
				/* eslint-enable camelcase */

				positionTypes = {
					WINDOW: "window",
					ORIGIN: "origin",
					ABSOLUTE: "absolute"
				},

				prototype = new Popup();

			ContextPopup.defaults = objectUtils.merge({}, Popup.defaults, defaults);
			ContextPopup.classes = classes;
			ContextPopup.events = events;
			ContextPopup.positionTypes = positionTypes;

			/**
			 * Build structure of Popup widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.Popup
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					arrow;

				// build elements of popup
				PopupPrototype._build.call(self, element);

				// set class for element
				element.classList.add(classes.popup);

				// create arrow
				arrow = document.createElement("div");
				arrow.appendChild(document.createElement("span"));
				arrow.classList.add(classes.arrow);
				ui.arrow = arrow;

				// add arrow to popup element
				element.appendChild(arrow);

				return element;
			};

			/**
			 * Init widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui;

				PopupPrototype._init.call(this, element);

				ui.arrow = ui.arrow || element.querySelector("." + classes.arrow);
			};

			/**
			 * Layouting popup structure
			 * @method layout
			 * @member ns.widget.core.ContextPopup
			 * @param {HTMLElement} element
			 */
			prototype._layout = function (element) {
				var self = this;

				this._reposition();
				PopupPrototype._layout.call(self, element);
			};

			/**
			 * Set position and size of popup.
			 * @method _reposition
			 * @param {Object} options
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._reposition = function (options) {
				var self = this,
					element = self.element,
					ui = self._ui,
					elementClassList = element.classList;

				options = objectUtils.merge({}, self.options, options);

				self.trigger(events.before_position, null, false);

				elementClassList.add(classes.build);

				// set height of content
				self._setContentHeight();

				// set class for contextpopup
				if ((options.positionTo === "origin") && ui.overlay) {
					ui.overlay.classList.add(classes.contextOverlay);
				}

				// set position of popup
				self._placementCoords(options);

				elementClassList.remove(classes.build);

			};

			/**
			 * Find the best position of context popup.
			 * @method findBestPosition
			 * @param {ns.widget.core.ContextPopup} self
			 * @param {HTMLElement} clickedElement
			 * @private
			 * @member ns.widget.core.ContextPopup
			 */
			function findBestPosition(self, clickedElement) {
				var options = self.options,
					arrowsPriority = options.arrow.split(","),
					element = self.element,
					windowWidth = window.innerWidth,
					windowHeight = window.innerHeight,
					popupWidth = domUtils.getElementWidth(element, "outer"),
					popupHeight = domUtils.getElementHeight(element, "outer"),
					// offset coordinates of clicked element
					clickElementRect = clickedElement.getBoundingClientRect(),
					clickElementOffsetX = clickElementRect.left,
					clickElementOffsetY = clickElementRect.top,
					// width of visible part of clicked element
					clickElementOffsetWidth = Math.min(clickElementRect.width,
						windowWidth - clickElementOffsetX),
					// height of visible part of clicked element
					clickElementOffsetHeight = Math.min(clickElementRect.height,
						windowHeight - clickElementOffsetY),
					// params for all types of popup
					// "l" - popup with arrow on the left side, "r" - right, "b" - bottom, "t" - top
					// dir - this letter is added as a suffix of class to popup's element
					// fixedPositionField - specifies which coordinate is changed for this type of popup
					// fixedPositionFactor - factor, which specifies if size should be added or subtracted
					// size - available size, which is needed for this type of popup (width or height)
					// max - maximum size of available place
					params = {
						"l": {
							dir: "l", fixedPositionField: "x", fixedPositionFactor: 1,
							size: popupWidth, max: clickElementOffsetX
						},
						"r": {
							dir: "r", fixedPositionField: "x", fixedPositionFactor: -1,
							size: popupWidth, max: windowWidth - clickElementOffsetX - clickElementOffsetWidth
						},
						"b": {
							dir: "b", fixedPositionField: "y", fixedPositionFactor: -1,
							size: popupHeight, max: clickElementOffsetY
						},
						"t": {
							dir: "t", fixedPositionField: "y", fixedPositionFactor: 1,
							size: popupHeight, max: windowHeight - clickElementOffsetY - clickElementOffsetHeight
						}
					},
					bestDirection,
					direction,
					bestOffsetInfo;

				// set value of bestDirection on the first possible type or top
				bestDirection = params[arrowsPriority[0]] || params.t;

				arrowsPriority.forEach(function (key) {
					var param = params[key],
						paramMax = param.max;

					if (!direction) {
						if (param.size < paramMax) {
							direction = param;
						} else if (paramMax > bestDirection.max) {
							bestDirection = param;
						}
					}
				});

				if (!direction) {
					direction = bestDirection;
					if (direction.fixedPositionField === "x") {
						popupWidth = direction.max;
					} else {
						popupHeight = direction.max;
					}
				}

				// info about the best position without taking into account type of popup
				bestOffsetInfo = {
					x: clickElementOffsetX + clickElementOffsetWidth / 2 - popupWidth / 2,
					y: clickElementOffsetY + clickElementOffsetHeight / 2 - popupHeight / 2,
					w: popupWidth,
					h: popupHeight,
					dir: direction.dir
				};

				// check type of popup and correct value for "fixedPositionField" coordinate
				bestOffsetInfo[direction.fixedPositionField] +=
					(direction.fixedPositionField === "x" ?
						(popupWidth + clickElementOffsetWidth) * direction.fixedPositionFactor :
						(popupHeight + clickElementOffsetHeight) * direction.fixedPositionFactor) / 2 +
					options.distance * direction.fixedPositionFactor;

				// fix min/max position
				bestOffsetInfo.x = bestOffsetInfo.x < 0 ? 0 : bestOffsetInfo.x + bestOffsetInfo.w > windowWidth ? windowWidth - bestOffsetInfo.w : bestOffsetInfo.x;
				bestOffsetInfo.y = bestOffsetInfo.y < 0 ? 0 : bestOffsetInfo.y + bestOffsetInfo.h > windowHeight ? windowHeight - bestOffsetInfo.h : bestOffsetInfo.y;

				return bestOffsetInfo;
			}

			/**
			 * Find the best position of arrow.
			 * @method adjustedPositionAndPlacementArrow
			 * @param {ns.widget.core.ContextPopup} self
			 * @param {Object} bestRectangle
			 * @param {number} x
			 * @param {number} y
			 * @private
			 * @member ns.widget.core.ContextPopup
			 */
			function adjustedPositionAndPlacementArrow(self, bestRectangle, x, y) {
				var ui = self._ui,
					wrapper = ui.wrapper,
					arrow = ui.arrow,
					popupElement = self.element,
					arrowStyle = arrow.style,
					windowWidth = window.innerWidth,
					windowHeight = window.innerHeight,
					wrapperRect = wrapper.getBoundingClientRect(),
					arrowHalfWidth = arrow.offsetWidth / 2,
					popupProperties = {
						"padding-top": 0,
						"padding-bottom": 0,
						"padding-left": 0,
						"padding-right": 0,
						"border-top-width": 0,
						"border-left-width": 0,
						"box-sizing": null
					},
					wrapperProperties = {
						"margin-top": 0,
						"margin-bottom": 0,
						"margin-left": 0,
						"margin-right": 0,
						"padding-top": 0,
						"padding-bottom": 0,
						"padding-left": 0,
						"padding-right": 0
					},
					margins,
					params = {
						"t": {pos: x, min: "left", max: "right", posField: "x", valField: "w", styleField: "left"},
						"b": {pos: x, min: "left", max: "right", posField: "x", valField: "w", styleField: "left"},
						"l": {pos: y, min: "top", max: "bottom", posField: "y", valField: "h", styleField: "top"},
						"r": {pos: y, min: "top", max: "bottom", posField: "y", valField: "h", styleField: "top"}
					},
					param = params[bestRectangle.dir],
					surplus,
					addPadding;

				domUtils.extractCSSProperties(popupElement, popupProperties);
				domUtils.extractCSSProperties(wrapper, wrapperProperties);
				addPadding = popupProperties["box-sizing"] === "border-box";
				margins = {
					"t": popupProperties["padding-top"] + wrapperProperties["margin-top"] + wrapperProperties["padding-top"],
					"b": popupProperties["padding-bottom"] + wrapperProperties["margin-bottom"] + wrapperProperties["padding-bottom"],
					"l": popupProperties["padding-left"] + wrapperProperties["margin-left"] + wrapperProperties["padding-left"],
					"r": popupProperties["padding-right"] + wrapperProperties["margin-right"] + wrapperProperties["padding-right"]
				};

				// value of coordinates of proper edge of wrapper
				wrapperRect = {
					// x-coordinate of left edge
					left: margins.l + bestRectangle.x,
					// x-coordinate of right edge
					right: margins.l + wrapperRect.width + bestRectangle.x,
					// y-coordinate of top edge
					top: margins.t + bestRectangle.y,
					// y-coordinate of bottom edge
					bottom: wrapperRect.height + margins.t + bestRectangle.y
				};

				if (wrapperRect[param.min] > param.pos - arrowHalfWidth) {
					surplus = bestRectangle[param.posField];
					if (surplus > 0) {
						bestRectangle[param.posField] = Math.max(param.pos - arrowHalfWidth, 0);
						param.pos = bestRectangle[param.posField] + arrowHalfWidth;
					} else {
						param.pos = wrapperRect[param.min] + arrowHalfWidth;
					}
				} else if (wrapperRect[param.max] < param.pos + arrowHalfWidth) {
					surplus = (param.valField === "w" ? windowWidth : windowHeight) -
						(bestRectangle[param.posField] + bestRectangle[param.valField]);
					if (surplus > 0) {
						bestRectangle[param.posField] += Math.min(surplus, (param.pos + arrowHalfWidth) - wrapperRect[param.max]);
						param.pos = bestRectangle[param.posField] + bestRectangle[param.valField] - arrowHalfWidth;
					} else {
						param.pos = wrapperRect[param.max] - arrowHalfWidth;
					}
				}

				arrowStyle[param.styleField] = (param.pos - arrowHalfWidth - bestRectangle[param.posField] - (addPadding ? popupProperties["border-" + param.styleField + "-width"] : 0)) + "px";

				return bestRectangle;
			}

			/**
			 * Set top, left and margin for popup's container.
			 * @method _placementCoordsWindow
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._placementCoordsWindow = function (element) {
				var elementStyle = element.style,
					elementWidth = element.offsetWidth,
					elementHeight = element.offsetHeight,
					elementMarginTop = domUtils.getCSSProperty(element, "margin-top", 0, "float"),
					elementMarginBottom = domUtils.getCSSProperty(element, "margin-bottom", 0, "float"),
					elementTop = window.innerHeight - elementHeight - elementMarginTop - elementMarginBottom;

				elementStyle.top = elementTop + "px";
				elementStyle.left = "50%";
				elementStyle.marginLeft = -(elementWidth / 2) + "px";
			};

			/**
			 * Set top, left and margin for popup's container.
			 * @method _placementCoordsAbsolute
			 * @param {HTMLElement} element
			 * @param {number} x
			 * @param {number} y
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._placementCoordsAbsolute = function (element, x, y) {
				var elementStyle = element.style,
					elementWidth = element.offsetWidth,
					elementHeight = element.offsetHeight;

				elementStyle.top = y + "px";
				elementStyle.left = x + "px";
				elementStyle.marginTop = -(elementHeight / 2) + "px";
				elementStyle.marginLeft = -(elementWidth / 2) + "px";
			};

			/**
			 * Find clicked element.
			 * @method _findClickedElement
			 * @param {number} x
			 * @param {number} y
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._findClickedElement = function (x, y) {
				return document.elementFromPoint(x, y);
			};

			/**
			 * Emulate position of event for clicked element.
			 * @method emulatePositionOfClick
			 * @param {string} bestDirection direction of arrow
			 * @param {HTMLElement} clickedElement
			 * @private
			 * @member ns.widget.core.ContextPopup
			 */
			function emulatePositionOfClick(bestDirection, clickedElement) {
				var clickedElementRect = clickedElement.getBoundingClientRect(),
					position = {};

				switch (bestDirection) {
					case "l":
						// the arrow will be on the left edge of container, so x-coordinate
						// should have value equals to the position of right edge of clicked element
						position.x = clickedElementRect.right;
						// y-coordinate should have value equals to the position of top edge of clicked
						// element plus half of its height
						position.y = clickedElementRect.top + clickedElementRect.height / 2;
						break;
					case "r":
						// the arrow will be on the right edge of container
						position.x = clickedElementRect.left;
						position.y = clickedElementRect.top + clickedElementRect.height / 2;
						break;
					case "t":
						// the arrow will be on the top edge of container
						position.x = clickedElementRect.left + clickedElementRect.width / 2;
						position.y = clickedElementRect.bottom;
						break;
					case "b":
						// the arrow will be on the bottom edge of container
						position.x = clickedElementRect.left + clickedElementRect.width / 2;
						position.y = clickedElementRect.top;
						break;
				}
				return position;
			}

			prototype._placementCoordsOrigin = function (clickedElement, options) {
				var self = this,
					element = self.element,
					elementStyle = element.style,
					elementClassList = element.classList,
					x = options.x,
					y = options.y,
					bestRectangle,
					emulatedPosition,
					arrowType,
					elementHeight;

				elementClassList.add(classes.context);

				elementHeight = element.offsetHeight;
				bestRectangle = findBestPosition(self, clickedElement);

				arrowType = bestRectangle.dir;
				elementClassList.add(classes.arrowDir + arrowType);
				self._ui.arrow.setAttribute("type", arrowType);

				if ((typeof x !== "number" && typeof y !== "number") || self.options.positionOriginCenter) {
					// if we found element, which was clicked, but the coordinates of event
					// was not available, we have to count these coordinates to the center of proper edge of element.
					emulatedPosition = emulatePositionOfClick(arrowType, clickedElement);
					x = emulatedPosition.x;
					y = emulatedPosition.y;
				}
				bestRectangle = adjustedPositionAndPlacementArrow(self, bestRectangle, x, y);

				if (elementHeight > bestRectangle.h) {
					self._setContentHeight(bestRectangle.h);
				}

				elementStyle.left = bestRectangle.x + "px";
				elementStyle.top = bestRectangle.y + "px";
			};

			prototype._placementCoordsElement = function (clickedElement) {
				var self = this,
					element = self.element,
					elementStyle = element.style,
					bestRectangle,
					elementHeight;

				element.classList.add(classes.context);

				elementHeight = element.offsetHeight;
				bestRectangle = findBestPosition(self, clickedElement);

				if (elementHeight > bestRectangle.h) {
					self._setContentHeight(bestRectangle.h);
				}

				elementStyle.left = bestRectangle.x + "px";
				elementStyle.top = bestRectangle.y + "px";
			};

			/**
			 * Find and set the best position for popup.
			 * @method _placementCoords
			 * @param {Object} options
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._placementCoords = function (options) {
				var self = this,
					positionTo = options.positionTo,
					x = options.x,
					y = options.y,
					element = self.element,
					clickedElement,
					link;

				switch (positionTo) {
					case positionTypes.ORIGIN:
						// if we know x-coord and y-coord, we open the popup with arrow
						link = options.link;
						if (link) {
							if (typeof link === "string") {
								clickedElement = document.getElementById(link);
							} else if (typeof link === "object") {
								clickedElement = link;
							}
						} else if (typeof x === "number" && typeof y === "number") {
							clickedElement = self._findClickedElement(x, y);
						}
						if (clickedElement) {
							self._placementCoordsOrigin(clickedElement, options);
							return;
						}
						break;
					case positionTypes.WINDOW:
						self._placementCoordsWindow(element);
						return;
					case positionTypes.ABSOLUTE:
						if (typeof x === "number" && typeof y === "number") {
							self._placementCoordsAbsolute(element, x, y);
							return;
						}
						break;
					default:
						// there is possible, that element or its id was given
						if (typeof positionTo === "string") {
							try {
								clickedElement = document.querySelector(options.positionTo);
							} catch (e) {
							}
						} else if (typeof positionTo === "object") {
							clickedElement = positionTo;
						}
						if (clickedElement) {
							self._placementCoordsElement(clickedElement, options);
							return;
						}
						break;
				}

				// if there was problem with setting position of popup, we set its position to window
				self._placementCoordsWindow(element);
			};

			/**
			 * Set height for popup's container.
			 * @method _setContentHeight
			 * @param {number} maxHeight
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._setContentHeight = function (maxHeight) {
				var self = this,
					element = self.element,
					content = self._ui.content,
					contentStyle,
					contentHeight,
					elementOffsetHeight;

				if (content) {
					contentStyle = content.style;

					if (contentStyle.height || contentStyle.minHeight) {
						contentStyle.height = "";
						contentStyle.minHeight = "";
					}

					maxHeight = maxHeight || window.innerHeight;

					contentHeight = content.offsetHeight;
					elementOffsetHeight = element.offsetHeight;

					if (elementOffsetHeight > maxHeight) {
						contentHeight -= (elementOffsetHeight - maxHeight);
						contentStyle.height = contentHeight + "px";
						contentStyle.minHeight = contentHeight + "px";
					}
				}
			};

			/**
			 * Hide popup.
			 * @method _onHide
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._onHide = function () {
				var self = this,
					ui = self._ui,
					element = self.element,
					elementClassList = element.classList,
					arrow = ui.arrow;

				elementClassList.remove(classes.context);
				["l", "r", "b", "t"].forEach(function (key) {
					elementClassList.remove(classes.arrowDir + key);
				});

				// we remove styles for element, which are changed
				// styles for container, header and footer are left unchanged
				if (element) {
					element.removeAttribute("style");
				}
				if (arrow) {
					arrow.removeAttribute("style");
				}

				PopupPrototype._onHide.call(self);
			};

			/**
			 * Destroy popup.
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self._ui,
					arrow = ui.arrow;

				PopupPrototype._destroy.call(self);

				if (arrow && arrow.parentNode) {
					arrow.parentNode.removeChild(arrow);
				}

				ui.arrow = null;
			};

			/**
			 * Set new position for popup.
			 * @method reposition
			 * @param {Object} options
			 * @param {number} options.x
			 * @param {number} options.y
			 * @param {string} options.positionTo
			 * @member ns.widget.core.ContextPopup
			 */
			prototype.reposition = function (options) {
				if (this._isActive()) {
					this._reposition(options);
				}
			};

			/**
			 * Refresh structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.ContextPopup
			 */
			prototype._refresh = function () {
				if (this._isActive()) {
					PopupPrototype._refresh.call(this);
					this.reposition(this.options);
				}
			};

			ContextPopup.prototype = prototype;
			ns.widget.core.ContextPopup = ContextPopup;

			engine.defineWidget(
				"Popup",
				"[data-role='popup'], .ui-popup",
				[
					"open",
					"close",
					"reposition"
				],
				ContextPopup,
				"core",
				true
			);

			// @remove
			// THIS IS ONLY FOR COMPATIBILITY
			ns.widget.popup = ns.widget.Popup;

			}(window, window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Listview Widget
 * Shows a list view.
 *
 * The list widget is used to display, for example, navigation data, results, and data entries. The following table describes the supported list classes.
 *
 * ## Default selectors
 *
 * Default selector for listview widget is class *ui-listview*.
 *
 * To add a list widget to the application, use the following code:
 *
 * ### List with basic items
 *
 * You can add a basic list widget as follows:
 *
 *      @example template
 *         <ul class="ui-listview">
 *             <li><span>List Item</span></li>
 *         </ul>
 *
 * ### List with link items
 *
 * You can add a list widget with a link and press effect that allows the user to click each list item as follows:
 *
 *      @example tau-listview-with-link
 *         <ul class="ui-listview">
 *             <li>
 *                 <a href="#">List Item</a>
 *             </li>
 *         </ul>
 *
 * ## JavaScript API
 *
 * Listview widget hasn't JavaScript API.
 *
 * @class ns.widget.core.Listview
 * @component-selector .ui-listview
 * @components-constraint 'listitem'
 * @component-type container-component
 * @extends ns.widget.BaseWidget
 */
/**
 * Listview with gradient background
 * @style ui-colored-list
 * @member ns.widget.core.Listview
 * @mobile
 */
/**
 * Enable snap list style
 * @style ui-snap-listview
 * @member ns.widget.core.Listview
 * @wearable
 */
/**
 *
 * @style ui-snap-listview
 * @member ns.widget.core.Listview
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				Listview = function () {
				},
				classes = {
					LISTVIEW: "ui-listview",
					DETAILS: "ui-details"
				},
				prototype = new BaseWidget();

			Listview.classes = classes;

			/**
			 * build Listview
			 * @method _build
			 * @private
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.core.Listview
			 */
			prototype._build = function (element) {
				element.classList.add(classes.LISTVIEW);
				return element;
			};

			Listview.prototype = prototype;
			ns.widget.core.Listview = Listview;

			engine.defineWidget(
				"Listview",
				"[data-role='listview'], .ui-listview",
				[],
				Listview,
				"core"
			);
			}(window.document, ns));

/**
 * # List Item
 *
 * You can add a basic list item as follows:
 *
 *      @example template
 *         <li><span>List Item</span></li>
 *
 *
 * @class ns.widget.core.ListItem
 * @component-selector .ui-listview li
 * @components-constraint 'text', 'image', 'checkbox', 'button', 'radio', 'toggleswitch', 'processing'
 * @component-type container-component
 * @extends ns.widget.BaseWidget
 */
/**
 *
 * @style ui-li-anchor
 * @member ns.widget.core.ListItem
 */
/**
 * Subtext for item
 * @style li-text-sub
 * @selector  > *
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Subtext for item
 * @style ui-li-sub-text
 * @selector  > *
 * @member ns.widget.core.ListItem
 * @wearable
 */
/**
 * Item with button on the right
 * @style li-has-right-btn
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Item with circular button on the right
 * @style li-has-right-circle-btn
 * @selector
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Item with thumbnail on the left
 * @style li-has-thumb
 * @selector
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Thumbnail inside item
 * @style li-thumb
 * @selector .li-has-thumb > *
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Item with checkbox
 * @style li-has-checkbox
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Item with radio button
 * @style li-has-radio
 * @selector
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Item with progressbar
 * @style li-has-progress
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Item with multiline
 * @style li-has-multiline
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Second subtext for multiline item positioned under the first one
 * @style ui-text-sub2
 * @selector .li-has-multiline > *
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Subtext for multiline item positioned on the right
 * @style ui-text-sub3
 * @selector .li-has-multiline > *
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 * Item with 2 lines
 * @style li-has-2line
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Item with 3 lines
 * @style li-has-3-lines
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 *
 * @style li-icon-sub
 * @selector  .li-text-sub > *
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 *
 * @style li-icon-sub
 * @selector .li-text-sub3 > *
 * @member ns.widget.core.ListItem
 * @mobile
 */
/**
 *
 * @style ui-li-static
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Expandable item
 * @style ui-expandable
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * Divider item
 * @style ui-group-index
 * @mobile
 * @member ns.widget.core.ListItem
 */
/**
 * List divider
 * @style ui-listview-divider
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Marquee item
 * @style ui-marquee
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Marquee item with blurry effect
 * @style ui-marquee-gradient
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Item with action icon
 * @style ui-li-has-action-icon
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Text for action item
 * @style ui-action-text
 * @selector .ui-li-has-action-icon > *
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Setting icon for action item
 * @style ui-action-setting
 * @selector .ui-li-has-action-icon > *
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Delete icon for action item
 * @style ui-action-delete
 * @selector .ui-li-has-action-icon > *
 * @wearable
 * @member ns.widget.core.ListItem
 */
/**
 * Adding icon for action item
 * @style ui-action-add
 * @selector .ui-li-has-action-icon > *
 * @wearable
 * @member ns.widget.core.ListItem
 */
;
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* global define, ns */
/**
 * #Button
 * Shows a control that can be used to generate an action event.
 *
 * The button component shows on the screen a control that you can use to generate an action event
 * when it is pressed and released. The component is coded with standard HTML anchor and input
 * elements.
 *
 * The following table describes the supported button classes.
 *
 * ## Default selectors
 * The button widget shows a control on the screen that you can use to generate an action event
 * when it is pressed and released.
 * This widget is coded with standard HTML anchor and input elements.
 *
 * Default selector for buttons is class *ui-btn*
 *
 * ### HTML Examples
 *
 * #### Standard button
 * To add a button widget to the application, use the following code:
 *
 *      @example
 *      <button type="button" class="ui-btn">Button</button>
 *      <a href="#" class="ui-btn">Button</a>
 *      <input type="button" class="ui-btn" value="Button" />
 *
 * #### Inline button
 *
 *      @example
 *      <input type="button" class="ui-btn ui-inline" value="Button" />
 *
 * #### Multiline text button
 *
 *      @example
 *      <a href="#" class="ui-btn ui-multiline ui-inline">A Button<br />Icon</a>
 *
 * ## Options
 *
 * ### Icons
 * Buttons can contains icons
 *
 * Creates an icon button in the header area is permitted but in content or footer area creating
 * icon are not supported.
 *
 * To use menu icon in header add class *ui-more* to the button element:
 *
 *      @example
 *      <button class="ui-btn ui-more ui-icon-overflow">More Options</button>
 *
 * Samsung Wearable Web UI Framework supports 3 icon css styles:
 *
 *  - ui-icon-detail
 *  - ui-icon-overflow
 *  - ui-icon-selectall
 *
 * ### Disabled
 *
 * If you want to make disabled button, add attribute *disabled* in button tag:
 *
 *      @example
 *      <button class="ui-btn" disabled="disabled">Button disabled</button>
 *
 * ### Inline
 *
 * If you want to make inline button, add class *ui-inline* to button element:
 *
 *      @example
 *      <button class="ui-btn ui-inline">Inline button</button>
 *
 * ### Multiline
 *
 * If you want to make multiline text button, add *ui-multiline* class
 *
 *      @example
 *      <button class="ui-btn ui-multiline">Multiline button</button>
 *
 * ### Color theme
 *
 * To optimize color support for the Samsung Wearable, the following styles below are supported:
 *
 * <table>
 *  <tr>
 *      <th>Class</th>
 *      <th>Default</th>
 *      <th>Press</th>
 *      <th>Disable</th>
 *  </tr>
 *  <tr>
 *      <td>ui-color-red</td>
 *      <td>#ce2302</td>
 *      <td>#dd654e</td>
 *      <td>#3d0a0a</td>
 *  </tr>
 *  <tr>
 *      <td>ui-color-orange</td>
 *      <td>#ed8600</td>
 *      <td>#f0aa56</td>
 *      <td>#462805</td>
 *  </tr>
 *  <tr>
 *      <td>ui-color-green</td>
 *      <td>#64a323</td>
 *      <td>#92be5e</td>
 *      <td>#1e3108</td>
 *  </tr>
 * </table>
 *
 * ### Button Group
 *
 * You can group buttons in columns or rows. The following table lists the supported button column
 * and row classes.
 *
 * <table>
 *  <tr>
 *      <th>Class</th>
 *      <th>Description</th>
 *  </tr>
 *  <tr>
 *      <td>ui-grid-col-1</td>
 *      <td>Defines the button column width as 100% of the screen.</td>
 *  </tr>
 *  <tr>
 *      <td>ui-grid-col-2</td>
 *      <td>Defines the button column width as 50% of the screen.</td>
 *  </tr>
 *  <tr>
 *      <td>ui-grid-col-3</td>
 *      <td>Defines the button column width as 33% of the screen.</td>
 *  </tr>
 *  <tr>
 *      <td>ui-grid-row</td>
 *      <td>Arranges the buttons in a row.</td>
 *  </tr>
 * </table>
 *
 * To implement the button groups, use the following code:
 *
 * #### For columns:
 *
 *      @example
 *      <div class="ui-grid-col-3" style="height:76px">
 *          <button type="button" class="ui-btn">Button Circle</button>
 *          <a href="#" class="ui-btn ui-color-red" >A Button Circle</a>
 *          <input type="button" class="ui-btn ui-color-orange" value="Input Button Circle" />
 *      </div>
 *
 * #### For rows:
 *
 *      @example
 *      <div class="ui-grid-row">
 *          <button type="button" class="ui-btn">Button Circle</button>
 *          <a href="#" class="ui-btn ui-color-red" >A Button Circle</a>
 *          <input type="button" class="ui-btn ui-color-orange" value="Input Button Circle" />
 *      </div>
 *
 * @since 2.0
 * @class ns.widget.core.Button
 * @component-selector button, [data-role="button"], .ui-btn, input[type="button"]
 * @component-type standalone-component
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				/**
				 * Create instance of widget
				 * @constructor
				 * @member ns.widget.core.Button
				 */
				utilDOM = ns.util.DOM,
				classes = {
					/**
					 * Standard button
					 * @style ui-btn
					 * @member ns.widget.core.Button
					 */
					BTN: "ui-btn",
					/**
					 * Disabled button
					 * @style ui-state-disabled
					 * @member ns.widget.core.Button
					 */
					DISABLED: "ui-state-disabled",
					INLINE: "ui-inline",
					BTN_ICON: "ui-btn-icon",
					ICON_PREFIX: "ui-icon-",
					BTN_CIRCLE: "ui-btn-circle",
					BTN_NOBG: "ui-btn-nobg",
					BTN_ICON_ONLY: "ui-btn-icon-only",
					BTN_TEXT_LIGHT: "ui-btn-text-light",
					BTN_TEXT_DARK: "ui-btn-text-dark",
					/**
					 * Change background color of button to red
					 * @style ui-color-red
					 * @preview <span style="background-color: red;">&nbsp;</span>
					 * @member ns.widget.core.Button
					 */
					/**
					 * Button for header
					 * @style ui-more
					 * @member ns.widget.core.Button
					 */
					/**
					 * Button more for header
					 * @style ui-icon-overflow
					 * @member ns.widget.core.Button
					 */
					/**
					 * Button details for header
					 * @style ui-icon-detail
					 * @member ns.widget.core.Button
					 */
					/**
					 * Button select all for header
					 * @style ui-icon-selectall
					 * @member ns.widget.core.Button
					 */
					/**
					 * Icon only style
					 * @style ui-btn-icon-only
					 * @member ns.widget.core.Button
					 */
					BTN_ICON_POSITION_PREFIX: "ui-btn-icon-",
					BTN_ICON_MIDDLE: "ui-btn-icon-middle"
				},
				Button = function () {
					var self = this;

					self.options = {};
					self._classesPrefix = classes.BTN + "-";
				},
				buttonStyle = {
					CIRCLE: "circle",
					TEXTLIGHT: "light",
					TEXTDARK: "dark",
					NOBG: "nobg",
					ICON_MIDDLE: "icon-middle"
				},

				prototype = new BaseWidget();

			Button.classes = classes;
			Button.prototype = prototype;

			/**
			 * Configure button
			 * @method _configure
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._configure = function () {
				/**
				 * Object with default options
				 * @property {Object} options
				 * @property {boolean} [options.inline=false] If is set true then button has inline style
				 * @property {?string} [options.icon=null] Set icon class name for button
				 * @property {boolean} [options.disabled=false] Disable button if is set to true
				 * @property {"left"|"right"|"button"|"top"} [options.iconpos="left"] Set icon position
				 * @member ns.widget.core.Button
				 * @static
				 */
				/**
				 * "circle" Make circle button
				 * "nobg" Make button without background
				 * @property {null|"circle"|"nobg"} [options.style=null] Set style of button
				 * @member ns.widget.core.Button
				 * @static
				 */
				this.options = {
					// common options
					inline: false, //url
					icon: null,
					disabled: false,
					// mobile options
					style: null,
					iconpos: "left",
					size: null,
					middle: false
				};
			};

			/**
			 * Set style option
			 * @method _setStyle
			 * @param {HTMLElement} element
			 * @param {string} style
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setStyle = function (element, style) {
				var options = this.options,
					buttonClassList = element.classList,
					change = false;

				style = style || options.style;

				switch (style) {
					case buttonStyle.CIRCLE:
						buttonClassList.remove(classes.BTN_NOBG);
						buttonClassList.add(classes.BTN_CIRCLE);
						change = true;
						break;
					case buttonStyle.NOBG:
						buttonClassList.remove(classes.BTN_CIRCLE);
						buttonClassList.add(classes.BTN_NOBG);
						change = true;
						break;
					case buttonStyle.TEXTLIGHT:
						buttonClassList.add(classes.BTN_TEXT_LIGHT);
						change = true;
						break;
					case buttonStyle.TEXTDARK:
						buttonClassList.add(classes.BTN_TEXT_DARK);
						change = true;
						break;
					default:
				}

				if (change) {
					options.style = style;
				}
			};

			/**
			 * Set inline option
			 * @method _setInline
			 * @param {HTMLElement} element
			 * @param {boolean} inline
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setInline = function (element, inline) {
				var options = this.options;

				inline = inline || options.inline;

				if (inline) {
					element.classList.add(classes.INLINE);
					options.inline = inline;
				}
			};

			/**
			 * Set icon option
			 * @method _setIcon
			 * @param {HTMLElement} element
			 * @param {string} icon
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setIcon = function (element, icon) {
				var self = this,
					classList = element.classList,
					options = self.options,
					styles = {},
					urlIcon,
					iconCSSRule = self._iconCSSRule;

				icon = icon || options.icon;
				options.icon = icon;

				if (icon) {
					classList.add(classes.BTN_ICON);
					if (icon.indexOf(".") === -1) {
						classList.add(classes.ICON_PREFIX + icon);
						self._setTitleForIcon(element);
						if (iconCSSRule) {
							utilDOM.removeCSSRule(iconCSSRule);
						}
					} else {
						// if icon is file path
						urlIcon = "url(\"" + icon + "\")";
						styles["-webkit-mask-image"] = urlIcon;
						styles["mask-image"] = urlIcon;
						self._iconCSSRule = utilDOM.setStylesForPseudoClass("#" + element.id, "after", styles);
					}
				} else {
					if (iconCSSRule) {
						utilDOM.removeCSSRule(iconCSSRule);
					}
				}
			};

			/**
			 * Set iconpos option
			 * @method _setIconpos
			 * @param {HTMLElement} element
			 * @param {string} iconpos
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setIconpos = function (element, iconpos) {
				var options = this.options,
					style = options.style,
					innerTextLength = element.textContent.length || (element.value ? element.value.length : 0);

				iconpos = iconpos || options.iconpos;

				if (options.icon && style !== buttonStyle.CIRCLE && style !== buttonStyle.NOBG) {
					if (innerTextLength > 0) {
						element.classList.add(classes.BTN_ICON_POSITION_PREFIX + iconpos);
					} else {
						element.classList.add(classes.BTN_ICON_ONLY);
					}
					options.iconpos = iconpos;
				}
			};

			/**
			 * Set title for button without showing text
			 * @method _setTitleForIcon
			 * @param {HTMLElement|HTMLInputElement|HTMLButtonElement} element
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setTitleForIcon = function (element) {
				var options = this.options,
					buttonText = element.textContent;

				// Add title to element if button not has text.
				if (options.iconpos === "notext" && !element.getAttribute("title")) {
					element.setAttribute("title", buttonText);
					ns.warn("iconpos='notext' is deprecated.");
				}
			};

			/**
			 * Sets button to disabled if element.disabled or element.disabled property is true,
			 * or class is set to ui-state-disabled
			 * @method _setDisabled
			 * @param {HTMLElement} element
			 * @param {boolean} state
			 * @protected
			 */
			prototype._setDisabled = function (element, state) {
				var self = this,
					options = self.options,
					buttonClassList = element.classList;

				if (state === true || options.disabled === true || element.disabled ||
					buttonClassList.contains(classes.DISABLED)) {
					options.disabled = true;
					self._disable(element);
				} else {
					options.disabled = false;
				}
			};
			/**
			 * Build Button
			 * @method _build
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.core.Button
			 */
			prototype._build = function (element) {
				var self = this,
					buttonClassList = element.classList;

				if (!buttonClassList.contains(classes.BTN)) {
					buttonClassList.add(classes.BTN);
				}

				self._setStyle(element);
				self._setInline(element);
				self._setIconpos(element);
				self._setIcon(element);
				self._setSize(element);
				self._setDisabled(element);

				return element;
			};

			/**
			 * Refresh structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._refresh = function () {
				var self = this,
					element = this.element;

				self._setStyle(element);
				self._setInline(element);
				self._setIconpos(element);
				self._setIcon(element);
				self._setSize(element);
				self._setDisabled(element);

				return null;
			};

			/**
			 * Get value of button
			 * @method _getValue
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._getValue = function () {
				return this.element.textContent;
			};

			/**
			 * Set size of button
			 * @method _setSize
			 * @param {HTMLElement} element
			 * @param {string|number} value
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setSize = function (element, value) {
				var style = element.style,
					options = this.options,
					size = parseInt(value || options.size, 10);

				if (size < 32) {
					size = 32;
				}
				if (size > 230) {
					size = 230;
				}
				style.height = size + "px";
				style.width = size + "px";
			};

			/**
			 * Set value of button
			 * @method _setValue
			 * @param {string} value
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._setValue = function (value) {
				this.element.textContent = value;
			};

			/**
			 * Enable button
			 * @method _enable
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._enable = function (element) {
				var self = this,
					options = self.options;

				if (element) {
					if (element.tagName.toLowerCase() === "button") {
						element.disabled = false;
					}
					if (!this.isCustomElement) {
						element.removeAttribute("disabled");
					}
					element.classList.remove(classes.DISABLED);
					options.disabled = false;
				}
			};

			/**
			 * Disable button
			 * @method _disable
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.Button
			 */
			prototype._disable = function (element) {
				var options = this.options;

				if (element) {
					if (element.tagName.toLowerCase() === "button") {
						element.disabled = true;
					}
					if (!this.isCustomElement) {
						element.setAttribute("disabled", "disabled");
					}
					element.classList.add(classes.DISABLED);
					options.disabled = true;
				}
			};

			ns.widget.core.Button = Button;

			engine.defineWidget(
				"Button",
				"button, [data-role='button'], .ui-btn, input[type='button']",
				[],
				Button,
				"core"
			);

			engine.defineWidget(
				"inputButton",
				"",
				[],
				Button,
				"core",
				false,
				false,
				HTMLInputElement
			);

			engine.defineWidget(
				"formButton",
				"",
				[],
				Button,
				"core",
				false,
				false,
				HTMLButtonElement
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Mobile UI Components
 *
 * The Web UI Framework (called as **TAU**; Tizen Advanced UI framework) provides rich Tizen components that are optimized for the Tizen Web browser.
 *
 * ##UI components list
 *
 * The following table displays the components provided by the Tizen mobile Web UI Framework.
 *
 * @class ns.widget.mobile
 * @seeMore https://developer.tizen.org/dev-guide/2.2.1/org.tizen.web.uiwidget.apireference/html/web_ui_framework.htm "Web UI Framework Reference"
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window, ns) {
	"use strict";
				ns.widget.mobile = ns.widget.mobile || {};
			}(window, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Popup
 * Widget handles creating and managing popup windows.
 *
 * ##Default selectors
 * In default all elements with _data-role=popup_ or CSS class _.ui-popup_ are changed to Tizen WebUI popups.
 *
 * ##HTML Examples
 *
 * ###Create simple popup from div
 *
 *		@example
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *		<!-- link related with popup-->
 *		<a href="#popup">Click to open popup</a>
 *
 * ###Create simple popup positioned to window
 *
 * Popup inherits value of option _positionTo_ from property _data-position-to_ set in link.
 *
 *		@example
 *		<!--definition of link, which opens popup and sets its position-->
 *		<a href="#center_info" data-position-to="window">Click to open popup</a>
 *		<!--definition of popup, which inherits property position from link-->
 *		<div id="center_info" data-role="popup" class="center_info">
 *			<div class="ui-popup-text">
 *				<p>Pop-up dialog box, a child window that blocks user interaction to the parent windows</p>
 *			</div>
 *		</div>
 *
 * ###Create popup with title and button
 *
 *		@example
 *		<a href="#center_title_1btn">Click to open popup</a>
 *		<!--definition of popup with a title and button-->
 *		<div id="center_title_1btn" data-role="popup" class="center_title_1btn">
 *			<div class="ui-popup-title">
 *				<h1>Popup title</h1>
 *			</div>
 *			<div class="ui-popup-text">
 *				Pop-up dialog box, a child window that blocks user interaction to the parent windows
 *			</div>
 *			<div class="ui-popup-button-bg">
 *				<a data-role="button" data-rel="back" data-inline="true">Button</a>
 *			</div>
 *		</div>
 *
 * ###Create popup with menu
 *
 * A menu can be created by placing listview inside a popup.
 *
 *		@example
 *		<a href="#center_liststyle_1btn">Click to open popup</a>
 *		<div id="center_liststyle_1btn" data-role="popup" class="center_liststyle_1btn">
 *			<div class="ui-popup-title">
 *				<h1>Popup title</h1>
 *			</div>
 *			<div class="ui-popup-scroller-bg" data-scroll="y">
 *				<ul data-role="listview" data-icon="1line-textonly">
 *					<li><a href="#">List item 1</a></li>
 *					<li><a href="#">List item 2</a></li>
 *				</ul>
 *			</div>
 *			<div class="ui-popup-button-bg">
 *				<a data-role="button" data-rel="back" data-inline="true">Cancel</a>
 *			</div>
 *		</div>
 *
 * ###Create popup with nested menu
 *
 * A nested menu can be created by placing collapsible-set widget with listview elements.
 *
 *		@example
 *		<a href="#popupNested">Click to open popup</a>
 *		<div id="popupNested" data-role="popup">
 *			<div data-role="collapsible-set" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d">
 *				<div data-role="collapsible">
 *					<h2>First menu</h2>
 *					<ul data-role="listview">
 *						<li><a href="#" >Item 1</a></li>
 *						<li><a href="#" >Item 2</a></li>
 *					</ul>
 *				</div>
 *				<div data-role="collapsible">
 *					<h2>Second menu</h2>
 *					<ul data-role="listview">
 *						<li><a href="#" >Item 1</a></li>
 *						<li><a href="#" >Item 2</a></li>
 *					</ul>
 *				</div>
 *			</div>
 *		</div>
 *
 * ###Create popup with form
 *
 * A form can be created by placing inputs elements inside popup.
 *
 *		@example
 *		<a href="#textbox_popup">Click to open popup</a>
 *		<div id="textbox_popup" data-role="popup" class="center_title_2btn">
 *			<div class="ui-popup-title">
 *				<h1>PopupTest<h1>
 *			</div>
 *			<div class="ui-popup-text">
 *				<input type="text" size="20" />
 *				<input type="text" size="20" />
 *			</div>
 *			<div class="ui-popup-button-bg">
 *				<a data-role="button" id="btn_textbox_popup_cancel" data-inline="true">Cancel</a>
 *				<a data-role="button" data-rel="back" data-inline="true">OK</a>
 *			</div>
 *		</div>
 *
 * ##Manual constructor
 * For manual creation of popup widget you can use constructor of widget:
 *
 *		@example
 *		<div id="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			var popupElement = document.getElementById("popup"),
 *				popup = tau.widget.Popup(popupElement);
 *			popup.open();
 *		</script>
 *
 * If jQuery library is loaded, its method can be used:
 *
 *		@example
 *		<div id="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			var popup = $("#popup").popup();
 *			popup.popup("open");
 *		</script>
 *
 * ##Context popup with arrow
 *
 * If property _id_ is set in link and option _positionTo="origin"_ in popup, the context popup will be opened after clicking.
 *
 *		@example
 *		<!-- definition of link, which opens popup with id popup in context style with arrow -->
 *		<a href="#popup" id="linkId" data-position-to="origin" data-role="button" data-inline="true">Click to open context popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic context popup, no options set.</p>
 *		</div>
 *
 * Be award that option _positionTo_ has value "origin" in popup by default. However, the property _positionTo_ is inherited from related link and this inherited value has higher priority during opening process and overwrites the previous value. So, if we do not change it in popup and do not set value of _data-position-to_ other than "origin" in link, popup connected with link will be always opened in context style.
 *
 * To be sure that popup will be opened in context style with arrow, we can set properties _data-position-to="origin"_ as well as _id_ in the related with popup link as in the example above.
 *
 * Moreover, the same result can be achieve by setting only _id_ and not setting _positionTo_ in link because popup has value "origin" for option _positionTo_ by default.
 *
 *		@example
 *		<!-- in link id is set -->
 *		<a href="#popup" id="linkId" data-role="button" data-inline="true">Click to open context popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *
 * After building, the value of option _positionTo_ can be changed by using method _option_.
 *
 *		@example
 *		<a href="#popup" id="linkId" data-role="button" data-inline="true">Click to open context popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// changing value of option positionTo by method option
 *			var popupWidget = tau.widget.Popup(document.getElementById("popup"));
 *			popupWidget.option("positionTo", "origin");
 *		</script>
 *
 * If jQuery is loaded:
 *
 *		@example
 *		<a href="#popup" id="linkId" data-role="button" data-inline="true">Click to open context popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// changing value of option positionTo by method option
 *			$("#popup").popup("option", "positionTo", "origin");
 *		</script>
 *
 *
 * Context popup can be created also manually for elements different than link by pushing options such as _positionTo_ and _link to method _open_.
 *
 *		@example
 *		<!-- element with no properties - popup will be opened next to it in context style manually -->
 *		<div id="linkId">Click to open context popup</div>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// set opening popup on click event
 *			document.getElementById("linkId").addEventListener("click", function () {
 *				// open context popup
 *				var popupWidget = tau.widget.Popup(document.getElementById("popup"));
 *				// opening with options
 *				popupWidget.open({link: "linkId", positionTo: "origin"});
 *			});
 *		</script>
 *
 * If jQuery is loaded:
 *
 *		@example
 *		<!-- element with no properties - popup will be opened next to it in context style manually -->
 *		<div id="linkId">Click to open context popup</div>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// set opening popup on click event
 *			$("#linkId").on("click", function () {
 *				// opening with options
 *				$("#popup").popup("open", {link: "linkId", positionTo: "origin"});
 *			});
 *		</script>
 *
 * These options can be also set globally and then method _open_ can be called without options. However, this solution can be used only for TAU API.
 *
 *		@example
 *		<!-- element with no properties - popup will be opened next to it in context style manually -->
 *		<div id="linkId">Link for popup</div>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// set options
 *			var popupWidget = tau.widget.Popup(document.getElementById("popup"));
 *			popupWidget.option({positionTo: "origin", link: "linkId"}); // here we set positionTo and id of link, which sets placement of popup
 *
 *			// set opening popup on click event
 *			document.getElementById("linkId").addEventListener("click", function () {
 *				//if options are set, we can call method open without options
 *				popupWidget.open();
 *			});
 *		</script>
 *
 * For jQuery API, id of link has to be always added as a option:
 *
 *		@example
 *		<!-- element with no properties - popup will be opened next to it in context style manually -->
 *		<div id="linkId">Link for popup</div>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// set option positionTo
 *			$("#popup").popup("option", "positionTo", "origin");
 *
 *			// set opening popup on click event
 *			$("#linkId").on("click", function () {
 *				// for jQuery API, link has to be added as a option
 *				$("#popup").popup("open", {link: "linkId"});
 *			});
 *		</script>
 *
 *
 * ##Special classes
 *
 * There are some special CSS classes, which changes the style of popup:
 *
 *  - _center_info_ - basic pop-up message<br>
 *  - _center_title_ - pop-up message with a title<br>
 *  - _center_basic_1btn_ - pop-up message with 1 button<br>
 *  - _center_basic_2btn_ - pop-up message with 2 horizontal buttons<br>
 *  - _center_title_1btn_ - pop-up message with a title and 1 button<br>
 *  - _center_title_2btn_ - pop-up message with a title and 2 horizontal buttons<br>
 *  - _center_title_3btn_ - pop-up message with a title and 3 horizontal buttons<br>
 *  - _center_button_vertical_ - pop-up message with vertical buttons<br>
 *  - _center_checkbox_ - pop-up message with a check box<br>
 *  - _center_liststyle_1btn_ - pop-up message with a list and 1 button<br>
 *  - _center_liststyle_2btn_ - pop-up message with a list and 2 horizontal buttons<br>
 *  - _center_liststyle_3btn_ - pop-up message with a list and 3 horizontal buttons<br>
 *
 * ##Methods
 *
 * To call method on widget you can use one of existing API:
 *
 * First API is from tau namespace:
 *
 *		@example
 *		var popupElement = document.getElementById("popup"),
 *			popup = tau.widget.Popup(popupElement);
 *
 *		popup.methodName(methodArgument1, methodArgument2, ...);
 *
 * Second API is jQuery Mobile API and for call _methodName_ you can use:
 *
 *		@example
 *		$(".selector").popup("methodName", methodArgument1, methodArgument2, ...);
 *
 * ##Opening popup
 * There are two ways to open popup.
 *
 * ###Opening by clicking on link
 *
 * If link has _id_ of popup set as value of property _href_, then this popup will be opened after clicking on it.
 *
 *		@example
 *		<!--definition of link, which opens popup with id popup-->
 *		<a href="#popup">Click to open popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 * Be award that context popup with arrow will be opened if link has _id_ property set and _data-position-to="origin"_  as in this example:
 *
 *		@example
 *		<!--definition of link, which opens context popup with id popup-->
 *		<a href="#popup" id="linkId" data-position-to="origin" data-role="button" data-inline="true">Click to open context popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 * To open window popup, property _data-position-to="window"_ must be set in link or popup.
 *
 *		@example
 *		<!--definition of link, which opens window popup with id popup-->
 *		<a href="#popup" id="linkId" data-position-to="window">Click to open popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 * ###Opening manually
 *
 * To open popup with _id_ "popup", tau namespace can be used:
 *
 *		@example
 *		<div id="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			var popupElement = document.getElementById("popup"),
 *				popup = tau.widget.Popup(popupElement);
 *			popup.open();
 *		</script>
 *
 * If jQuery library is loaded, this method can be used:
 *
 *		@example
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			var popup = $("#popup").popup();
 *			popup.popup("open");
 *		</script>
 *
 *
 * ## Closing popup
 *
 * ###Closing by clicking on button inside
 *
 * If link inside popup has property _data-rel="back"_, then popup will be closed after clicking on it as in this example:
 *
 *		@example
 *		<a href="#center_title_1btn" data-position-to="window">Click to open popup</a>
 *		<!--definition of popup with a title and button-->
 *		<div id="center_title_1btn" data-role="popup" class="center_title_1btn">
 *			<div class="ui-popup-title">
 *				<h1>Popup title</h1>
 *			</div>
 *			<div class="ui-popup-text">
 *				Pop-up dialog box, a child window that blocks user interaction to the parent windows
 *			</div>
 *			<div class="ui-popup-button-bg">
 *				<a data-role="button" data-rel="back" data-inline="true">Button</a>
 *			</div>
 *		</div>
 *
 *
 * The selector, which causes closing on click, can be changed by setting option _closeLinkSelector_ in popup.
 *
 * ###Closing manually
 *
 * To close popup with _id_ "popup", tau namespace can be used:
 *
 *		@example
 *		<a href="#popup" data-position-to="window">Click to open popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			var popupElement = document.getElementById("popup"),
 *				popup = tau.widget.Popup(popupElement);
 *			// close popup after opening
 *			popupElement.addEventListener("popupafteropen", function () {
 *				popup.close();
 *			});
 *		</script>
 *
 * If jQuery library is loaded, this method can be used:
 *
 *		@example
 *		<a href="#popup" data-position-to="window">Click to open popup</a>
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			$("#popup").on("popupafteropen", function () {
 *				$("#popup").popup("close");
 *			});
 *		</script>
 *
 * ## Handling Popup Events
 *
 * To use popup events, use the following code:
 *
 *		@example
 *		<!-- Popup html code -->
 *		<div id="popup" data-role="popup">
 *			<p>This is a completely basic popup, no options set.</p>
 *		</div>
 *
 *		<script>
 *			// Use popup events
 *			var popup = document.getElementById("popup");
 *			popup.addEventListener("popupafteropen", function() {
 *				// Implement code for popupafteropen event
 *			});
 *		</script>
 *
 * Full list of available events is in [events list section](#events-list).

 * @since 2.0
 * @class ns.widget.mobile.Popup
 * @extends ns.widget.core.Popup
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Michał Szepielak <m.szepielak@samsung.com>
 */

/**
 * Triggered when process of opening popup is completed.
 * The "popupafteropen" event is triggered when the popup has completely appeared on the screen and all associated animations have completed.
 * @event popupafteropen
 * @member ns.widget.mobile.Popup
 */
/**
 * Triggered when process of opening popup is completed.
 * The "popupshow" event is triggered when the popup has completely appeared on
 * the screen and all associated animations have completed. This event is
 * triggered in the same time as event "popupafteropen".
 * @event popupshow
 * @member ns.widget.mobile.Popup
 */
/**
 * Triggered before a popup computes the coordinates where it will appear.
 * The "beforeposition" event is triggered before the popup starts the opening animations and calculates the coordinates where it will appear on the screen. Handling this event gives an opportunity to modify the content of the popup before it appears on the screen.
 * @event beforeposition
 * @member ns.widget.mobile.Popup
 */

/**
 * Triggered when the process of closing popup is completed.
 * The "popupafterclose" event is triggered when the popup has completely disappeared from the screen and all associated animations have completed.
 * @event popupafterclose
 * @member ns.widget.mobile.Popup
 */
/**
 * Triggered when the process of closing popup is completed.
 * The "popuphide" event is triggered when the popup has completely disappeared
 * from the screen and all associated animations have completed. This event is
 * triggered at the same time as event "popupafterclose".
 * @event popuphide
 * @member ns.widget.mobile.Popup
 */
(function (window, document, ns) {
	"use strict";
				var CorePopup = ns.widget.core.ContextPopup,
				CorePopupPrototype = CorePopup.prototype,

				Listview = ns.widget.core.Listview,

				engine = ns.engine,

				doms = ns.util.DOM,

				POPUP_SELECTOR = "[data-role='popup'], .ui-popup",

				objectUtils = ns.util.object,

				utilSelector = ns.util.selectors,

				Popup = function () {
					var self = this;

					CorePopup.call(this);
					// set options
					self.options = objectUtils.merge(self.options, Popup.defaults);
					self._positionCallback = null;
				};

			Popup.classes = CorePopup.classes;

			/**
			* @property {Object} options Object with default options
			* @property {string} [options.transition="none"] Sets the default transition for the popup.
			* @property {string} [options.positionTo="origin"] Sets the element relative to which the popup will be centered.
			* @property {Array} [options.directionPriority=["bottom", "top", "right", "left"]] Sets directions of popup's placement by priority.
			* First one has the highest priority, last the lowest. It is *deprecated* option.
			* @property {string} [options.closeLinkSelector="a[data-rel="back"]"] Sets selector for buttons in popup
			* @property {boolean} [options.history=false] Sets whether to alter the url when a popup is open to support the back button.
			* @member ns.widget.mobile.Popup
			*/
			Popup.defaults = objectUtils.merge({}, CorePopup.defaults, {
				closeLinkSelector: "a[data-rel='back']",
				transition: "pop",
				directionPriority: ["bottom", "top", "right", "left"], /* deprecated */
				arrow: "b,t,r,l",
				positionTo: "origin"
			});

			Popup.events = objectUtils.merge({}, CorePopup.events, {
				AFTER_OPEN: "popupafteropen",
				AFTER_CLOSE: "popupafterclose"
			});

			Popup.selector = POPUP_SELECTOR;

			Popup.prototype = new CorePopup();

			/**
			* Build structure of popup widget
			* @method _build
			* @param {HTMLElement} element
			* @return {HTMLElement}
			* @protected
			* @member ns.widget.mobile.Popup
			*/
			Popup.prototype._build = function (element) {
				var page = utilSelector.getClosestByClass(element, "ui-page") || document.body,
					elementClassList = element.classList,
					self = this,
					i,
					popupContentClassList,
					DOMTokenListPrototype = DOMTokenList.prototype,
					classListToSwap;

				if (element.parentNode !== page) {
					page.appendChild(element);
				}
				element = CorePopupPrototype._build.call(self, element);
				popupContentClassList = self._ui.content.classList;

				//This is for backwards compatibility when
				//.ui-popup-activity class was in div with class
				//.ui-popup-content, now .ui-popup-activity class
				// should be placed in most outer Popup div that has class .ui-popup
				if (popupContentClassList.contains("ui-popup-activity")) {

					classListToSwap = [];

					for (i = 0; i < popupContentClassList.length; i++) {
						if (popupContentClassList[i].indexOf("ui-popup-activity") !== -1) {
							classListToSwap.push(popupContentClassList[i]);
						}
					}

					DOMTokenListPrototype.remove.apply(popupContentClassList, classListToSwap);
					for (i = 0; i < classListToSwap.length; i++) {
						elementClassList.add(classListToSwap[i]);
					}
				}
				return element;
			};

			Popup.prototype._setDirectionPriority = function (element, value) {
				if (value) {
					this.options.arrow = value.map(function (arrow) {
						return arrow.charAt(0).toLowerCase();
					}).join(",");
				}
			};

			/**
			 * Refresh structure
			 * @method _refresh
			 * @protected
			 * @member ns.widget.mobile.Popup
			 */
			Popup.prototype._refresh = function () {
				var self = this;

				if (this._isActive()) {
					if (typeof self._positionCallback === "function") {
						self._positionCallback();
					}
					CorePopupPrototype._refresh.call(this);
				}
			};

			/**
			 * set height of content
			 * @method _setContentHeight
			 * @protected
			 * @member ns.widget.mobile.Popup
			 * */
			Popup.prototype._setContentHeight = function () {
				var computedMaxHeight = window.innerHeight - doms.getCSSProperty(this.element, "margin-top", 0, "float");

				CorePopupPrototype._setContentHeight.call(this, computedMaxHeight);
			};

			/**
			 * Find clicked element.
			 * @method _findClickedElement
			 * @param {number} x
			 * @param {number} y
			 * @protected
			 * @member ns.widget.mobile.Popup
			 */
			Popup.prototype._findClickedElement = function (x, y) {
				var element = CorePopupPrototype._findClickedElement.call(this, x, y),
					button = utilSelector.getClosestBySelector(element, engine.getWidgetDefinition("Button").selector);

				return button || element;
			};

			/**
			 * Show popup
			 * @method _show
			 * @protected
			 * @member ns.widget.mobile.Popup
			 */
			Popup.prototype._show = function () {
				var self = this,
					listviewElement,
					listview;

				// Disabled colored list for contextual popups
				if (self.options.positionTo !== "window") {
					listviewElement = self.element.querySelector("." + Listview.classes.LISTVIEW);
					if (listviewElement) {
						listview = engine.getBinding(listviewElement);
						if (listview) {
							listview.option("coloredBackground", false);
						}
					}
				}

				CorePopupPrototype._show.call(self);
			};

			/**
			 * Show popup
			 * @method _onShow
			 * @protected
			 * @member ns.widget.mobile.Popup
			 */
			Popup.prototype._onShow = function () {
				CorePopupPrototype._onShow.call(this);
				this.trigger(Popup.events.AFTER_OPEN);
			};

			/**
			 * Hide popup
			 * @method _onHide
			 * @protected
			 * @member ns.widget.mobile.Popup
			 */
			Popup.prototype._onHide = function () {
				CorePopupPrototype._onHide.call(this);
				this.trigger(Popup.events.AFTER_CLOSE);
			};

			/**
			 * Set callback, which is called on "resize" event. This callback should return desired position of popup after resizing.
			 *
			 * This function should be used instead of "setPositionCB".
			 *
			 *      @example
			 *      <div id="popup" data-role="popup">
			 *          <p>This is a completely basic popup, no options set.</p>
			 *      </div>
			 *
			 *      <script>
			 *          var popupWidget = tau.widget.Popup(document.getElementById("popup"));
			 *
			 *          popupWidget.setPositionCallback(function() {
			 *                return {x: 10, y: 20};
			 *         });
			 *      </script>
			 *
			 * If jQuery is loaded:
			 *
			 *      @example
			 *      <div id="popup" data-role="popup">
			 *          <p>This is a completely basic popup, no options set.</p>
			 *      </div>
			 *
			 *      <script>
			 *          $("#popup").popup("setPositionCallback", function() {
			 *                return {x: 10, y: 20};
			 *          });
			 *      </script>
			 *
			 * @method setPositionCallback
			 * @param {Function} callback Function called on resizing. It should return desired position of popup as object with "x" and "y" properties.
			 * @member ns.widget.mobile.Popup
			 * @since 2.3
			 */
			Popup.prototype.setPositionCallback = function (callback) {
				this._positionCallback = callback;
			};

			ns.widget.mobile.Popup = Popup;
			engine.defineWidget(
				"Popup",
				POPUP_SELECTOR,
				[
					"open",
					"close",
					"reposition",
					"setPositionCallback",
					"setPositionCB"
				],
				Popup,
				"mobile",
				true
			);
			}(window, window.document, ns));

/*global window, define, ns*/
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # ScrollView Widget
 * Widgets allows for creating scrollable panes, lists, etc.
 *
 * ## Default selectors
 * All elements with _data-role=content attribute or _.ui-scrollview
 * css class will be changed to ScrollView widgets, unless they specify
 * _data-scroll=none attribute.
 *
 * ### HTML Examples
 *
 * #### Data attribute
 *
 *		@example
 *		<div data-role="page">
 *			<div data-role="content"><!-- this will become scrollview //-->
 *				content data
 *			</div>
 *		</div>
 *
 * #### CSS Class
 *
 *		@example
 *		<div data-role="page">
 *			<div class="ui-content"><!-- this will become scrollview //-->
 *				content data
 *			</div>
 *		</div>
 *
 * ## Manual constructor
 *
 * To create the widget manually you can use 2 different APIs, the TAU
 * API or jQuery API.
 *
 * ### Create scrollview by TAU API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			</div>
 *		</div>
 *		<script>
 *			var page = tau.widget.Page(document.getElementById("myPage")),
 *				scrollview = tau.widget.Scrollview(page.ui.content);
 *		</script>
 *
 * ### Create scrollview using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			</div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role='content']").scrollview();
 *		</script>
 *
 * ## Options for Scrollview widget
 *
 * Options can be set using data-* attributes or by passing them to
 * the constructor.
 *
 * There is also a method **option** for changing them after widget
 * creation.
 *
 * jQuery mobile format is also supported.
 *
 * ## Scroll
 *
 * This options specifies of a content element should become Scrollview
 * widget.
 *
 * You can change this by all available methods for changing options.
 *
 * ### By data-scroll attribute
 *
 *		@example
 *		<div data-role="page">
 *			<div data-role="content" data-scroll="none">
 *				content
 *			</div>
 *		</div>
 *
 * ### By config passed to constructor
 *
 *		@example
 *		<div class="myPageClass" data-role="page">
 *			<div data-role="content">
 *				content
 *			</div>
 *		</div>
 *		<script>
 *			var contentElement = document.querySelector(".myPageClass > div[data-role=content]");
 *			tau.widget.Scrollview(contentElement, {
 *				"scroll": false
 *			});
 *		</script>
 *
 * ### By using jQuery API
 *
 *		@example
 *		<div class="myPageClass" data-role="page">
 *			<div data-role="content">
 *				content
 *			</div>
 *		</div>
 *		<script>
 *			$(".myPageClass > div[data-role='content']").scrollview({
 *				"scroll": false
 *			});
 *		</script>
 *
 * ## ScrollJumps
 *
 * Scroll jumps are small buttons which allow the user to quickly
 * scroll to top or left
 *
 * You can change this by all available methods for changing options.
 *
 * ### By data-scroll-jump
 *
 *		@example
 *		<div data-role="page">
 *			<div data-role="content" data-scroll-jump="true">
 *				content
 *			</div>
 *		</div>
 *
 * ### By config passed to constructor
 *
 *		@example
 *		<div class="myPageClass" data-role="page">
 *			<div data-role="content">
 *				content
 *			</div>
 *		</div>
 *		<script>
 *			var contentElement = document.querySelector(".myPageClass > div[data-role=content]");
 *			tau.widget.Scrollview(contentElement, {
 *				"scrollJump": true
 *			});
 *		</script>
 *
 * ### By using jQuery API
 *
 *		@example
 *		<div class="myPageClass" data-role="page">
 *			<div data-role="content">
 *				content
 *			</div>
 *		</div>
 *		<script>
 *			$(".myPageClass > div[data-role='content']").scrollview({
 *				"scrollJump": true
 *			});
 *		</script>
 *
 * ## Methods
 *
 * Page methods can be called trough 2 APIs: TAU API and jQuery API
 * (jQuery mobile-like API)
 *
 * @class ns.widget.mobile.Scrollview
 * @extends ns.widget.core.Scrollview
 *
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Grzegorz Osimowicz <g.osimowicz@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 */
/**
 * Triggered when scrolling operation starts
 * @event scrollstart
 * @member ns.widget.mobile.Scrollview
 */
/**
 * Triggered when scroll is being updated
 * @event scrollupdate
 * @member ns.widget.mobile.Scrollview
 */
/**
 * Triggered when scrolling stops
 * @event scrollstop
 * @member ns.widget.mobile.Scrollview
 */
(function (window, document, ns) {
	"use strict";
				var Scrollview = ns.widget.core.Scrollview;

			ns.widget.mobile.Scrollview = Scrollview;
			ns.engine.defineWidget(
				"Scrollview",
				".ui-content:not([data-scroll='none']):not([data-handler='true']):not(.ui-scrollview-clip)" +
						":not(.ui-scrolllistview):not(.ui-scrollhandler)" +
						", [data-scroll]:not([data-scroll='none']):not([data-handler='true']):not(.ui-scrollhandler)" +
						", .ui-scrollview:not([data-scroll='none']):not([data-handler='true']):not(.ui-scrollhandler)",
				[
					"scrollTo",
					"ensureElementIsVisible",
					"centerToElement",
					"getScrollPosition",
					"skipDragging",
					"translateTo"
				],
				Scrollview,
				"tizen",
				true
			);
			}(window, window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * #BaseWidgetMobile
 * Extension of class BaseWidget for mobile profile.
 * This class has compatibility properties and methods with jQuery Mobile Widget.
 * @class ns.widget.mobile.BaseWidgetMobile
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
	
			var BaseWidgetMobile = function () {
					this.options = {};
				},
				BaseWidget = ns.widget.BaseWidget,
				basePrototype = BaseWidget.prototype,
				parentConfigure = basePrototype.configure,
				parentDisable = basePrototype.disable,
				parentEnable = basePrototype.enable,
				prototype = new BaseWidget(),
				slice = [].slice;

			BaseWidgetMobile.classes = BaseWidget.classes;

			/**
			 * Configures widget object from definition.
			 * @method configure
			 * @param {Object} definition
			 * @param {string} definition.name Name of widget
			 * @param {string} definition.selector Selector of widget
			 * @param {string} definition.binding Path to file with widget (without extension)
			 * @param {HTMLElement} element
			 * @param {Object} options Configure options
			 * @member ns.widget.mobile.BaseWidgetMobile
			 * @return {ns.widget.mobile.BaseWidgetMobile}
			 * @instance
			 */
			prototype.configure = function (definition, element, options) {
				var self = this,
					definitionName,
					widgetName;

				parentConfigure.call(self, definition, element, options);
				if (definition) {
					definitionName = definition.name;
					widgetName = definitionName && definitionName.toLowerCase();
					/**
					 * @property {string} widgetName Widget base class
					 * @member ns.widget.mobile.BaseWidgetMobile
					 * @instance
					 */
					self.widgetName = widgetName;
					/**
					* @property {string} widgetBaseClass Widget base class
					* @member ns.widget.mobile.BaseWidgetMobile
					* @instance
					*/
					self.widgetBaseClass = self.namespace + "-" + widgetName;
					/**
					* @property {number} uuid Number id of widget instance
					* @member ns.widget.mobile.BaseWidgetMobile
					* @instance
					*/
					self.uuid = ns.getNumberUniqueId();

					/**
					 * @property {string} eventNamespace Namespace of widget events (suffix for events)
					 * @member ns.widget.mobile.BaseWidgetMobile
					 * @instance
					 */
					self.eventNamespace = "." + widgetName + (self.uuid || "");

					/**
					 * @property {string} [defaultElement='<div>'] Default element for the widget
					 * @member ns.widget.mobile.BaseWidgetMobile
					 * @instance
					 */
					self.defaultElement = "<div>";
				}

				return this;
			};

			/**
			* Disables widget.
			* @method disable
			* @member ns.widget.mobile.BaseWidgetMobile
			* @instance
			*/
			prototype.disable = function () {
				var self = this,
					element = self.element,
					elementClasses = element.classList,
					args = slice.call(arguments);

				parentDisable.apply(self, args);
				elementClasses.add(self.widgetFullName + "-disabled");
			};

			/**
			* Enables widget.
			* @method enable
			* @member ns.widget.mobile.BaseWidgetMobile
			* @instance
			*/
			prototype.enable = function () {
				var self = this,
					element = self.element,
					elementClasses = element.classList,
					args = slice.call(arguments);

				parentEnable.apply(self, args);
				elementClasses.remove(self.widgetFullName + "-disabled");
			};

			/**
			* Throws exception.
			* @method raise
			* @param {?string} msg Message of throw
			* @member ns.widget.mobile.BaseWidgetMobile
			* @instance
			*/
			prototype.raise = function (msg) {
				throw "Widget [" + this.widgetName + "]: " + msg;
			};

			/**
			* Returns element of widget.
			* @method widget
			* @member ns.widget.mobile.BaseWidgetMobile
			* @return {HTMLElement}
			* @instance
			*/
			prototype.widget = function () {
				return this.element;
			};

			BaseWidgetMobile.prototype = prototype;

			// definition
			ns.widget.mobile.BaseWidgetMobile = BaseWidgetMobile;

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * #Expandable
 * Expandable component allows you to expand or collapse content when tapped.
 *
 * ## Default selectors
 * All elements with _data-role="expandable"_ or class _.ui-expandable_ are
 * changed to expandable component.
 *
 * ###HTML Examples
 *
 * ####Create expandable div using data-role
 *
 *		@example
 *		<div id="expandable" data-role="expandable">
 *			<h1>Expandable head</h1>
 *			<div>Content</div>
 *		</div>
 *
 * ####Create Expandable list using data-role
 *
 *		@example
 *		<ul data-role="listview">
 *			<li data-role="expandable">
 *				<h2>Expandable head</h2>
 *				<-- sub list -->
 *				<ul data-role="listview">
 *					<li>sub list item1</li>
 *					<li>sub list item2</li>
 *				</ul>
 *			</li>
 *		</ul>
 *
 * ####Create using class selector
 *
 *		@example
 *		<div id="expandable" class="ui-expandable">
 *			<h1>Expandable head</h1>
 *			<div>Content</div>
 *		</div>
 *
 * ## Manual constructor
 * For manual creation of Expandable component you can use constructor of component
 * from **tau** namespace:
 *
 *		@example
 *		<script>
 *			var expandableElement = document.getElementById("expandable"),
 *				expandable = tau.widget.Expandable(expandableElement,
 *					{collapsed: false});
 *		</script>
 *
 * Constructor has one require parameter **element** which are base
 * **HTMLElement** to create widget. We recommend get this element by method
 * *document.getElementById*. Second parameter is **options** and it is
 * a object with options for widget.
 *
 * ##Options for Collapsible Widget
 *
 * You can change option for widget using method **option**.
 *
 * ##Methods
 *
 * To call method on widget you can use one of existing API:
 *
 *		@example
 *		var expandableElement = document.getElementById("expandable"),
 *			expandable = tau.widget.Expandable(expandableElement);
 *
 *		expandable.methodName(methodArgument1, methodArgument2, ...);
 *
 * @since 2.4
 * @class ns.widget.mobile.Expandable
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Marcin Jakuszko <m.jakuszko@samsung.com>
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 * @author Heeju Joo <heeju.joo@samsung.com>
 */
(function (document, ns) {
	"use strict";
				/**
				 * @property {Object} BaseWidget alias variable
				 * @private
				 * @static
				 */
			var BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				/**
				 * @property {Object} engine alias variable
				 * @private
				 * @static
				 */
				engine = ns.engine,
				/**
				 * @property {Object} selectors alias variable
				 * @private
				 * @static
				 */
				selectors = ns.util.selectors,
				/**
				 * @property {Object} eventUtil alias variable
				 * @private
				 * @static
				 */
				eventUtil = ns.event,
				/**
				 * @property {Object} domUtils alias variable
				 * @private
				 * @static
				 */
				domUtils = ns.util.DOM,

				/**
				 * @property {Object} expandableSelectors selectors used in this widget
				 * @private
				 * @static
				 */
				expandableSelectors = {
					HEADING: "h1,h2,h3,h4,h5,h6,legend,li"
				},

				Expandable = function () {
					/**
					 * Expandable widget options.
					 * @property {boolean} [options.collapsed=true] Determines if content should be collapsed on load
					 * @property {string} [options.heading="h1,h2,h3,h4,h5,h6,legend,li"] Within the Expandable container, the first immediate child element
					 * that matches this selector will be used as the header for the Expandable.
					 */
					this.options = {
						collapsed: true,
						heading: expandableSelectors.HEADING
					};

					this._eventHandlers = {};
					this._ui = {
						expandableHeadingElement: null,
						expandableHeadingContent: null
					};

				},
				/**
				 * Dictionary object containing commonly used widget classes
				 * @property {Object} classes
				 * @readonly
				 * @static
				 * @member ns.widget.mobile.Expandable
				 */
				classes = {
					uiExpandable: "ui-expandable",
					uiExpandableContent: "ui-expandable-content",
					uiExpandableContentCollapsed: "ui-expandable-content-collapsed",
					uiExpandableCollapsed: "ui-expandable-collapsed",
					uiExpandableHeading: "ui-expandable-heading",
					uiExpandableHeadingCollapsed: "ui-expandable-heading-collapsed",
					uiExpandableHeadingToggle: "ui-expandable-heading-toggle",
					uiExpandableHeadingActive: "ui-expandable-heading-active"
				};


			Expandable.prototype = new BaseWidget();
			Expandable.classes = classes;
			Expandable.selectors = expandableSelectors;

			/**
			 * Handler function for expanding/collapsing widget
			 * @method toggleExpandableHandler
			 * @param {ns.widget.mobile.Expandable} self
			 * @param {HTMLElement} element
			 * @param {Event} event
			 * @private
			 */
			function toggleExpandableHandler(self, element, event) {
				var	ui = self._ui,
					elementClassList = element.classList,
					heading = ui.expandableHeadingElement,
					headingClassList = heading.classList,
					content = ui.expandableContentElement,
					contentClassList = content.classList,
					isCollapse = event.type === "collapse";

				if (event.defaultPrevented) {
					return;
				}

				event.preventDefault();

				//Toggle functions switched to if/else statement due to toggle bug on Tizen
				if (isCollapse) {
					elementClassList.add(classes.uiExpandableCollapsed);
					headingClassList.add(classes.uiExpandableHeadingCollapsed);
					contentClassList.add(classes.uiExpandableContentCollapsed);
				} else {
					elementClassList.remove(classes.uiExpandableCollapsed);
					headingClassList.remove(classes.uiExpandableHeadingCollapsed);
					contentClassList.remove(classes.uiExpandableContentCollapsed);
				}

				content.setAttribute("aria-hidden", isCollapse);
				eventUtil.trigger(element, isCollapse ? "collapsed" : "expanded");
			}

			function setHeadingActiveClassHandler(self, setClass) {
				var headingClassList = self._ui.expandableHeadingElement.classList;

				if (setClass) {
					headingClassList.add(classes.uiExpandableHeadingActive);
				} else {
					headingClassList.remove(classes.uiExpandableHeadingActive);
				}
			}

			function toggleEventTypeHandler(self, event) {
				var element = self.element,
					heading = self._ui.expandableHeadingElement,
					eventType = heading.classList.contains(classes.uiExpandableHeadingCollapsed) ? "expand" : "collapse";

				eventUtil.trigger(element, eventType);

				event.preventDefault();
				eventUtil.stopPropagation(event);
			}

			/**
			 * Build widget structure
			 * @method _build
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.mobile.Expandable
			 */
			Expandable.prototype._build = function (element) {
				var self = this,
					options = self.options,
					ui = self._ui,
					elementClassList = element.classList,
					expandableHeading,
					expandableContent,
					alternativeHeading;

				if ((element.parentNode && element.parentNode.tagName.toLowerCase() === "ul") && (element.tagName.toLowerCase() === "div")) {
					ns.warn("Don't make the Expandable list using <div>. It violates standard of HTML rule. Instead of, please use <li>.");
				}
				elementClassList.add(classes.uiExpandable);

				// First child matching selector is Expandable header
				expandableHeading = selectors.getChildrenBySelector(element, options.heading)[0];
				if (!expandableHeading) {
										expandableHeading = document.createElement("h1");
					element.appendChild(expandableHeading);
				}

				if (expandableHeading.tagName.toLowerCase() === "legend") {
					alternativeHeading = document.createElement("div");
					alternativeHeading.setAttribute("role", "heading");
					alternativeHeading.innerHTML = expandableHeading.innerHTML;
					element.replaceChild(alternativeHeading, expandableHeading);
					expandableHeading = alternativeHeading;
				}
				expandableHeading.classList.add(classes.uiExpandableHeading);

				// Wrap all widget content
				domUtils.wrapInHTML(element.childNodes, "<div class='" + classes.uiExpandableContent + "'></div>");

				// Move header out
				element.insertBefore(expandableHeading, element.firstChild);

				domUtils.wrapInHTML(expandableHeading.childNodes, "<a class='" + classes.uiExpandableHeadingToggle + "'></a>");

				expandableContent = expandableHeading.nextElementSibling;

				ui.expandableHeadingElement = expandableHeading;
				ui.expandableContentElement = expandableContent;

				return element;
			};

			/**
			 * Init widget structure
			 * @method _init
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.mobile.Expandable
			 */
			Expandable.prototype._init = function (element) {
				var self = this,
					ui = self._ui;

				ui.expandableHeadingElement = ui.expandableHeadingElement || selectors.getChildrenByClass(element, classes.uiExpandableHeading)[0];
				ui.expandableContentElement = ui.expandableContentElement || selectors.getChildrenByClass(element, classes.uiExpandableContent)[0];

				if (self.options.collapsed) {
					element.classList.add(classes.uiExpandableCollapsed);
					ui.expandableHeadingElement.classList.add(classes.uiExpandableHeadingCollapsed);
					ui.expandableContentElement.classList.add(classes.uiExpandableContentCollapsed);
				}

				return element;
			};

			/**
			 * Bind widget events
			 * @method _bindEvents
			 * @protected
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.Expandable
			 */
			Expandable.prototype._bindEvents = function (element) {
				var self = this,
					eventHandlers = self._eventHandlers,
					heading = self._ui.expandableHeadingElement;

				// Declare handlers with and assign them to local variables
				eventHandlers.toggleExpandable = toggleExpandableHandler.bind(null, self, element);
				eventHandlers.removeActiveClass = setHeadingActiveClassHandler.bind(null, self, false);
				eventHandlers.addActiveClass = setHeadingActiveClassHandler.bind(null, self, true);
				eventHandlers.toggleEventType = toggleEventTypeHandler.bind(null, self);

				eventUtil.on(element, "expand collapse", eventHandlers.toggleExpandable, false);

				eventUtil.on(heading, "vmousedown", eventHandlers.addActiveClass, false);
				eventUtil.on(heading, "vmousemove vmousecancel vmouseup", eventHandlers.removeActiveClass, false);
				eventUtil.on(heading, "vclick", eventHandlers.toggleEventType, false);
			};

			/**
			 * This method refreshes Expandable.
			 *
			 *		@example
			 *		<div id="Expandable" data-role="expandable">
			 *			<h6>Expandable head</h6>
			 *			<div>Content</div>
			 *		</div>
			 *
			 *		<script>
			 *			var ExpandableWidget = tau.widget.Expandable(document.getElementById("Expandable"));
			 *			ExpandableWidget.refresh();
			 *		</script>
			 *
			 * @method refresh
			 * @chainable
			 * @member ns.widget.mobile.Expandable
			 */

			/**
			* Refresh structure
			* @method _refresh
			* @protected
			* @member ns.widget.mobile.Expandable
			*/
			Expandable.prototype._refresh = function () {
				return;
			};

			/**
			 * Removes the Expandable functionality completely.
			 *
			 *		@example
			 *		<div id="Expandable" data-role="expandable">
			 *			<h6>Expandable head</h6>
			 *			<div>Content</div>
			 *		</div>
			 *
			 *		<script>
			 *			var ExpandableWidget = tau.widget.Expandable(document.getElementById("Expandable"));
			 *			ExpandableWidget.destroy();
			 *		</script>
			 *
			 * @method destroy
			 * @member ns.widget.mobile.Expandable
			 */

			/**
			 * Destroy widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.Expandable
			 */
			Expandable.prototype._destroy = function () {
				var self = this,
					element = self.element,
					heading = self._ui.expandableHeadingElement,
					eventHandlers = self._eventHandlers,
					parentNode = element.parentNode;

				eventUtil.off(element, "expand collapse", eventHandlers.toggleExpandable, false);

				eventUtil.off(heading, "vmousedown", eventHandlers.addActiveClass, false);
				eventUtil.off(heading, "vmousemove vmousecancel vmouseup", eventHandlers.removeActiveClass, false);
				eventUtil.off(heading, "vclick", eventHandlers.toggleEventType, false);

				self._ui = null;
				self._eventHandlers = null;

				eventUtil.trigger(document, "destroyed", {
					widget: "Expandable",
					parent: parentNode
				});
			};

			// definition
			ns.widget.mobile.Expandable = Expandable;
			engine.defineWidget(
				"Expandable",
				"[data-role='expandable'], .ui-expandable",
				[],
				Expandable,
				"mobile"
			);
			}(window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, define, ns */
/**
 * # List View
 * List view component is used to display, for example, navigation data,
 * results, and data entries, in a list format.
 *
 * ## Default selectors
 * All elements which have a data-role [data-role="listview"] or class .ui-listview
 * will become Listview widgets. It is recommended to use UL, LI tags for list creation
 *
 * ###HTML examples
 *
 * ####Create a page widget using the data-role attribute
 *
 *      @example
 *      <ul data-role="listview">
 *          <li>
 *              ...some item
 *          </li>
 *      </ul>
 *
 * #### Create a listview widget using css classes
 *
 *      @example
 *      <ul class="ui-listview">
 *          <li>
 *            ...some item
 *          </li>
 *      </ul>
 *
 * ### Manual constructor
 *
 * These examples show how to create a Listview widget by hand using
 * JavaScript code
 *
 * #### Created using TAU api
 *
 *      @example
 *      <ul class="ui-listview ui-colored-list" id="list">
 *          <li class="ui-li-flex">
 *               <span class="ui-li-area ui-li-area-a">
 *                   <span class="ui-li-text">
 *                        <span>1 text...</span>
 *                   </span>
 *               </span>
 *           </li>
 *      </ul>
 *
 *      <script type="text/javascript">
 *          var listview = tau.widget.Listview(document.getElementById("list"));
 *      </script>
 *
 * #### Create reorder list with TAU api
 *
 *      @example
 *      <a class="ui-btn" id="dragButton">DRAG</a>
 *      <ul class="ui-listview ui-colored-list" id="reorder">
 *          <li class="ui-li-flex">
 *              <span>1 text...</span>
 *          </li>
 *          <li class="ui-li-flex">
 *              <span>2 text...</span>
 *          </li>
 *      </ul>
 *
 *      <script type="text/javascript">
 *        var listview = tau.widget.Listview(document.getElementById("reorder"));
 *        var dragButton = document.getElementById("dragButton");
 *
 *        tau.event.on(dragButton, "click", function(){
 *            listview.toggleDragMode();
 *        });
 *      </script>
 *
 * ## Options for Listview widget
 *
 * Options can be set by using data-* attributes or by passing them
 * to the constructor.
 *
 * There is also a method **option** for changing them after widget
 * creation.
 *
 * @class ns.widget.mobile.Listview
 * @extends ns.widget.core.Listview
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @since 2.0
 */
(function (window, document, ns) {
	"use strict";
				var Page = ns.widget.core.Page,
				Popup = ns.widget.mobile.Popup,
				Scrollview = ns.widget.mobile.Scrollview,
				CoreListview = ns.widget.core.Listview,
				CoreListviewProto = CoreListview.prototype,
				utils = ns.util,
				objectUtils = utils.object,
				selectorUtils = utils.selectors,
				eventUtils = ns.event,
				now = Date.now,
				colorDefinitionRegex = new RegExp("[^0-9\-\.:,]+", "gi"),
				min = Math.min,
				max = Math.max,
				round = Math.round,
				ceil = Math.ceil,
				slice = [].slice,
				utilsEvents = ns.event,
				isNumber = utils.isNumber,
				colorTmp = [0, 0, 0, 0],
				MAX_IDLE_TIME = 3 * 1000, //3s
				direction = {
					PREV: -1,
					HOLD: 0,
					NEXT: 1
				},
				Listview = function () {
					var self = this,
						/**
						 * @property {Object} options
						 * @property {boolean} options.coloredBackground=true enables/disables colored background
						 */
						options = {
							coloredBackground: true,
							colorRestOfTheScreenBellow: true,
							colorRestOfTheScreenAbove: true,
							firstColorStep: 0,
							lastColorStep: 0
						};

					CoreListview.call(self);

					// merge options from prototype
					self.options = (!self.options) ?
						options :
						objectUtils.fastMerge(self.options, options);

					// async function (requestAnimationFrame)
					self._async = utils.requestAnimationFrame;
					// rendering context
					self._context = null;
					// canvas elements style
					self._canvasStyle = null;
					// detected parent scrollable element
					self._scrollableContainer = null;
					// detected parent page element
					self._pageContainer = null;
					// detected parent popup element
					self._popupContainer = null;
					// drawCallback
					self._drawCallback = null;
					// scrollCallback
					self._scrollCallback = null;
					// _backgroundRenderCallback
					self._backgroundRenderCallback = null;
					// flag for async timers
					self._running = false;
					// flag for drawing
					self._redraw = false;
					// starting default color for gradient background
					self._colorBase = [250, 250, 250, 1];
					// color modifier for each background gradient step
					self._colorStep = [0, 0, 0, -0.04];
					// _lastChange
					self._lastChange = 0;
					// arrays of neighbor colored listview related to parent
					self._siblingListsBellow = [];
					self._siblingListsAbove = [];

					initializeGlobalsForDrag(self);
				},
				WIDGET_SELECTOR = "[data-role='listview'], .ui-listview",
				/**
				 * @property {Object} classes
				 * @property {string} classes.BACKGROUND_LAYER
				 * @property {string} classes.GRADIENT_BACKGROUND_DISABLED
				 * @member ns.widget.mobile.Listview
				 * @static
				 * @readonly
				 */
				classes = {
					/**
					 */
					"BACKGROUND_LAYER": "ui-listview-background",
					"GRADIENT_BACKGROUND_DISABLED": "ui-listview-background-disabled",
					"GROUP_INDEX": "ui-group-index",
					"POPUP_LISTVIEW": "ui-popup-listview",
					"DRAG_ACTIVE": "ui-drag-active",
					"EXPANDABLE": "ui-expandable",
					"ITEM": "ui-listview-item",
					"ITEM_ACTIVE": "ui-listview-item-active",
					"HELPER": "ui-listview-helper",
					"HOLDER": "ui-listview-holder",
					"SNAPSHOT": "snapshot",
					"HANDLER": "ui-listview-handler",
					"DRAG_MODE": "dragMode",
					"ACTIVATE_HANDLERS": "activateHandlers",
					"CANCEL_ANIMATION": "cancelAnimation",
					"DEACTIVATE_HANDLERS": "deactivateHandlers"
				},

				/**
				 * @property {Object} events
				 * @property {string} events.BACKGROUND_RENDER
				 * @member ns.widget.mobile.Listview
				 * @static
				 * @readonly
				 */
				events = {
					"BACKGROUND_RENDER": "event-listview-background-render"
				},
				engine = ns.engine,
				prototype = new CoreListview();

			/**
			 * Set globals for drag functionality, constructor helper
			 * @method initializeGlobalsForDrag
			 * @param {Object} self
			 * @member ns.widget.mobile.Listview
			 * @private
			 */
			function initializeGlobalsForDrag(self) {
				self._topOffset = window.innerHeight;
				self._previousVisibleElement = null;
				self._canvasWidth = 0;
				self._canvasHeight = 0;

				self._dragMode = false;
				self.originalListPosition = 0;
				self.indexDraggingElement = 0;

				self._ui = {
					helper: {},
					holder: {}
				};

				self._snapshotItems = [];
				self._liElements = [];
				self.topValue = 0;
				self.isScrolling = null;
				self._reorderElements = [];
			}

			/**
			 * Modifies input color array (rgba) by a specified
			 * modifier color array (rgba)
			 * @method modifyColor
			 * @param {Array} color input array of color values (rgba)
			 * @param {Array} modifier array of color values (rgba)
			 * @member ns.widget.mobile.Listview
			 * @return {number} Return opacity of color
			 * @private
			 */
			function modifyColor(color, modifier) {
				color[0] += modifier[0];
				color[1] += modifier[1];
				color[2] += modifier[2];
				color[3] += modifier[3];

				color[0] = min(max(0, color[0]), 255);
				color[1] = min(max(0, color[1]), 255);
				color[2] = min(max(0, color[2]), 255);
				color[3] = min(max(0, color[3]), 1);

				return color[3];
			}

			/**
			 * Copies values from one color array (rgba) to other
			 * @method copyColor
			 * @param {Array} inc color array (rgba)
			 * @param {Array} out color array (rgba)
			 * @member ns.widget.mobile.Listview
			 * @private
			 */
			function copyColor(inc, out) {
				out[0] = inc[0];
				out[1] = inc[1];
				out[2] = inc[2];
				out[3] = inc[3];
			}

			/**
			 * Returns number from specified value (mixed) or
			 * 0 if no param is not a number
			 * @method toNumber
			 * @param {mixed} val
			 * @return {number}
			 * @member ns.widget.mobile.Listview
			 * @private
			 */
			function toNumber(val) {
				var res = parseFloat(val);

				// fast NaN check
				if (res === res) {
					return res;
				}

				return 0;
			}

			Listview.classes = objectUtils.fastMerge(classes, CoreListview.classes);
			Listview.events = events;

			prototype._setFirstColorStep = function (element, value) {
				value = parseInt(value, 10);
				this.options.firstColorStep = value;

				return true;
			}

			/**
			 * Enables / disables colored background
			 * @method _setColoredBackground
			 * @param {HTMLElement} element Main element of widget
			 * @param {boolean} value option value
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._setColoredBackground = function (element, value) {
				element.classList.toggle(classes.GRADIENT_BACKGROUND_DISABLED, !value);
				this.options.coloredBackground = value;
			};

			prototype._addCanvas = function (element) {
				var canvas = document.createElement("canvas"),
					context = canvas.getContext("2d");

				canvas.classList.add(classes.BACKGROUND_LAYER);
				element.insertBefore(canvas, element.firstElementChild);
				this._context = context;

				return canvas;
			}

			/**
			 * Builds widget
			 * @method _build
			 * @param {HTMLElement} element Main element of widget
			 * @member ns.widget.mobile.Listview
			 * @return {HTMLElement}
			 * @protected
			 */
			prototype._build = function (element) {
				var self = this,
					newElement = CoreListviewProto._build.call(self, element),
					isChildListview = element &&
						selectorUtils.getClosestByClass(element.parentElement, "ui-listview");

				self._isChildListview = isChildListview;

				if (!isChildListview) {
					self._addCanvas(newElement);
				}

				return newElement;
			};

			prototype._getCanvas = function () {
				var self = this,
					canvas;

				// If reference to canvas has been changed.
				// Developer can remove canvas from list, eg. by element.innerHTML = "<li>item</li>"
				if (self._context) {
					canvas = self._context.canvas;
				}
				if (!canvas || !canvas.parentElement) {
					canvas = self.element.querySelector("." + classes.BACKGROUND_LAYER);
					if (!canvas) {
						canvas = self._addCanvas(self.element);
					}
					self._context = canvas.getContext("2d");
				}

				return canvas;
			}

			/**
			 * Init colors used to draw colored bars
			 * @method _prepareColors
			 * @member ns.widget.mobile.Listview
			 * @protected
			 * */
			prototype._prepareColors = function () {
				var self = this,
					canvas = self._getCanvas(),
					computedAfter,
					colorCSSDefinition,
					baseColor,
					modifierColor,
					colors;

				if (canvas) {
					computedAfter = window.getComputedStyle(canvas, ":before");
					colorCSSDefinition = computedAfter.getPropertyValue("content");

					if (colorCSSDefinition.length > 0) {
						colorCSSDefinition = colorCSSDefinition.replace(colorDefinitionRegex, "");
						colors = colorCSSDefinition.split("::");
						if (colors.length === 2) {
							baseColor = colors[0].split(",").filter(isNumber).map(toNumber);
							modifierColor = colors[1].split(",").filter(isNumber).map(toNumber);
							if (baseColor.length > 0) {
								copyColor(baseColor, self._colorBase);
							}
							if (modifierColor.length > 0) {
								copyColor(modifierColor, self._colorStep);
							}
						}
					}
				}
			};

			/**
			 * Refresh background canvas
			 * @method _refreshBackgroundCanvas
			 * @param {HTMLElement} container
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._refreshBackgroundCanvas = function (container, element) {
				var self = this,
					canvas = self._getCanvas(),
					canvasStyle,
					rect,
					// canvasHeight of canvas element
					canvasHeight,
					// canvasWidth of canvas element
					canvasWidth;

				if (canvas) {
					canvasStyle = canvas.style;
					rect = element.getBoundingClientRect();
					canvasHeight = 0;
					canvasWidth = rect.width;

					// calculate canvasHeight of canvas
					if (container) {
						canvasHeight = container.getBoundingClientRect().height;
					}

					canvasHeight = Math.max(rect.height, canvasHeight) + self._topOffset;

					// limit canvas for better performance
					canvasHeight = Math.min(canvasHeight, 4 * window.innerHeight);
					self._canvasHeight = canvasHeight;
					self._canvasWidth = canvasWidth;

					// init canvas
					canvas.setAttribute("width", canvasWidth);
					canvas.setAttribute("height", canvasHeight);
					canvasStyle.width = canvasWidth + "px";
					canvasStyle.height = canvasHeight + "px";
				}
			};

			/**
			 * Find and store all possible containers which may contains the listview
			 * @method _findContainers
			 * @param {HTMLElement} element widget element
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._findContainers = function (element) {
				var self = this;

				self._pageContainer = selectorUtils.getClosestByClass(element, Page.classes.uiPage);
				self._popupContainer = selectorUtils.getClosestByClass(element, Popup.classes.popup);
				self._scrollableContainer = selectorUtils.getClosestByClass(element, Scrollview.classes.clip) ||
					selectorUtils.getClosestByTag(element, "section");
			};

			/**
			 * Method checks if listview contains in popup then add specific class
			 * @method _checkClosestPopup
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._checkClosestPopup = function () {
				var self = this,
					popupContainer = self._popupContainer,
					popupContent;

				if (popupContainer) {
					popupContainer.classList.add(classes.POPUP_LISTVIEW);
					popupContent = popupContainer.querySelector("." + Popup.classes.content);
					if (popupContent) {
						self._scrollableContainer = popupContent;
					}
				}
			};

			/**
			 * Refreshes colored list in widget background,
			 * @method _refreshColoredBackground
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._refreshColoredBackground = function () {
				var self = this,
					element = self.element,
					canvas;

				// if listview contains in popup then add specific class
				self._checkClosestPopup();

				self._redraw = true;
				self._lastChange = Date.now();
				self._previousVisibleElement = null;

				self._prepareColors();
				self._refreshBackgroundCanvas(self._scrollableContainer, element);

				canvas = self._getCanvas();
				if (canvas) {
					if (element.firstElementChild &&
						element.firstElementChild.tagName.toLowerCase() !== "canvas") {
						element.insertBefore(canvas, element.firstElementChild);
					} else if (!(element.firstElementChild instanceof HTMLElement)) {
						element.appendChild(canvas);
					}
					if (typeof self._frameCallback === "function") {
						self._frameCallback();
					}
				}
			};

			/**
			 * Refreshes widget, critical to call after changes (ex. in background color)
			 * @method _refresh
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._refresh = function () {
				var self = this,
					element = self.element,
					popupContainer = selectorUtils.getClosestByClass(element, Popup.classes.popup);

				self._findContainers(element);

				if (self.options.coloredBackground) {
					self._refreshColoredBackground();
				} else if (popupContainer) {
					// if listview contains in popup then remove specific class
					popupContainer.classList.remove(classes.POPUP_LISTVIEW);
					self._popupContainer = popupContainer;
				}
			};

			/**
			 * Initializes widget and async timers
			 * @method _init
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this,
					context = self._context,
					canvas,
					foundSelf = false,
					siblingLists;

				self.options.firstColorStep = parseInt(self.options.firstColorStep, 10);

				if (!self._isChildListview) {
					if (!context) {
						canvas = element.querySelector("." + classes.BACKGROUND_LAYER);
						if (canvas) {
							context = canvas.getContext("2d");
						}
					} else {
						canvas = context.canvas;
					}

					if (context) {
						self._canvasStyle = canvas.style;
						self._frameCallback = self._handleFrame.bind(self);

						self.refresh();
					}
				}

				// check other sibling colored lists
				siblingLists = [].slice.call(self.element.parentElement.querySelectorAll(WIDGET_SELECTOR));
				// remove itself listview from list and above listview elements
				self._siblingListsBellow = siblingLists.filter(function (listviewElement) {
					if (foundSelf) {
						return true;
					}
					foundSelf = listviewElement === self.element;
					return false;
				});
				foundSelf = false;
				self._siblingListsAbove = siblingLists.filter(function (listviewElement) {
					if (foundSelf || listviewElement === self.element) {
						foundSelf = true;
						return false;
					}
					return true;
				});

				if (self._siblingListsBellow.length > 0) {
					// disable coloring the test of space below current listview
					self.options.colorRestOfTheScreenBellow = false;
				}
				if (self._siblingListsAbove.length > 0) {
					self.options.colorRestOfTheScreenAbove = false;
				}

			};

			/**
			 * Handles scroll event data
			 * @method _handleScroll
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._handleScroll = function () {
				var self = this;

				self._lastChange = now();

				if (!self._running) {
					self._running = true;
					self._async(self._frameCallback);
				}
			};

			/**
			 * Handles touch start event
			 * Used to disable scroll event listener on reorder
			 * @method _handleTouchStart
			 * @member ns.widget.mobile.Listview
			 * @param {Event} event Event
			 * @protected
			 */
			prototype._handleTouchStart = function (event) {
				var self = this,
					scrollableContainer = self._scrollableContainer;

				if (self._dragMode && event.srcElement.classList.contains(classes.HANDLER)) {
					eventUtils.off(scrollableContainer, "scroll", self._reorderCallback);
				}
			};

			/**
			 * Handles touch end event
			 * Used to re-enable scroll event listener when reorder ends
			 * @method _handleTouchEnd
			 * @member ns.widget.mobile.Listview
			 * @param {Event} event Event
			 * @protected
			 */
			prototype._handleTouchEnd = function (event) {
				var self = this,
					scrollableContainer = self._scrollableContainer;

				if (self._dragMode && event.srcElement.classList.contains(classes.HANDLER)) {
					eventUtils.on(scrollableContainer, "scroll", self._reorderCallback);
				}
			};

			/**
			 * Handles scroll and scroll end
			 * This method re-enables canvas on listview and removes backgrounds
			 * added on reorder
			 * It also adds timeout that is triggered on scroll end
			 * @method _handleReorderScroll
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._handleReorderScroll = function () {
				var self = this,
					reorderElements = self._reorderElements,
					liElement,
					liElements;

				self._setColoredBackground(self.element, true);

				liElements = slice.call(self.element.querySelectorAll("li"));

				while (reorderElements.length > 0) {
					liElement = reorderElements.pop();
					if (liElements[liElement]) {
						liElements[liElement].style.backgroundColor = "";
					}
				}

				window.clearTimeout(self.isScrolling);

				self.isScrolling = setTimeout(function () {
					self._setReorderBackground();
				}, 70);
			};

			/**
			 * Refresh event wrapper
			 * @method _backgroundRender
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._backgroundRender = function () {
				this.refresh();
			};

			/**
			 * Calculate element height as difference between top of current element and top of next element
			 * @param {HTMLElement} nextVisibleLiElement
			 * @param {Object} rectangle
			 * @return {number}
			 */
			function calculateElementHeight(nextVisibleLiElement, rectangle) {
				// we need round to eliminate empty spaces between bars
				if (nextVisibleLiElement) {
					return round(nextVisibleLiElement.getBoundingClientRect().top - rectangle.top);
				}
				return round(rectangle.height);
			}

			/**
			 * Handles frame computations and drawing (if necessary)
			 * @method _handleFrame
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._handleFrame = function () {
				var self = this,
					element = self.element,
					// get all li liElements
					liElements = slice.call(element.querySelectorAll("li")),
					nextVisibleLiElement,
					// scrollable container, connected with scrollview
					scrollableContainer = self._scrollableContainer,
					scrollTop = scrollableContainer ? scrollableContainer.scrollTop : 0,
					scrollableContainerRect = null,
					scrollableContainerTop = 0,
					// top of element to calculate offset top
					top = element.getBoundingClientRect().top,
					previousVisibleElement = self._previousVisibleElement,
					topOffset = self._topOffset,
					rectangle,
					currentVisibleLiElement = getNextVisible(liElements),
					liOffsetTop,
					height;

				if (scrollableContainer) {
					scrollableContainerRect = scrollableContainer.getBoundingClientRect();
					scrollableContainerTop = scrollableContainerRect.top;
				}

					// Reset first color step if listview is above top edge of scroll container
				if (scrollableContainerRect && top < scrollableContainerTop) {
					if (self.options.firstColorStep !== 0) {
						self.options.firstColorStep = 0;
						self._redraw = true;
					}
				}

				while (currentVisibleLiElement) {
					// store size of current element
					rectangle = getElementRectangle(currentVisibleLiElement);
					liOffsetTop = rectangle.top - top ;
					// get next element to calculate difference
					nextVisibleLiElement = getNextVisible(liElements);
					height = calculateElementHeight(nextVisibleLiElement, rectangle);
					if (liOffsetTop + height - (scrollableContainerTop - top - scrollTop) >= scrollTop) {
						if (currentVisibleLiElement !== previousVisibleElement && self._context) {
							self._previousVisibleElement = currentVisibleLiElement;
							self._context.canvas.style.transform = "translateY(" + (liOffsetTop - topOffset) + "px)";
							self._redraw = true;
						}
						currentVisibleLiElement = null;
					} else {
						// go to next element
						currentVisibleLiElement = nextVisibleLiElement;
					}
				}

				if (self._redraw && self._context) {
					self._handleDraw();
				}
				if (self._running && self._context) {
					self._async(self._frameCallback);
				}
				if (now() - self._lastChange >= MAX_IDLE_TIME) {
					self._running = false;
				}
			};

			/**
			 * Get next visible element from list
			 * @param {HTMLElement} liElements
			 * @return {HTMLElement}
			 */
			function getNextVisible(liElements) {
				var next = liElements.shift();

				while (next) {
					if (next.offsetHeight) {
						return next;
					}
					next = liElements.shift();
				}

				return null;
			}

			/**
			 * Calculate rectangle and create new object which is not read-only
			 * @param {HTMLElement} element
			 * @return {{top: (number|*), height: (number|*), left, width}}
			 */
			function getElementRectangle(element) {
				var rectangle = element.getBoundingClientRect();

				return {
					top: rectangle.top,
					height: rectangle.height,
					left: rectangle.left,
					width: rectangle.width
				};
			}

			/**
			 * Draw bar on canvas
			 * @param {CanvasRenderingContext2D} context
			 * @param {Object} rectangle
			 */
			function drawRectangle(context, rectangle) {
				// set color
				context.fillStyle = "rgba(" + colorTmp[0] + "," + colorTmp[1] + "," + colorTmp[2] + "," + colorTmp[3] + ")";
				// first element is bigger by offset, to show color on scroll in up direction
				context.fillRect(rectangle.left, rectangle.top, rectangle.width, rectangle.height);
			}

			/**
			 * Init color variable and clear canvas
			 * @method _prepareCanvas
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._prepareCanvas = function () {
				var self = this,
					i;

				// prepare first color
				copyColor(self._colorBase, colorTmp);

				// modify first color refer to option "firstColorStep"
				for (i = 0; i < self.options.firstColorStep; i++) {
					modifyColor(colorTmp, self._colorStep);
				}
				self.options.lastColorStep = self.options.firstColorStep;

				// clear canvas
				self._context.clearRect(0, 0, self._canvasWidth, self._canvasHeight);
			};

			function adjustRectangle(rectangle, topOffset, listLeft, previousTop) {
				rectangle.height += topOffset;
				rectangle.left -= listLeft;
				rectangle.top = previousTop;
				return rectangle;
			}

			/**
			 * Draw backgrounds on LI elements
			 * @method _drawLiElements
			 * @member ns.widget.mobile.Listview
			 * @return {Object}
			 * @protected
			 */
			prototype._drawLiElements = function () {
				var self = this,
					element = self.element,
					elements = slice.call(element.querySelectorAll("li")),
					firstElement = elements[0],
					firstElementRect,
					visibleLiElement = getNextVisible(elements),
					nextVisibleLiElement,
					context = self._context,
					step = self._colorStep,
					rectangleList = element.getBoundingClientRect(),
					listLeft = rectangleList.left,
					// get scroll top
					scrollableContainer = self._scrollableContainer,
					scrollableContainerTop = (scrollableContainer) ?
						scrollableContainer.getBoundingClientRect().top : 0,
					// store dimensions of li
					rectangle = null,
					// top on each last element
					previousBottom = 0,
					// top offset of widget
					topOffset = self._topOffset,
					changeColor,
					backgroundRectangles = [],
					firstItem = null,
					firstRectangle = null,
					onceClearUpperPartOfCanvas = false;

				if (firstElement) {
					firstElementRect = firstElement.getBoundingClientRect();
					if (firstElementRect.top + firstElementRect.height >= scrollableContainerTop) {
						onceClearUpperPartOfCanvas = true;
					}
				}

				while (visibleLiElement) {
					// if li element is group index, the color of next element wont change
					changeColor = (!visibleLiElement.classList.contains(classes.GROUP_INDEX) &&
						!visibleLiElement.classList.contains(classes.EXPANDABLE));
					//calculate size of li element
					rectangle = getElementRectangle(visibleLiElement);
					nextVisibleLiElement = getNextVisible(elements);
					rectangle.height = calculateElementHeight(nextVisibleLiElement, rectangle);
					//check if next element is group index, if yes then change its color
					if (!changeColor && nextVisibleLiElement &&
						(nextVisibleLiElement.classList.contains(classes.GROUP_INDEX) ||
						nextVisibleLiElement.classList.contains(classes.EXPANDABLE))) {
						changeColor = true;
					}
					// check that element is visible (can be partially visible)
					if (ceil(rectangle.top + rectangle.height) >= scrollableContainerTop) {
						// adjust height for first element
						rectangle = adjustRectangle(rectangle, topOffset, listLeft, previousBottom);
						topOffset = 0;
						backgroundRectangles.push({
							rectangle: rectangle,
							changeColor: changeColor
						})
						previousBottom += rectangle.height;
					}
					// get visibleLiElement element
					visibleLiElement = nextVisibleLiElement;
				}

				//Remove color buffer above first element if list is not coloring rest of the screen
				firstItem = backgroundRectangles[0];
				if (firstItem) {
					firstRectangle = firstItem.rectangle;
					if (!self.options.colorRestOfTheScreenAbove || onceClearUpperPartOfCanvas) {
						// clear area above list and shrink the rectangle to cover only ont
						self._context.clearRect(firstRectangle.left, firstRectangle.top, firstRectangle.width, firstRectangle.height);
						firstRectangle.top += self._topOffset;
						firstRectangle.height -= self._topOffset;
					}
				}


				backgroundRectangles.forEach(function (bgRect) {
					drawRectangle(context, bgRect.rectangle)
					if (bgRect.changeColor) {
						modifyColor(colorTmp, step);
						self.options.lastColorStep++;
					}
				});

				return rectangle;
			};

			/**
			 * Draw rest of empty space
			 * @method _drawEndOfList
			 * @param {Object} rectangle
			 * @param {CanvasRenderingContext2D} context
			 * @protected
			 */
			prototype._drawEndOfList = function (rectangle, context) {
				var canvasRect;

				// fill rest of canvas by color of next item
				if (rectangle !== null) {
					canvasRect = context.canvas.getBoundingClientRect();
					if (rectangle.height + rectangle.top < canvasRect.height) {
						rectangle.top += rectangle.height;
						rectangle.height = canvasRect.height - rectangle.top;
						drawRectangle(context, rectangle);
					}
				}
			};

			/**
			 * Handles drawing of step-gradient background
			 * @method _handleDraw
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._handleDraw = function () {
				var self = this,
					// store dimensions of li
					rectangle,
					nextListview;

				self._prepareCanvas();
				rectangle = self._drawLiElements();
				if (self.options.colorRestOfTheScreenBellow) {
					self._drawEndOfList(rectangle, self._context);
				}
				// change first color of next listviews
				self._siblingListsBellow.forEach(function (listviewElement) {
					nextListview = ns.engine.getBinding(listviewElement);
					if (nextListview) {
						nextListview.option("firstColorStep", self.options.lastColorStep);
					}
				});

				self._redraw = false;
			};

			/**
			 * Bounds to events
			 * @method _bindEvents
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._bindEvents = function () {
				var self = this,
					scrollableContainer = self._scrollableContainer,
					pageContainer = self._pageContainer,
					popupContainer = self._popupContainer;

				if (!self._isChildListview) {
					if (scrollableContainer) {
						self._scrollableContainer = scrollableContainer;
						self._scrollCallback = self._handleScroll.bind(self);
						self._reorderCallback = self._handleReorderScroll.bind(self);
						self._touchStartCallback = self._handleTouchStart.bind(self);
						self._touchEndCallback = self._handleTouchEnd.bind(self);
						eventUtils.on(scrollableContainer, "touchstart", self._scrollCallback);
						eventUtils.on(scrollableContainer, "touchmove", self._scrollCallback);
						eventUtils.on(scrollableContainer, "touchstart", self._touchStartCallback);
						eventUtils.on(scrollableContainer, "touchend", self._touchEndCallback);
						eventUtils.on(scrollableContainer, "scroll", self._scrollCallback);
					}

					self._backgroundRenderCallback = self._backgroundRender.bind(self);
					self.on("expand collapse", self._backgroundRenderCallback, false);
					// support rotation
					eventUtils.on(window, "resize", self._backgroundRenderCallback, false);

					if (pageContainer) {
						eventUtils.on(pageContainer, Page.events.BEFORE_SHOW, self._backgroundRenderCallback);
					}
					if (popupContainer) {
						eventUtils.on(popupContainer, Popup.events.transition_start, self._backgroundRenderCallback);
					}

					utilsEvents.on(self.element, "animationend webkitAnimationEnd", self, true);
				}
			};

			/**
			 * Destroys widget
			 * @method _destroy
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.Listview
			 * @protected
			 */
			prototype._destroy = function (element) {
				var self = this;

				//phantom hack
				if (element) {
					utilsEvents.off(element, "animationend webkitAnimationEnd", self, true);
				}

				if (self._context) {
					if (self._context.canvas.parentElement) {
						self._context.canvas.parentElement.removeChild(self._context.canvas);
					}
					self._context = null;
				}

				if (self._scrollCallback) {
					if (self._scrollableContainer) {
						eventUtils.off(self._scrollableContainer, "touchstart", self._scrollCallback);
						eventUtils.off(self._scrollableContainer, "touchmove", self._scrollCallback);
						eventUtils.off(self._scrollableContainer, "scroll", self._scrollCallback);
						eventUtils.off(self._scrollableContainer, "touchstart", this._touchStartCallback);
						eventUtils.off(self._scrollableContainer, "touchend", this._touchEndCallback);
						self._scrollableContainer = null;
					}
					self._scrollCallback = null;
				}

				if (self._backgroundRenderCallback) {
					self.off("expand collapse", self._backgroundRenderCallback, false);
					eventUtils.off(window, "resize", self._backgroundRenderCallback, false);
					if (element) {
						eventUtils.off(element, events.BACKGROUND_RENDER, self._backgroundRenderCallback);
					}
					if (self._pageContainer) {
						eventUtils.off(self._pageContainer, Page.events.BEFORE_SHOW, self._backgroundRenderCallback);
						self._pageContainer = null;
					}
					if (self._popupContainer) {
						self._popupContainer.classList.remove(classes.POPUP_LISTVIEW);
						eventUtils.off(self._popupContainer, Popup.events.transition_start, self._backgroundRenderCallback);
						self._popupContainer = null;
					}
					self._backgroundRenderCallback = null;
				}

			};

			/**
			 * Create holder element to help reordering
			 * @method _createHolder
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._createHolder = function () {
				var holder = document.createElement("li"),
					classList = holder.classList;

				classList.add(classes.ITEM);
				classList.add(classes.HOLDER);

				return holder;
			};

			/**
			 * set direction when moving
			 * Based on the previous position defined as moveTop,
			 * the new direction is set
			 * @method _setDirection
			 * @protected
			 * @param {number} moveY new position
			 * @param {number} moveTop previous position
			 * @member ns.widget.mobile.Listview
			 */
			prototype._setDirection = function (moveY, moveTop) {
				var self = this;

				if (moveY < moveTop) {
					self._direction = direction.PREV;
				} else if (moveY === moveTop) {
					self._direction = direction.HOLD;
				} else {
					self._direction = direction.NEXT;
				}
			};

			/**
			 * Relocate elements when "move down" event occur
			 *
			 * New position is calculated for holder element and for items
			 * After setting the new position for item, holder change position in DOM
			 * If we move the pointer for a great length then few items at once should
			 * change location
			 *
			 * When going down we need to go over item for 70% (30% to bottom)
			 * @method _changeLocationDown
			 * @protected
			 * @param {number} range new position for cursor
			 * @param {HTMLElement} holder temporary replacement element
			 * @param {Object} helper is a dragging element
			 * @param {number} length number of items
			 * @member ns.widget.mobile.Listview
			 */
			prototype._changeLocationDown = function (range, holder, helper, length) {
				var item,
					index = 1,
					self = this,
					element = self.element;

				range = range + helper.height;

				//skip first element, as it is canvas with gradient
				for (; index < length; index++) {
					item = element.children[index];
					//to not go over same element and when cursor is over the item for 70%
					if ((helper.element !== item && holder !== item) &&
						range > (self._snapshotItems[index - 1] + item.offsetHeight * 70 / 100) &&
						//item top should be bigger then holder top when going down
						(self._snapshotItems[index - 1] > holder.offsetTop + 15)) {

						//set new top for the moved item, this will consider different height of the elements
						self._snapshotItems[index - 1] = self._snapshotItems[index - 2] + item.offsetHeight;
						self._appendLiStylesToElement(item, self._snapshotItems[index - 2]);
						self._appendLiStylesToElement(holder, self._snapshotItems[index - 1]);
						element.insertBefore(holder, element.children[index].nextSibling);
					}
				}
			};

			/**
			 * Relocate elements when "move up" event occur
			 *
			 * New position is calculated for holder element and for items
			 * After setting the new position for item, holder change position in DOM
			 * If we move the pointer for a great length then few items at once should
			 * change location
			 *
			 * When going up we need to go over item for 30% (70% to bottom)
			 * @method _changeLocationUp
			 * @protected
			 * @param {number} range new position for cursor
			 * @param {HTMLElement} holder temporary replacement element
			 * @param {Object} helper is a dragging element
			 * @param {number} length number of items
			 * @member ns.widget.mobile.Listview
			 */
			prototype._changeLocationUp = function (range, holder, helper, length) {
				var item,
					self = this,
					index = length - 1,
					element = self.element;

				for (; index > 0; index--) {
					item = element.children[index];
					//to not go over same element
					if ((helper.element !== item && holder !== item) &&
						//when cursor is over the item for 70%
						range < (self._snapshotItems[index - 1] + (item.offsetHeight * 30 / 100)) &&
						//item top should be smaller then holder top when going up
						self._snapshotItems[index - 1] + item.offsetHeight < holder.offsetTop + holder.offsetHeight - 15) {

						//set new top for the moved item, this will consider different height of the elements
						self._snapshotItems[index] = self._snapshotItems[index - 1] + holder.offsetHeight;
						element.insertBefore(holder, element.children[index]);
						self._appendLiStylesToElement(holder, self._snapshotItems[index - 1]);
						self._appendLiStylesToElement(item, self._snapshotItems[index]);
					}
				}
			};

			/**
			 * Method for drag prepare event
			 * @method _prepare
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._prepare = function () {
				var self = this,
					element = self.element,
					parentElement = element.parentElement;

				//it means that dragend was not called and handler was just clicked
				if (!element.classList.contains(classes.SNAPSHOT)) {
					self._recalculateTop();
					//save before dragging, the position for original list
					self.originalListPosition = parentElement.getBoundingClientRect().top - 55;
					element.classList.add(classes.SNAPSHOT);
					//change scrollTop as we use absolute and top positions now
					parentElement.scrollTop = -self.originalListPosition;
				}
			};

			/**
			 * Method for dragstart event
			 *
			 * Prepare holder element to replace the drag item
			 * Drag item is represented as helper and it is replaced
			 * by holder because we change the location of helper with top
			 * @method _start
			 * @protected
			 * @param {Event} event Event
			 * @member ns.widget.mobile.Listview
			 */
			prototype._start = function (event) {
				var self = this,
					top,
					holder,
					element = self.element,
					helper = self._ui.helper,
					helperElement = event.detail.srcEvent.srcElement.parentElement,
					helperElementComputed = window.getComputedStyle(helperElement, null),
					helperStyle = helperElement.style;

				top = parseInt(helperElementComputed.getPropertyValue("top"), 10);

				helperElement.classList.add(classes.HELPER);
				holder = self._createHolder();
				holder.style.height = parseFloat(helperElementComputed.getPropertyValue("height")) + "px";
				holder.style.top = helperElement.style.top;

				element.insertBefore(holder, helperElement);
				element.appendChild(helperElement);

				self._appendLiStylesToElement(helperElement, top);

				helper.element = helperElement;
				helper.style = helperStyle;
				helper.height = parseFloat(helperElementComputed.getPropertyValue("height")) || 0;
				helper.startY = event.detail.estimatedY - 55 - helper.height / 2;
				helper.position = {
					startTop: top,
					moveTop: helper.startY,
					startIndex: [].indexOf.call(element.children, holder)
				};

				self._ui.holder = holder;
				helper.element = helperElement;
				self._ui.helper = helper;

				self.topValue = event.detail.estimatedY;
				helper.move = 0;
			};


			/**
			 * callback when drag event
			 * @method _move
			 * @protected
			 * @param {Event} event Event
			 * @member ns.widget.mobile.Listview
			 */
			prototype._move = function (event) {
				var moveY,
					self = this,
					headerHeight,
					ui = self._ui,
					helper = ui.helper,
					holder = ui.holder,
					element = self.element,
					position = helper.position,
					helperElement = helper.element,
					length = element.childElementCount - 1,
					headerElement = document.querySelector(".ui-page-active .ui-header");

				headerHeight = (headerElement) ? headerElement.offsetHeight : 0;
				//move element only in Y axis
				moveY = event.detail.estimatedY - headerHeight - helper.height / 2 +
					-self.originalListPosition;
				self._appendLiStylesToElement(helperElement, moveY);

				self._setDirection(moveY, position.moveTop);
				helperElement.classList.add("ui-listview-item-moved");
				position.moveTop = moveY;

				if (self._direction > 0) {
					self._changeLocationDown(moveY, holder, helper, length);
				} else if (self._direction < 0) {
					self._changeLocationUp(moveY, holder, helper, length);
				}
			};

			/**
			 * Method for dragend event
			 *
			 * Replace holder with the draggable item
			 * @method _end
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._end = function () {
				var self = this,
					element = self.element,
					helper = self._ui.helper,
					holder = self._ui.holder,
					helperElement = helper.element;

				helperElement.classList.remove("ui-listview-item-moved");
				helperElement.classList.remove(classes.HELPER);
				self._appendLiStylesToElement(helperElement, holder.offsetTop);

				element.insertBefore(helperElement, holder);
				element.removeChild(holder);
				self._ui.helper = {};

				self._removeTopOffsets();
				element.classList.remove(classes.SNAPSHOT);

				//change scrollTop as we use absolute and top positions now
				element.parentElement.parentElement.scrollTop = -self.originalListPosition;
				//refresh cache
				self._liElements = slice.call(element.querySelectorAll("li"));
				self.trigger("scroll");
			};

			/**
			 * Method for setting item background on reorder
			 * Ir disables canvas and manually sets background of visible items
			 * It disables canvas on
			 * @method _setReorderBackground
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._setReorderBackground = function () {
				var self = this,
					reorderElements = self._reorderElements,
					element = self.element,
					liElementRect,
					headerElement,
					headerHeight,
					liElements = slice.call(element.querySelectorAll("li")),
					opacity = 100,
					i,
					nextColor;

				headerElement = document.querySelector(".ui-page-active .ui-header");
				headerHeight = (headerElement) ? headerElement.offsetHeight : 0;

				self._setColoredBackground(element, false);
				reorderElements.length = 0;

				for (i = 0; i < liElements.length; i++) {
					liElementRect = liElements[i].getBoundingClientRect();
					if ((liElementRect.bottom >= headerHeight) &&
						(liElementRect.top <= (window.innerHeight || document.documentElement.clientHeight))) {
						reorderElements.push(i);
						nextColor = "rgba(250, 250, 250, " + (opacity / 100) + ")";
						liElements[i].style.backgroundColor = nextColor;
						opacity -= 4;
					}
				}
				if (reorderElements[reorderElements.length - 1] < liElements.length - 1) {
					reorderElements.push(reorderElements[reorderElements.length - 1] + 1);
					liElements[reorderElements[reorderElements.length - 1]].style.backgroundColor = nextColor;
				}
				if (reorderElements[0] > 0) {
					reorderElements.unshift(reorderElements[0] - 1);
					liElements[reorderElements[0]].style.backgroundColor = "white";
				}
			};

			/**
			 * Method for click event
			 *
			 * Because "prepare drag" is run every time when we press handler
			 * we need to use "click" to revert changes done by "prepare drag"
			 * Click is not called if we start drag
			 * @method _click
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._click = function () {
				var self = this;

				self._removeTopOffsets();
				self.element.classList.remove(classes.SNAPSHOT);

				//change scrollTop as we use absolute and top positions now
				self.element.parentElement.parentElement.scrollTop = -self.originalListPosition;
			};

			/**
			 * Method for animationEnd event
			 *
			 * Controls the behavior for 2 animations:
			 * -sliding from right to left
			 * -sliding from left to right
			 * @method _animationEnd
			 * @protected
			 * @param {Event} event Event
			 * @member ns.widget.mobile.Listview
			 */
			prototype._animationEnd = function (event) {
				var self = this,
					listContainer = event.target.parentElement.parentElement,
					listContainerClasses = listContainer.classList;

				if (listContainerClasses.contains(classes.ACTIVATE_HANDLERS)) {
					listContainerClasses.remove(classes.ACTIVATE_HANDLERS);

					//disable animation, so it will not run when reorder
					listContainerClasses.add(classes.CANCEL_ANIMATION);
				} else if (listContainerClasses.contains(classes.DEACTIVATE_HANDLERS)) {
					//remove handlers after animation end
					self._removeHandlers();
					listContainerClasses.remove(classes.DEACTIVATE_HANDLERS);
					listContainerClasses.remove(classes.DRAG_MODE);
				}
				event.stopImmediatePropagation();
				event.preventDefault();
			};

			/**
			 * Handle events
			 * @method handleEvent
			 * @public
			 * @param {Event} event Event
			 * @member ns.widget.mobile.Listview
			 */
			prototype.handleEvent = function (event) {
				var self = this,
					srcElement = (event.detail && event.detail.srcEvent &&
						event.detail.srcEvent.srcElement) || event.srcElement;

				if (srcElement.classList.contains(classes.HANDLER)) {
					switch (event.type) {
						case "click":
							self._click(event);
							event.preventDefault();
							break;
						case "dragprepare":
							self._prepare(event);
							break;
						case "dragstart":
							self._start(event);
							event.preventDefault();
							break;
						case "drag":
							self._move(event);
							break;
						case "dragend":
							self._end(event);
							break;
						case "animationend":
						case "webkitAnimationEnd":
							self._animationEnd(event);
							break;
					}
				}
			};

			/**
			 * add handlers for list elements
			 * @method _appendHandlers
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._appendHandlers = function () {
				var i = 0,
					self = this,
					handler,
					liElement,
					liElements = self._liElements,
					length = liElements.length;

				for (; i < length; i++) {
					liElement = liElements[i];
					handler = document.createElement("div");
					handler.classList.add(classes.HANDLER);
					liElement.appendChild(handler);
				}
			};

			/**
			 * remove handlers from list elements
			 * @method _removeHandlers
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._removeHandlers = function () {
				var i = 0,
					self = this,
					handler,
					liElement,
					liElements = self._liElements,
					length = liElements.length;

				for (; i < length; i++) {
					liElement = liElements[i];
					handler = liElement.querySelector("." + classes.HANDLER);
					liElement.removeChild(handler);
				}
			};

			/**
			 * add new tops
			 * @method _recalculateTop
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._recalculateTop = function () {
				var i = 0,
					self = this,
					offsetTop,
					liElement,
					liElements = self._liElements,
					length = liElements.length;

				for (; i < length; i++) {
					liElement = liElements[i];
					offsetTop = liElement.offsetTop;
					self._snapshotItems.push(offsetTop);
					self._appendLiStylesToElement(liElements[i], self._snapshotItems[i]);
				}
			};

			/**
			 * remove top offsets when going back to standard list mode
			 * @method _removeTopOffsets
			 * @protected
			 * @member ns.widget.mobile.Listview
			 */
			prototype._removeTopOffsets = function () {
				var i = 0,
					self = this,
					liElements = self._liElements,
					length = liElements.length;

				for (; i < length; i++) {
					liElements[i].style.top = "";
				}
			};

			/**
			 * adds top style to the elements
			 * @method _appendLiStylesToElement
			 * @protected
			 * @param {HTMLElement} element
			 * @param {number} Y distance form top
			 * @member ns.widget.mobile.Listview
			 */
			prototype._appendLiStylesToElement = function (element, Y) {
				element.style.top = Y + "px";
			};

			/**
			 * switch list in drag mode, add draggable handlers
			 * @method toggleDragMode
			 * @public
			 * @member ns.widget.mobile.Listview
			 */
			prototype.toggleDragMode = function () {
				var self = this,
					element = self.element,
					scrollableContainer = self._scrollableContainer;

				self._dragMode = !self._dragMode;

				if (self._dragMode) {
					element.classList.add(classes.DRAG_MODE);
					self._liElements = slice.call(element.querySelectorAll("li"));
					self._appendHandlers();
					element.classList.add(classes.ACTIVATE_HANDLERS);

					utilsEvents.on(element, "click drag dragstart dragend dragcancel dragprepare", self, true);
					eventUtils.on(scrollableContainer, "scroll", self._reorderCallback);
					self.trigger("scroll");
					//support drag
					utilsEvents.enableGesture(
						element,
						new utilsEvents.gesture.Drag({
							blockVertical: false
						})
					);
				} else {
					element.classList.remove(classes.CANCEL_ANIMATION);
					element.classList.add(classes.DEACTIVATE_HANDLERS);

					utilsEvents.off(element, "click drag dragstart dragend dragcancel dragprepare", self, true);
					utilsEvents.disableGesture(element);
				}
			};

			Listview.prototype = prototype;
			ns.widget.mobile.Listview = Listview;
			engine.defineWidget(
				"Listview",
				WIDGET_SELECTOR,
				[],
				Listview,
				"mobile",
				true,
				true,
				HTMLUListElement
			);

			}(window, window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* global window, define */
/* eslint-disable no-console */
/**
 * #Core namespace
 * Object contains main framework methods.
 * @class ns
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 */
(function (document, console) {
	"use strict";
			var idNumberCounter = 0,
			currentDate = +new Date(),
			slice = [].slice,
			rootNamespace = "",
			fileName = "",
			infoForLog = function (args) {
				var dateNow = new Date();

				args.unshift("[" + rootNamespace + "][" + dateNow.toLocaleString() + "]");
			},
			ns = window.ns || window.tau || {},
			nsConfig = window.nsConfig || window.tauConfig || {};

		ns.info = ns.info || {
			profile: "custom"
		};
		ns.tauPerf = ns.tauPerf || {};

		window.ns = ns;
		window.nsConfig = nsConfig;

		window.tau = ns;
		window.tauConfig = nsConfig;

		rootNamespace = nsConfig.rootNamespace;
		fileName = nsConfig.fileName;

		/**
		 * Return unique id
		 * @method getUniqueId
		 * @static
		 * @return {string}
		 * @member ns
		 */
		ns.getUniqueId = function () {
			return rootNamespace + "-" + ns.getNumberUniqueId() + "-" + currentDate;
		};

		/**
		 * Return unique id
		 * @method getNumberUniqueId
		 * @static
		 * @return {number}
		 * @member ns
		 */
		ns.getNumberUniqueId = function () {
			return idNumberCounter++;
		};

		/**
		 * logs supplied messages/arguments
		 * @method log
		 * @static
		 * @member ns
		 */
		ns.log = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.log.apply(console, args);
			}
		};

		/**
		 * logs supplied messages/arguments ad marks it as warning
		 * @method warn
		 * @static
		 * @member ns
		 */
		ns.warn = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.warn.apply(console, args);
			}
		};

		/**
		 * logs supplied messages/arguments and marks it as error
		 * @method error
		 * @static
		 * @member ns
		 */
		ns.error = function () {
			var args = slice.call(arguments);

			infoForLog(args);
			if (console) {
				console.error.apply(console, args);
			}
		};

		/**
		 * get from nsConfig
		 * @method getConfig
		 * @param {string} key
		 * @param {*} [defaultValue] value returned when config is not set
		 * @return {*}
		 * @static
		 * @member ns
		 */
		ns.getConfig = function (key, defaultValue) {
			return nsConfig[key] === undefined ? defaultValue : nsConfig[key];
		};

		/**
		 * set in nsConfig
		 * @method setConfig
		 * @param {string} key
		 * @param {*} value
		 * @param {boolean} [asDefault=false] value should be treated as default (doesn't overwrites
		 * the config[key] if it already exists)
		 * @static
		 * @member ns
		 */
		ns.setConfig = function (key, value, asDefault) {
			if (!asDefault || nsConfig[key] === undefined) {
				nsConfig[key] = value;
			}
		};

		/**
		 * Return path for framework script file.
		 * @method getFrameworkPath
		 * @return {?string}
		 * @member ns
		 */
		ns.getFrameworkPath = function () {
			var scripts = document.getElementsByTagName("script"),
				countScripts = scripts.length,
				i,
				url,
				arrayUrl,
				count;

			for (i = 0; i < countScripts; i++) {
				url = scripts[i].src;
				arrayUrl = url.split("/");
				count = arrayUrl.length;
				if (arrayUrl[count - 1] === fileName + ".js" ||
					arrayUrl[count - 1] === fileName + ".min.js") {
					return arrayUrl.slice(0, count - 1).join("/");
				}
			}
			return null;
		};

		}(window.document, window.console));

/*global window, ns, define, XMLHttpRequest, console, Blob */
/*jslint nomen: true, browser: true, plusplus: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Utilities
 *
 * The Tizen Advanced UI (TAU) framework provides utilities for easy-developing
 * and fully replaceable with jQuery method. When user using these DOM and
 * selector methods, it provide more light logic and it proves performance
 * of web app. The following table displays the utilities provided by the
 * TAU framework.
 *
 * @class ns.util
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var currentFrame = null,
				util = ns.util || {},
				// frames callbacks which should be run in next request animation frame
				waitingFrames = [],
				slice = [].slice,
				// inform that loop was added to request animation frame callback
				loopWork = false;

			/**
			 * Function which is use as workaround when any type of request animation frame not exists
			 * @param {Function} callback
			 * @method _requestAnimationFrameOnSetTimeout
			 * @static
			 * @member ns.util
			 * @protected
			 */
			util._requestAnimationFrameOnSetTimeout = function (callback) {
				currentFrame = window.setTimeout(callback.bind(callback, +new Date()), 1000 / 60);
			};

			/**
			 * Function which support every request animation frame.
			 * @method _loop
			 * @protected
			 * @static
			 * @member ns.util
			 */
			util._loop = function () {
				var loopWaitingFrames = slice.call(waitingFrames),
					currentFrameFunction = loopWaitingFrames.shift(),
					loopTime = performance.now();

				waitingFrames = [];

				while (currentFrameFunction) {
					currentFrameFunction();
					if (performance.now() - loopTime < 15) {
						currentFrameFunction = loopWaitingFrames.shift();
					} else {
						currentFrameFunction = null;
					}
				}
				if (loopWaitingFrames.length || waitingFrames.length) {
					waitingFrames.unshift.apply(waitingFrames, loopWaitingFrames);
					util.windowRequestAnimationFrame(util._loop);
				} else {
					loopWork = false;
				}
			};

			/**
			 * Find browser prefixed request animation frame function.
			 * @method _getRequestAnimationFrame
			 * @protected
			 * @static
			 * @member ns.util
			 */
			util._getRequestAnimationFrame = function () {
				return (window.requestAnimationFrame ||
					window.webkitRequestAnimationFrame ||
					window.mozRequestAnimationFrame ||
					window.oRequestAnimationFrame ||
					window.msRequestAnimationFrame ||
					util._requestAnimationFrameOnSetTimeout).bind(window);
			};

			/**
			 * Original requestAnimationFrame from object window.
			 * @method windowRequestAnimationFrame
			 * @static
			 * @member ns.util
			 */
			util.windowRequestAnimationFrame = util._getRequestAnimationFrame();

			/**
			 * Special requestAnimationFrame function which add functions to queue of callbacks
			 * @method requestAnimationFrame
			 * @static
			 * @member ns.util
			 */
			util.requestAnimationFrame = function (callback) {
				waitingFrames.push(callback);
				if (!loopWork) {
					util.windowRequestAnimationFrame(util._loop);
					loopWork = true;
				}
			};

			util._cancelAnimationFrameOnSetTimeout = function () {
				// probably wont work if there is any more than 1
				// active animationFrame but we are trying anyway
				window.clearTimeout(currentFrame);
			};

			util._getCancelAnimationFrame = function () {
				return (window.cancelAnimationFrame ||
					window.webkitCancelAnimationFrame ||
					window.mozCancelAnimationFrame ||
					window.oCancelAnimationFrame ||
					window.msCancelAnimationFrame ||
					util._cancelAnimationFrameOnSetTimeout).bind(window);
			};

			util.cancelAnimationFrame = util._getCancelAnimationFrame();

			/**
			 * fetchSync retrieves a text document synchronously, returns null on error
			 * @param {string} url
			 * @param {=string} [mime=""] Mime type of the resource
			 * @return {string|null}
			 * @static
			 * @member ns.util
			 */
			function fetchSync(url, mime) {
				var xhr = new XMLHttpRequest(),
					status;

				xhr.open("get", url, false);
				if (mime) {
					xhr.overrideMimeType(mime);
				}
				xhr.send();
				if (xhr.readyState === 4) {
					status = xhr.status;
					if (status === 200 || (status === 0 && xhr.responseText)) {
						return xhr.responseText;
					}
				}

				return null;
			}

			util.fetchSync = fetchSync;

			/**
			 * Removes all script tags with src attribute from document and returns them
			 * @param {HTMLElement} container
			 * @return {Array.<HTMLElement>}
			 * @protected
			 * @static
			 * @member ns.util
			 */
			function removeExternalScripts(container) {
				var scripts = slice.call(container.querySelectorAll("script[src]")),
					i = scripts.length,
					script;

				while (--i >= 0) {
					script = scripts[i];
					script.parentNode.removeChild(script);
				}

				return scripts;
			}

			util._removeExternalScripts = removeExternalScripts;

			/**
			 * Evaluates code, reason for a function is for an atomic call to evaluate code
			 * since most browsers fail to optimize functions with try-catch blocks, so this
			 * minimizes the effect, returns the function to run
			 * @param {string} code
			 * @return {Function}
			 * @static
			 * @member ns.util
			 */
			function safeEvalWrap(code) {
				return function () {
					try {
						window.eval(code);
					} catch (e) {
						if (e.stack) {
							ns.error(e.stack);
						} else if (e.name && e.message) {
							ns.error(e.name, e.message);
						} else {
							ns.error(e);
						}
					}
				};
			}

			util.safeEvalWrap = safeEvalWrap;

			/**
			 * Calls functions in supplied queue (array)
			 * @param {Array.<Function>} functionQueue
			 * @static
			 * @member ns.util
			 */
			function batchCall(functionQueue) {
				var i,
					length = functionQueue.length;

				for (i = 0; i < length; ++i) {
					functionQueue[i]();
				}
			}

			util.batchCall = batchCall;

			/**
			 * Creates new script elements for scripts gathered from a different document
			 * instance, blocks asynchronous evaluation (by renaming src attribute) and
			 * returns an array of functions to run to evaluate those scripts
			 * @param {Array.<HTMLElement>} scripts
			 * @param {HTMLElement} container
			 * @return {Array.<Function>}
			 * @protected
			 * @static
			 * @member ns.util
			 */
			function createScriptsSync(scripts, container) {
				var scriptElement,
					scriptBody,
					i,
					length,
					queue = [];

				// proper order of execution
				for (i = 0, length = scripts.length; i < length; ++i) {
					scriptBody = util.fetchSync(scripts[i].src, "text/plain");
					if (scriptBody) {
						scriptElement = document.adoptNode(scripts[i]);
						scriptElement.setAttribute("data-src", scripts[i].src);
						scriptElement.removeAttribute("src"); // block evaluation
						queue.push(util.safeEvalWrap(scriptBody));
						if (container) {
							container.appendChild(scriptElement);
						}
					}
				}

				return queue;
			}

			util._createScriptsSync = createScriptsSync;

			function removeInlineScripts(element) {
				var result = [],
					script;

				[].slice.call(element.querySelectorAll(
					"script:not([data-src]):not([type]):not([id]):not([src])"
					)).forEach(function (item) {
						script = document.createElement("script");
						script.innerText = item.textContent;
						item.parentNode.removeChild(item);
						result.push(script);
					});

				return result;
			}

			util._removeInlineScripts = removeInlineScripts;

			/**
			 * Method make asynchronous call of function
			 * @method async
			 * @inheritdoc #requestAnimationFrame
			 * @member ns.util
			 * @static
			 */
			util.async = util.requestAnimationFrame;

			/**
			 * Appends element from different document instance to current document in the
			 * container element and evaluates scripts (synchronously)
			 * @param {HTMLElement} element
			 * @param {HTMLElement} container
			 * @return {HTMLElement}
			 * @method importEvaluateAndAppendElement
			 * @member ns.util
			 * @static
			 */
			util.importEvaluateAndAppendElement = function (element, container) {
				var externalScriptsQueue =
						util._createScriptsSync(util._removeExternalScripts(element), element),
					inlineScripts = util._removeInlineScripts(element),
					newNode = document.importNode(element, true);

				container.appendChild(newNode); // append and eval inline
				inlineScripts.forEach(function (script) {
					container.appendChild(script);
				});
				util.batchCall(externalScriptsQueue);

				return newNode;
			};

			/**
			 * Checks if specified string is a number or not
			 * @method isNumber
			 * @param {string} query
			 * @return {boolean}
			 * @member ns.util
			 * @static
			 */
			util.isNumber = function (query) {
				var parsed = parseFloat(query);

				return !isNaN(parsed) && isFinite(parsed);
			};

			/**
			 * Reappear script tags to DOM structure to correct run script
			 * @method runScript
			 * @param {string} baseUrl
			 * @param {HTMLScriptElement} script
			 * @member ns.util
			 * @deprecated 2.3
			 */
			util.runScript = function (baseUrl, script) {
				var newScript = document.createElement("script"),
					scriptData,
					i,
					scriptAttributes = slice.call(script.attributes),
					src = script.getAttribute("src"),
					attribute,
					status;

				// 'src' may become null when none src attribute is set
				if (src !== null) {
					src = util.path.makeUrlAbsolute(src, baseUrl);
				}

				//Copy script tag attributes
				i = scriptAttributes.length;
				while (--i >= 0) {
					attribute = scriptAttributes[i];
					if (attribute.name !== "src") {
						newScript.setAttribute(attribute.name, attribute.value);
					} else {
						newScript.setAttribute("data-src", attribute.value);
					}
				}

				if (src) {
					scriptData = util.fetchSync(src, "text/plain");
									} else {
					scriptData = script.textContent;
				}

				if (scriptData) {
					// add the returned content to a newly created script tag
					newScript.src = window.URL.createObjectURL(new Blob([scriptData], {type: "text/javascript"}));
					newScript.textContent = scriptData; // for compatibility with some libs ex. template systems
				}
				script.parentNode.replaceChild(newScript, script);
			};

			ns.util = util;
			}(window, window.document, ns));

/*global define*/
(function () {
	"use strict";
			/*eslint-disable*/
		/**
		 * BezierEasing - use bezier curve for transition easing function
		 * by Gaëtan Renaudeau 2014 - 2015 – MIT License
		 *
		 * Credits: is based on Firefox's nsSMILKeySpline.cpp
		 * Usage:
		 * var spline = BezierEasing([ 0.25, 0.1, 0.25, 1.0 ])
		 * spline.get(x) => returns the easing value | x must be in [0, 1] range
		 *
		 * @class utils.BezierCurve
		 */


			// These values are established by empiricism with tests (tradeoff: performance VS precision)
		var NEWTON_ITERATIONS = 4;
		var NEWTON_MIN_SLOPE = 0.001;
		var SUBDIVISION_PRECISION = 0.0000001;
		var SUBDIVISION_MAX_ITERATIONS = 10;

		var kSplineTableSize = 11;
		var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);

		var float32ArraySupported = typeof Float32Array === "function";

		/**
		 *
		 * @param aA1
		 * @param aA2
		 * @returns {number}
		 */
		function a (aA1, aA2) {
			return 1.0 - 3.0 * aA2 + 3.0 * aA1;
		}

		/**
		 *
		 * @param aA1
		 * @param aA2
		 * @returns {number}
		 */
		function b (aA1, aA2) {
			return 3.0 * aA2 - 6.0 * aA1;
		}

		/**
		 *
		 * @param aA1
		 * @returns {number}
		 */
		function c (aA1) {
			return 3.0 * aA1;
		}

		/**
		 * Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
		 * @param aT
		 * @param aA1
		 * @param aA2
		 * @returns {number}
		 */
		function calcBezier (aT, aA1, aA2) {
			return ((a(aA1, aA2)*aT + b(aA1, aA2))*aT + c(aA1))*aT;
		}


		/**
		 * Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
		 * @param aT
		 * @param aA1
		 * @param aA2
		 * @returns {*}
		 */
		function getSlope (aT, aA1, aA2) {
			return 3.0 * a(aA1, aA2)*aT*aT + 2.0 * b(aA1, aA2) * aT + c(aA1);
		}

		/**
		 *
		 * @param aX
		 * @param aA
		 * @param aB
		 * @param mX1
		 * @param mX2
		 * @returns {*}
		 */
		function binarySubdivide (aX, aA, aB, mX1, mX2) {
			var currentX, currentT, i = 0;
			do {
				currentT = aA + (aB - aA) / 2.0;
				currentX = calcBezier(currentT, mX1, mX2) - aX;
				if (currentX > 0.0) {
					aB = currentT;
				} else {
					aA = currentT;
				}
			} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
			return currentT;
		}

		/**
		 *
		 * @param aX
		 * @param aGuessT
		 * @param mX1
		 * @param mX2
		 * @returns {*}
		 */
		function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
			for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
				var currentSlope = getSlope(aGuessT, mX1, mX2);
				if (currentSlope === 0.0) {
					return aGuessT;
				}
				var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
				aGuessT -= currentX / currentSlope;
			}
			return aGuessT;
		}

		function validateArguments(points) {
			if (!points || points.length !== 4) {
				throw new Error("BezierEasing: points must contains 4 values");
			}
			for (var i = 0; i < 4; ++i) {
				if (typeof points[i] !== "number" || isNaN(points[i]) || !isFinite(points[i])) {
					throw new Error("BezierEasing: points should be integers.");
				}
			}
			if (points[0] < 0 || points[0] > 1 || points[2] < 0 || points[2] > 1) {
				throw new Error("BezierEasing x values must be in [0, 1] range.");
			}
		}

		/**
		 * points is an array of [ mX1, mY1, mX2, mY2 ]
		 * @param points
		 * @param _b
		 * @param _c
		 * @param _d
		 * @returns {BezierEasing}
		 * @constructor
		 */
		function BezierEasing (points, _b, _c, _d) {
			if (arguments.length === 4) {
				return new BezierEasing([points, _b, _c, _d]);
			}
			if (!(this instanceof BezierEasing)) {
				return new BezierEasing(points);
			}

			validateArguments(points);

			this._str = "BezierEasing(" + points + ")";
			this._css = "cubic-bezier(" + points + ")";
			this._p = points;
			this._mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : [];
			this._precomputed = false;

			this.get = this.get.bind(this);
			return this;
		}

		BezierEasing.prototype = {

			/**
			 *
			 * @param x
			 * @returns {*}
			 */
			get: function (x) {
				var mX1 = this._p[0],
					mY1 = this._p[1],
					mX2 = this._p[2],
					mY2 = this._p[3];
				if (!this._precomputed) {
					this._precompute();
				}
				if (mX1 === mY1 && mX2 === mY2) {
					return x;
				} // linear
				// Because JavaScript number are imprecise, we should guarantee the extremes are right.
				if (x <= 0) {
					return 0;
				}
				if (x >= 1) {
					return 1;
				}
				return calcBezier(this._getTForX(x), mY1, mY2);
			},

			/**
			 *
			 * @private
			 */
			_precompute: function () {
				var mX1 = this._p[0],
					mY1 = this._p[1],
					mX2 = this._p[2],
					mY2 = this._p[3];
				this._precomputed = true;
				if (mX1 !== mY1 || mX2 !== mY2) {
					this._calcSampleValues();
				}
			},

			/**
			 *
			 * @private
			 */
			_calcSampleValues: function () {
				var mX1 = this._p[0],
					mX2 = this._p[2];
				for (var i = 0; i < kSplineTableSize; ++i) {
					this._mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
				}
			},

			/**
			 * getTForX chose the fastest heuristic to determine the percentage value precisely from a
			 * given X projection.
			 * @param aX
			 * @returns {*}
			 * @private
			 */
			_getTForX: function (aX) {
				var mX1 = this._p[0],
					mX2 = this._p[2],
					mSampleValues = this._mSampleValues;

				var intervalStart = 0.0;
				var currentSample = 1;
				var lastSample = kSplineTableSize - 1;

				for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX;
				       ++currentSample) {
					intervalStart += kSampleStepSize;
				}
				--currentSample;

				// Interpolate to provide an initial guess for t
				var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] -
					mSampleValues[currentSample]);
				var guessForT = intervalStart + dist * kSampleStepSize;

				var initialSlope = getSlope(guessForT, mX1, mX2);
				if (initialSlope >= NEWTON_MIN_SLOPE) {
					return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
				} else if (initialSlope === 0.0) {
					return guessForT;
				} else {
					return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
				}
			}
		};

		// CSS mapping
		BezierEasing.css = {
			ease:        BezierEasing.ease = new BezierEasing(0.25, 0.1, 0.25, 1.0),
			easeIn:     BezierEasing.easeIn = new BezierEasing(0.42, 0.0, 1.00, 1.0),
			easeOut:    BezierEasing.easeOut = new BezierEasing(0.00, 0.0, 0.58, 1.0),
			easeInOut: BezierEasing.easeInOut = new BezierEasing(0.42, 0.0, 0.58, 1.0)
		};

		if (ns && ns.util) {
			ns.util.bezierCurve = BezierEasing;
		}

		}());

/* global requestAnimationFrame, define, ns */
/**
 * Main file of applications, which connect other parts
 */
// then we can load plugins for libraries and application
(function (window, document, ns) {
	"use strict";
				var utils = ns.util,
				requestAnimationFrame = utils.requestAnimationFrame,
			/**
				 * Util to change value of object property in given time
				 * @class Animation
				 */
				Animate = function (object) {
					var self = this;

					self._object = object;
					self._animate = {
						chain: [],
						chainIndex: 0
					};
					// This is used to keep track of elapsed time of paused animation
					self._pausedTimeDiff = null;
					self._animateConfig = null;
				},
				linear = function (x, a, b) {
					a = (a === undefined) ? 1 : a;
					b = (b === undefined) ? 0 : b;
					return x * (a || 0) + (b || 0);
				},
				inverseTiming = function (x) {
					return 1 - x;
				},
				prototype = {};

			utils.bezierCurve = utils.bezierCurve || bezierCurve;

			Animate.prototype = prototype;

			Animate.timing = {
				linear: linear,
				ease: utils.bezierCurve.ease.get,
				easeInOut: utils.bezierCurve.easeInOut.get
			};

			function firstDefined() {
				var args = [].slice.call(arguments),
					i = 0,
					length = args.length,
					arg;

				for (; i < length; i++) {
					arg = args[i];
					if (arg !== undefined) {
						return arg;
					}
				}
				return null;
			}

			prototype.destroy = function () {
				var self = this;

				self._object = null;
				self._animate = null;
				self._animateConfig = null;
			};

			function calculateSteps(option, currentPoint) {
				var percent,
					step,
					steps = option.steps,
					from = option.from,
					to = null,
					percentStart = 0,
					percentStop = 100,
					floatPoint;

				for (percent in steps) {
					if (steps.hasOwnProperty(percent)) {
						step = steps[percent];
						floatPoint = percent / 100;
						if (currentPoint >= floatPoint) {
							from = step;
							percentStart = floatPoint;
						} else if (to === null) {
							to = step;
							percentStop = floatPoint;
						}
					}
				}
				return from + (currentPoint - percentStart) / (percentStop - percentStart) *
					(to - from);
			}

			function eachOption(config, animateConfig, option) {
				var propertyObject,
					from,
					steps = option.steps || config.steps;

				option.duration = firstDefined(option.duration, config.duration);
				option.delay = firstDefined(option.delay, config.delay, 0);
				propertyObject = firstDefined(option.object, this._object);
				option.simpleProperty = option.property;
				option.property.split(".").forEach(function (property) {
					if (typeof propertyObject[property] === "object" && propertyObject[property] !== null) {
						propertyObject = propertyObject[property];
						option.propertyObject = propertyObject;
					} else {
						option.simpleProperty = property;
					}
				});
				option.propertyObject = propertyObject;
				if (steps) {
					option.calculate = calculateSteps.bind(null, option);
					steps[0] = firstDefined(steps[0], option.from, propertyObject[option.simpleProperty]);
					option.from = steps["0"];
					option.to = firstDefined(steps["100"], option.to);
					option.diff = 0;
					option.current = steps[0];
					option.direction = option.from < option.to ? 1 : -1;
				} else {
					option.calculate = option.calculate || linear;
					from = firstDefined(option.from, propertyObject[option.simpleProperty]);
					option.from = from;
					option.diff = (option.to - from);
					option.current = from;
					option.direction = from < option.to ? 1 : -1;
				}

				// calculate value change in full time
				option.startTime = Date.now() + option.delay;

				if (this._pausedTimeDiff) {
					option.startTime = Date.now() - this._pausedTimeDiff;
					this._pausedTimeDiff = 0;
				}
				// save last time of recalculate options
				option.lastCalculationTime = option.startTime;
				// set timing function
				option.timing = firstDefined(option.timing, config.timing, linear);

				animateConfig.push(option);
			}

			prototype._initAnimate = function () {
				var self = this,
					animateConfig = [],
					options = self._animate.chain[self._animate.chainIndex++];

				if (options) {
					options.forEach(eachOption.bind(self, self._config, animateConfig));
					self._animateConfig = animateConfig;
				} else {
					self._animateConfig = null;
				}
			};

			function animateLoopCallback(self, copiedArgs) {
				if (self._animate) {
					self._animate.chain = [].slice.call(copiedArgs);
					self.start();
				}
			}

			function animateRevertCallback(self, copiedArgs) {
				var chain = [].slice.call(copiedArgs),
					newChain = [];

				chain.forEach(function (options) {
					newChain.unshift(options);
					options.forEach(function (option) {
						option.timing = inverseTiming;
					});
				});
				self._animate.chain = newChain;
				self._animate.callback = null;
				self.start();
			}

			/**
			 * Set animate
			 * @param {Object...} options list of animations configs
			 * @return {Animate}
			 */
			prototype.set = function (options) {
				var self = this,
					config,
				// converts arguments to array
					args = [].slice.call(arguments),
					copiedArgs;

				// we get last argument
				config = args.pop();

				if (!Array.isArray(config)) {
				// if last arguments is object then we use it as global animation config
					self._animate.config = config;
				} else {
				// otherwise this is description of one animation loop and back to args array
					args.push(config);
					config = null;
				}

				self._config = config;

				// copy array to be sure that we have new reference objects
				copiedArgs = [].slice.call(args);

				if (config) {
					if (config.loop && config.duration > 0) {
					// when animation is in loop then we create callback on animation and to restart animation
						self._animate.callback = animateLoopCallback.bind(null, self, copiedArgs);
					} else if (config.withRevert) {
						self._animate.callback = animateRevertCallback.bind(null, self, copiedArgs);
					} else {
					// otherwise we use callback from options
						self._animate.callback = options.callback || config.callback;
					}
				}

				// cache options in object
				self._animate.chain = args;

				return self;
			};

			/**
			 * Start animation
			 * @param {Function} [callback] function called after finish animation
			 */
			prototype.start = function (callback) {
				var self = this;

				self.active = true;
			// init animate options
				self._initAnimate();

			// setting callback function
				callback = self._animate.callback || callback;

				if (self._animate.chainIndex < self._animate.chain.length) {
				// if we have many animations in chain that we set callback
				// to start next animation from chain after finish current
				// animation
					self._animationTimeout = self._calculateAnimate.bind(self, self.start.bind(self, callback));
				} else {
					self._animationTimeout = self._calculateAnimate.bind(self, callback);
				}
				self._calculateAnimate(callback);
				return self;
			};

			/**
			 * Stop animations
			 */
			prototype.stop = function () {
				var self = this;

				self.active = false;
				// reset index of animations chain
				self._animate.chainIndex = 0;
				// reset current animation config
				self._animateConfig = null;
			// clear timeout
				self._animationTimeout = null;
				return self;
			};

			prototype.pause = function () {
				var self = this;

				self.active = false;
				if (self._animateConfig) {
					self._pausedTimeDiff = Date.now() - self._animateConfig[0].startTime;
					self.stop();
				}
			};

			function calculateOption(option, time) {
				var timeDiff,
					current = null;

				if (option && option.startTime < time) {
				// if option is not delayed
					timeDiff = time - option.startTime;

					if (timeDiff >= option.duration) {
						// if current is bigger then end we finish loop and we take next animate from chain
						timeDiff = option.duration;
						if (option.callback) {
							option.callback();
						}
					}

					if (option.duration > 0) {
						current = option.calculate(
							option.timing(timeDiff / option.duration),
							option.diff,
							option.from,
							option.current
						);
					}

					if (current !== null) {
						option.current = current;
						// we set next calculation time
						option.propertyObject[option.simpleProperty] = option.current;
						if (timeDiff >= option.duration) {
							// inform about remove animation config
							return 2;
						}
						// inform widget about redraw
						return 1;
					}

					if (timeDiff >= option.duration) {
						// inform about remove animation config
						return 2;
					}
				}
				return 0;
			}

			/**
			 * Method called in loop to calculate current state of animation
			 * @param {Function} callback
			 * @private
			 */
			prototype._calculateAnimate = function (callback) {
				var self = this,
					// current animation config
					animateConfig = self._animateConfig,
					// number of animations which is not finished
					notFinishedAnimationsCount,
					// flag inform that redraw is necessary
					redraw = false,
					i = 0,
					length,
					time = Date.now(),
					calculatedOption;

				if (animateConfig) {
					notFinishedAnimationsCount = animateConfig.length;
					length = animateConfig.length;

					// calculating options changed in animation
					while (i < length) {
						if (animateConfig[i].duration > 0) {
							calculatedOption = calculateOption(animateConfig[i], time);
							if (calculatedOption === 2) {
								notFinishedAnimationsCount--;
								// remove current config and recalculate loop arguments
								animateConfig.splice(i, 1);
								length--;
								i--;
								redraw = true;
							} else if (calculatedOption === 1) {
								redraw = true;
							}
						} else {
							notFinishedAnimationsCount--;
						}
						i++;
					}
					// redraw is necessary
					if (redraw && self._tickFunction) {
						self._tickFunction(self._object);
					}
					if (notFinishedAnimationsCount) {
					// setting next loop state
						if (self._animationTimeout) {
							requestAnimationFrame(self._animationTimeout);
						}
					} else {
						// Animation state can be change to "stopped"
						self.stop();
						// animation is finished
						if (callback) {
							callback();
						}
					}
				}
			};

		/**
			 * Set function which will be called after animation change property of object
			 * @param {Function} tickFunction
			 * @return {Animation}
			 */
			prototype.tick = function (tickFunction) {
				var oldTickFunction = this._tickFunction;

				if (oldTickFunction) {
					this._tickFunction = function (object) {
						oldTickFunction(object);
						tickFunction(object);
					};
				} else {
					this._tickFunction = tickFunction;
				}
				return this;
			};

			utils.Animate = Animate;
		}(window, window.document, ns));


/*global window, define, console, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Marquee
 * Shows a component which moves left and right.
 *
 * It makes <div> element with text move horizontally like legacy <marquee> tag
 *
 * ## Make Marquee Element
 * If you want to use Marquee widget, you have to declare below attributes in <div> element and make
 * Marquee widget in JS code.
 * To use a Marquee widget in your application, use the following code:
 *
 *    @example
 *    <div class="ui-content">
 *        <ul class="ui-listview">
 *            <li><div class="ui-marquee" id="marquee">Marquee widget code sample</div></li>
 *        </ul>
 *    </div>
 *    <script>
 *        var marqueeEl = document.getElementById("marquee"),
 *            marqueeWidget = new tau.widget.Marquee(marqueeEl,
 *              {marqueeStyle: "scroll", delay: "3000"});
 *    </script>
 *
 * @author Heeju Joo <heeju.joo@samsung.com>
 * @class ns.widget.core.Marquee
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
	
			var BaseWidget = ns.widget.BaseWidget,
				/**
				 * Alias for class ns.engine
				 * @property {ns.engine} engine
				 * @member ns.widget.core.Marquee
				 * @private
				 */
				engine = ns.engine,
				/**
				 * Alias for class ns.util.object
				 * @property {Object} objectUtils
				 * @member ns.widget.core.Marquee
				 * @private
				 */
				objectUtils = ns.util.object,

				Animation = ns.util.Animate,

				states = {
					RUNNING: "running",
					STOPPED: "stopped",
					IDLE: "idle"
				},

				Marquee = function () {
					this.options = objectUtils.copy(Marquee.defaults);
					// event callbacks
					this._callbacks = {};
				},

				prototype = new BaseWidget(),

				CLASSES_PREFIX = "ui-marquee",

				eventType = {
					/**
					 * Triggered when the marquee animation end.
					 * @event marqueeend
					 * @member ns.widget.core.Marquee
					 */
					MARQUEE_START: "marqueestart",
					MARQUEE_END: "marqueeend",
					MARQUEE_STOPPED: "marqueestopped"
				},
				/**
				 * Dictionary for CSS class of marquee play state
				 * @property {Object} classes
				 * @member ns.widget.core.Marquee
				 * @static
				 */
				classes = {
					MARQUEE_CONTENT: CLASSES_PREFIX + "-content",
					MARQUEE_GRADIENT: CLASSES_PREFIX + "-gradient",
					MARQUEE_ELLIPSIS: CLASSES_PREFIX + "-ellipsis",
					ANIMATION_RUNNING: CLASSES_PREFIX + "-anim-running",
					ANIMATION_STOPPED: CLASSES_PREFIX + "-anim-stopped",
					ANIMATION_IDLE: CLASSES_PREFIX + "-anim-idle"
				},

				/**
				 * Dictionary for marquee style
				 */
				style = {
					SCROLL: "scroll",
					SLIDE: "slide",
					ALTERNATE: "alternate",
					ENDTOEND: "endToEnd"
				},

				ellipsisEffect = {
					GRADIENT: "gradient",
					ELLIPSIS: "ellipsis",
					NONE: "none"
				},

				round100 = function (value) {
					return Math.round(value * 100) / 100;
				},

				/**
				 * Options for widget
				 * @property {Object} options
				 * @property {string|"slide"|"scroll"|"alternate"} [options.marqueeStyle="slide"] Sets the
				 * default style for the marquee
				 * @property {number} [options.speed=60] Sets the speed(px/sec) for the marquee
				 * @property {number|"infinite"} [options.iteration=1] Sets the iteration count number for
				 * marquee
				 * @property {number} [options.delay=2000] Sets the delay(ms) for marquee
				 * @property {"linear"|"ease"|"ease-in"|"ease-out"|"cubic-bezier(n,n,n,n)"}
				 * [options.timingFunction="linear"] Sets the timing function for marquee
				 * @property {"gradient"|"ellipsis"|"none"} [options.ellipsisEffect="gradient"] Sets the
				 * end-effect(gradient) of marquee
				 * @property {boolean} [options.autoRun=true] Sets the status of autoRun
				 * @member ns.widget.core.Marquee
				 * @static
				 */
				defaults = {
					marqueeStyle: style.SLIDE,
					speed: 60,
					iteration: 1,
					currentIteration: 1,
					delay: 0,
					timingFunction: "linear",
					ellipsisEffect: ellipsisEffect.GRADIENT,
					runOnlyOnEllipsisText: true,
					animation: states.STOPPED,
					autoRun: true
				},
				GRADIENTS = {
					LEFT: "-webkit-linear-gradient(left, transparent 0, rgb(255, 255, 255) 15%," +
					" rgb(255, 255, 255) 100%)",
					BOTH: "-webkit-linear-gradient(left, transparent 0, rgb(255, 255, 255)" +
					" 15%, rgb(255, 255, 255) 85%, transparent 100%",
					RIGHT: "-webkit-linear-gradient(left, rgb(255, 255, 255) 0, rgb(255," +
					" 255, 255) 85%, transparent 100%)"
				};

			Marquee.classes = classes;
			Marquee.defaults = defaults;

			prototype._calculateTranslateFunctions = {
				scroll: function (self, state, diff, from, current) {
					var value = from + state * diff,
						returnValue;

					returnValue = "translateX(-" + round100(value) + "px)";
					if (current === returnValue) {
						return null;
					}
					return returnValue;
				},
				slide: function (self, state, diff, from, current) {
					var stateDOM = self._stateDOM,
						containerWidth = stateDOM.offsetWidth,
						textWidth = stateDOM.children[0].offsetWidth,
						value,
						returnValue;

					value = state * (textWidth - containerWidth);
					returnValue = "translateX(-" + round100(value) + "px)";
					if (current === returnValue) {
						return null;
					}
					return returnValue;
				},
				alternate: function (self, state, diff, from, current) {
					var stateDOM = self._stateDOM,
						containerWidth = stateDOM.offsetWidth,
						textWidth = stateDOM.children[0].offsetWidth,
						value = from + state * diff,
						returnValue;

					if (value > textWidth / 2) {
						value = textWidth - (value - textWidth / 2) * 2;
					} else {
						value *= 2;
					}
					value = value / textWidth * (textWidth - containerWidth);
					returnValue = "translateX(-" + round100(value) + "px)";
					if (current === returnValue) {
						return null;
					}
					return returnValue;
				},
				endToEnd: function (self, state, diff, from, current) {
					var stateDOM = self._stateDOM,
						textWidth = stateDOM.children[0].offsetWidth,
						containerWidth = stateDOM.offsetWidth,
						value,
						returnValue;

					value = state * (textWidth + containerWidth);
					if (value > textWidth) {
						value = containerWidth - value + textWidth;
					} else {
						value = -value;
					}
					returnValue = "translateX(" + round100(value) + "px)";
					if (current === returnValue) {
						return null;
					}
					return returnValue;
				}
			};

			prototype._calculateEndToEndGradient = function (state, diff, from, current) {
				var self = this,
					stateDOM = self._stateDOM,
					textWidth = stateDOM.children[0].offsetWidth,
					containerWidth = stateDOM.offsetWidth,
					returnTimeFrame = (textWidth / (textWidth + containerWidth)),
					returnValue;

				if (self.options.ellipsisEffect === "none") {
					return null;
				}
				if (state > returnTimeFrame) {
					returnValue = GRADIENTS.RIGHT;
				} else if (state > 0) {
					returnValue = GRADIENTS.BOTH;
				} else {
					returnValue = GRADIENTS.LEFT;
				}

				if (current === returnValue) {
					return null;
				}
				return returnValue;
			};

			prototype._calculateStandardGradient = function (state, diff, from, current) {
				var returnValue;

				if (isNaN(state)) {
					return null;
				}
				if (this.options.ellipsisEffect === "none") {
					return null;
				}
				if (state === 1) {
					returnValue = GRADIENTS.LEFT;
				} else if (state > 0) {
					returnValue = GRADIENTS.BOTH;
				} else {
					returnValue = GRADIENTS.RIGHT;
				}

				if (current === returnValue) {
					return null;
				}
				return returnValue;
			};

			/**
			 * Build Marquee DOM
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.Marquee
			 */
			prototype._build = function (element) {
				var marqueeInnerElement = element.querySelector("." + classes.MARQUEE_CONTENT);

				if (!marqueeInnerElement) {
					marqueeInnerElement = document.createElement("div");

					while (element.hasChildNodes()) {
						marqueeInnerElement.appendChild(element.removeChild(element.firstChild));
					}
					marqueeInnerElement.classList.add(classes.MARQUEE_CONTENT);
					element.appendChild(marqueeInnerElement);
				}
				return element;
			};

			prototype._initStateDOMstructure = function () {
				this._stateDOM = {
					classList: [],
					offsetWidth: null,
					style: {
						webkitMaskImage: null
					},
					children: [
						{
							offsetWidth: null,
							style: {
								webkitTransform: null
							}
						}
					]
				};
			};

			prototype._initAnimation = function () {
				var self = this,
					stateDOM = self._stateDOM,
					stateDOMfirstChild = stateDOM.children[0],
					width = stateDOMfirstChild.offsetWidth,
					animation = new Animation({}),
					state = {
						hasEllipsisText: (width > 0),
						animation: [{
							object: stateDOMfirstChild.style,
							property: "webkitTransform",
							calculate: self._calculateTranslateFunctions.scroll.bind(null, self),
							from: 0,
							to: width
						}, {
							object: stateDOM.style,
							calculate: self._calculateStandardGradient.bind(self),
							property: "webkitMaskImage",
							from: 0,
							to: 1
						}],
						animationConfig: {
							duration: width / self.options.speed * 1000,
							timing: Animation.timing.linear
						}
					};

				self.state = state;

				animation.tick(self._render.bind(self, true));

				self._animation = animation;
			};

			/**
			 * Init Marquee Style
			 * @method _init
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.Marquee
			 */
			prototype._init = function (element) {
				var self = this;

				self._initStateDOMstructure();
				self._initDOMstate();
				self._initAnimation();
				self.option(self.options);

				return element;
			};

			prototype._setEllipsisEffect = function (element, value) {
				return this._togglePrefixedClass(this._stateDOM, CLASSES_PREFIX + "-", value);
			};

			prototype._updateDuration = function () {
				var self = this,
					stateDOM = self._stateDOM,
					state = self.state,
					firstChild = stateDOM.children[0],
					width = firstChild.offsetWidth,
					dWidth = width - stateDOM.offsetWidth,
					animationConfig = state.animationConfig;

				animationConfig.duration = (dWidth > 0) ?
					width / self.options.speed * 1000 :
					0;
				self._animation.set(state.animation, animationConfig);
			};

			prototype._setSpeed = function (element, value) {
				var self = this;

				self.options.speed = parseInt(value, 10);
				self._updateDuration();
				return false;
			};

			function animationIterationCallback(self) {
				var animation = self._animation,
					state = self.state;

				if (self.options.currentIteration++ < self.options.iteration) {
					animation.set(state.animation, state.animationConfig);
					animation.stop();
					animation.start();
				} else {
					self.options.animation = states.STOPPED;
					self.trigger(eventType.MARQUEE_END);
				}
			}

			prototype._setIteration = function (element, value) {
				var self = this,
					state = self.state,
					animationConfig = state.animationConfig;

				if (value === "infinite") {
					animationConfig.loop = true;
					animationConfig.callback = function () {
						self.options.animation = states.STOPPED;
						self.trigger.bind(self, eventType.MARQUEE_END);
					};
				} else {
					value = parseInt(value, 10);
					self.options.currentIteration = 1;
					animationConfig.loop = false;
					animationConfig.callback = animationIterationCallback.bind(null, self);
				}
				self._animation.set(state.animation, animationConfig);
				self.options.loop = value;
				return false;
			};

			prototype._setDelay = function (element, value) {
				var self = this,
					state = self.state,
					animationConfig = state.animationConfig;

				value = parseInt(value, 10);
				animationConfig.delay = value;
				self._animation.set(state.animation, animationConfig);
				self.options.delay = value;
				return false;
			};

			prototype._setTimingFunction = function (element, value) {
				var self = this,
					state = self.state,
					animationConfig = state.animationConfig;

				animationConfig.timing = Animation.timing[value];
				self._animation.set(state.animation, animationConfig);
				self.options.timing = value;
				return false;
			};

			prototype._setAutoRun = function (element, value) {
				if (value) {
					this.start();
				}
				return false;
			};

			prototype._setAnimation = function (element, value) {
				var self = this,
					animation = self._animation,
					stateDOM = self._stateDOM,
					options = self.options,
					width = stateDOM.children[0].offsetWidth - stateDOM.offsetWidth,
					runOnlyOnEllipsisText = options.runOnlyOnEllipsisText;

				if (value !== options.animation) {
					if (value === states.RUNNING) {
						if ((runOnlyOnEllipsisText && width) || (!runOnlyOnEllipsisText)) {
							self.options.currentIteration = 1;
							animation.start();
							self.trigger(eventType.MARQUEE_START);
						}
					} else {
						animation.pause();
						self.trigger(eventType.MARQUEE_STOPPED);
					}
					options.animation = value;
				}
				return false;
			};

			prototype._setMarqueeStyle = function (element, value) {
				var self = this,
					animation = self.state.animation;

				animation[0].calculate = self._calculateTranslateFunctions[value].bind(null, self);
				if (value === "endToEnd") {
					animation[1].calculate = self._calculateEndToEndGradient.bind(self);
				} else {
					animation[1].calculate = self._calculateStandardGradient.bind(self);
				}
				self.options.marqueeStyle = value;
				return false;
			};

			/**
			 * Destroy widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.Marquee
			 */
			prototype._destroy = function () {
				var self = this,
					marqueeInnerElement;

				self.state = null;
				self._animation.stop();
				self._animation.destroy();
				self._animation = null;
				self.element.classList.remove(classes.MARQUEE_GRADIENT);
				self.element.style.webkitMaskImage = "";

				marqueeInnerElement = self.element.querySelector("." + classes.MARQUEE_CONTENT);
				if (marqueeInnerElement) {
					while (marqueeInnerElement.hasChildNodes()) {
						self.element.appendChild(marqueeInnerElement.removeChild(marqueeInnerElement.firstChild));
					}
					self._stateDOM.children = [];
					self.element.removeChild(marqueeInnerElement);
				}
				self._stateDOM = null;
			};

			/**
			 * Start Marquee animation
			 *
			 * #####Running example in pure JavaScript:
			 *
			 *    @example
			 *    <div class="ui-marquee" id="marquee">
			 *        <p>MarqueeTEST TEST message TEST for marquee</p>
			 *    </div>
			 *    <script>
			 *        var marqueeWidget = tau.widget.Marquee(document.getElementById("marquee"));
			 *        marqueeWidget.start();
			 *    </script>
			 *
			 * @method start
			 * @member ns.widget.core.Marquee
			 */
			prototype.start = function () {
				this.option("animation", "running");
			};

			/**
			 * Pause Marquee animation
			 *
			 * #####Running example in pure JavaScript:
			 *    @example
			 *    <div class="ui-marquee" id="marquee">
			 *        <p>MarqueeTEST TEST message TEST for marquee</p>
			 *    </div>
			 *    <script>
			 *        var marqueeWidget = tau.widget.Marquee(document.getElementById("marquee"));
			 *        marqueeWidget.stop();
			 *    </script>
			 *
			 * @method stop
			 * @member ns.widget.core.Marquee
			 */
			prototype.stop = function () {
				var self = this,
					animation = self._animation;

				animation.pause();
				this.option("animation", "stopped");
			};

			/**
			 * Reset Marquee animation
			 *
			 * #####Running example in pure JavaScript:
			 *    @example
			 *    <div class="ui-marquee" id="marquee">
			 *        <p>MarqueeTEST TEST message TEST for marquee</p>
			 *    </div>
			 *    <script>
			 *        var marqueeWidget = tau.widget.Marquee(document.getElementById("marquee"));
			 *        marqueeWidget.reset();
			 *    </script>
			 *
			 * @method reset
			 * @member ns.widget.core.Marquee
			 */
			prototype.reset = function () {
				var self = this,
					stateDOM = self._stateDOM;

				this.option("animation", "stopped");
				stateDOM.style.webkitMaskImage = (this.options.ellipsisEffect === "none") ? "" : GRADIENTS.RIGHT;
				stateDOM.children[0].style.webkitTransform = "translateX(0)";
				self._render();
			};

			Marquee.prototype = prototype;
			ns.widget.core.Marquee = Marquee;

			engine.defineWidget(
				"Marquee",
				".ui-marquee",
				["start", "stop", "reset"],
				Marquee,
				"core"
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Tab Bar Widget
 * The tabbar widget shows an unordered list of tabs on the screen wrapped
 * together in a single group.
 *
 * This widget can be placed in at top of page inside Tabs widget.
 *
 * ## Default selectors
 * In default elements matches to:
 *
 *  - HTML elements with data-role="tabbar"
 *  - HTML elements with class ui-tabbar
 *
 * ###HTML Examples
 *
 * ####Create simple tab bar in header
 *
 *        @example
 *        <div data-role="page">
 *            <div data-role="header">
 *                <div data-role="tabbar">
 *                    <ul>
 *                        <li><a data-icon="naviframe-edit">Tabbar1</a></li>
 *                        <li><a data-icon="naviframe-cancel">Tabbar2</a></li>
 *                        <li><a data-icon="naviframe-call">Tabbar3</a></li>
 *                    </ul>
 *                </div>
 *            </div>
 *            <div data-role="content">
 *                Content
 *            </div>
 *        </div>
 *
 * ####Create simple tab bar in footer
 *
 *        @example
 *        <div data-role="page">
 *            <div data-role="content">Content</div>
 *            <div data-role="footer">
 *                <div data-role="tabbar">
 *                    <ul>
 *                        <li><a data-icon="naviframe-edit">Tabbar1</a></li>
 *                        <li><a data-icon="naviframe-cancel">Tabbar2</a></li>
 *                        <li><a data-icon="naviframe-call">Tabbar3</a></li>
 *                    </ul>
 *                </div>
 *            </div>
 *        </div>
 *
 * ## Manual constructor
 * For manual creation of search bar widget you can use constructor of widget from
 * **tau** namespace:
 *
 *        @example
 *        <div data-role="page" id="tab-bar-page">
 *            <div data-role="header">
 *                <div id="ready-for-tab-bar">
 *                    <ul>
 *                        <li><a data-icon="naviframe-edit">Tabbar1</a></li>
 *                        <li><a data-icon="naviframe-cancel">Tabbar2</a></li>
 *                        <li><a data-icon="naviframe-call">Tabbar3</a></li>
 *                    </ul>
 *                </div>
 *            </div>
 *            <div data-role="content">Content</div>
 *      </div>
 *        <script>
 *            (function (document) {
 *				var pageElement = document.getElementById("tab-bar-page"),
 *					tabBarElement = document.getElementById("ready-for-tab-bar"),
 *					tabBar;
 *
 *				function createPageHandle() {
 *					tabBar = tau.widget.TabBar(tabBarElement);
 *				}
 *
 *				pageElement.addEventListener("pagecreate", createPageHandle);
 *			}(document));
 *        </script>
 *
 * Constructor has one require parameter **element** which are base
 * **HTMLElement** to create widget. We recommend get this element by method
 * *document.getElementById*. Second parameter is **options** and it is a object
 * with options for widget.
 *
 * If jQuery library is loaded, its method can be used:
 *
 *        @example
 *        <div data-role="page" id="tab-bar-page">
 *            <div data-role="header">
 *                <div id="ready-for-tab-bar">
 *                    <ul>
 *                        <li><a data-icon="naviframe-edit">Tabbar1</a></li>
 *                        <li><a data-icon="naviframe-cancel">Tabbar2</a></li>
 *                        <li><a data-icon="naviframe-call">Tabbar3</a></li>
 *                    </ul>
 *                </div>
 *            </div>
 *            <div data-role="content">Content</div>
 *        </div>
 *        <script>
 *            (function (document) {
 *				function createPageHandle() {
 *					$("#ready-for-tab-bar").tabbar();
 *				}
 *
 *				$("#tab-bar-page").on("pagecreate", createPageHandle);
 *			}(document));
 *        </script>
 *
 * jQuery Mobile constructor has one optional parameter is **options** and it is
 * a object with options for widget.
 *
 * ##Options for tab bar widget
 *
 * Options for widget can be defined as _data-..._ attributes or give as
 * parameter in constructor.
 *
 * You can change option for widget using method **option**.
 *
 * ##Methods
 *
 * To call method on widget you can use one of existing API:
 *
 * First API is from tau namespace:
 *
 *        @example
 *        <script>
 *        var tabBarElement = document.getElementById("tab-bar"),
 *            tabBar = tau.widget.TabBar(TabBarElement);
 *
 *        tabBar.methodName(methodArgument1, methodArgument2, ...);
 *        </script>
 *
 * Second API is jQuery Mobile API and for call _methodName_ you can use:
 *
 *        @example
 *        <script>
 *        $(".selector").tabbar("methodName", methodArgument1, methodArgument2, ...);
 *        </script>
 *
 * @class ns.widget.core.TabBar
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var Tab = ns.widget.core.Tab,
				TabPrototype = Tab.prototype,
				engine = ns.engine,
				Page = ns.widget.core.Page,
				domUtils = ns.util.DOM,

				TabBar = function () {
					var self = this;

					self._type = {
						orientation: "portrait",
						withIcon: false,
						withTitle: false,
						static: false
					};
					self._ui = {
						tabs: [],
						links: []
					};
					/**
					 * Object with default options
					 * @property {Object} options
					 * @property {string} [options.active="0"] Number of activated tab.
					 * @property {string} [options.autoChange=true] Defined if widget should set
					 * @member ns.widget.core.TabBar
					 */
					self.options = {
						active: 0,
						autoChange: true,
						autoPositionSet: true
					};
					self._marqueeOptions = {
						ellipsisEffect: "none",
						marqueeStyle: "scroll",
						iteration: "infinite",
						delay: 1000
					};
				},
				CLASS_PREFIX = "ui-tabbar",
				/**
				 * Object with class dictionary
				 * @property {Object} classes
				 * @static
				 * @member ns.widget.core.TabBar
				 * @readonly
				 */
				classes = {
					TABBAR: CLASS_PREFIX,
					TAB_ACTIVE: "ui-tab-active",
					TAB_NO_TEXT: "ui-tab-no-text",
					TITLE: "ui-title",
					TABS_WITH_TITLE: "ui-tabs-with-title",
					TABBAR_WITH_TITLE: CLASS_PREFIX + "-with-title",
					TABBAR_BEFORE_TITLE: CLASS_PREFIX + "-before-title",
					TABBAR_WITH_ICON: CLASS_PREFIX + "-with-icon",
					TABBAR_PORTRAIT: CLASS_PREFIX + "-portrait",
					TABBAR_LANDSCAPE: CLASS_PREFIX + "-landscape",
					TABBAR_TEXT: CLASS_PREFIX + "-text",
					TABBAR_STATIC: CLASS_PREFIX + "-static",
					ANCHOR: CLASS_PREFIX + "-anchor",
					INACTIVE_TOO_LONG_TEXT: CLASS_PREFIX + "-inactive-text-overflow"
				},
				events = ns.event,
				DEFAULT_NUMBER = {
					PORTRAIT_LIMIT_LENGTH: 3,
					PORTRAIT_DEVIDE_NUMBER: 3.7,
					LANDSCAPE_LIMIT_LENGTH: 4,
					LANDSCAPE_DEVIDE_NUMBER: 4.7,
					WITH_ICON_WITH_TITLE: 2,
					WITH_ICON_NO_TITLE: 4,
					DURATION: 250
				},
				prototype = new Tab();

			TabBar.prototype = prototype;
			TabBar.classes = classes;

			function findTitle(element) {
				var parentNode = element.parentNode,
					title;

				while (parentNode && !parentNode.classList.contains(Page.classes.uiPage)) {
					title = parentNode.querySelector("." + classes.TITLE);
					if (title) {
						return title;
					}
					parentNode = parentNode.parentNode;
				}
				return 0;
			}

			/**
			 * Configure widget options, detect active item based on classes
			 * @method _configure
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._configure = function (element) {
				var links = element.querySelectorAll("li a"),
					activeIndex = -1;

				[].forEach.call(links, function (linkElement, index) {
					if (linkElement.classList.contains(classes.TAB_ACTIVE)) {
						activeIndex = index;
					}
				});
				if (activeIndex > -1) {
					this.options.active = activeIndex;
				}
			};

			/**
			 * Detect structure and add base classes for element
			 * @method _detectType
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._detectType = function (element) {
				var self = this,
					type = self._type,
					title = findTitle(element),
					link = element.querySelector("li a");

				if (title) {
					title.parentNode.classList.add(classes.TABS_WITH_TITLE);
					element.classList.add(classes.TABBAR_WITH_TITLE);
					type.withTitle = true;
				}
				if (element.nextElementSibling === title || element.nextElementSibling === title.parentNode) {
					element.classList.add(classes.TABBAR_BEFORE_TITLE);
				}
				if (link && link.hasAttribute("data-icon")) {
					element.classList.add(classes.TABBAR_WITH_ICON);
					type.withIcon = true;
				}
			};

			/**
			 * Build tabs and links, add classes, create span labels
			 * @method _buildTabsAndLinks
			 * @param {HTMLElement} element
			 * @return {boolean}
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._buildTabsAndLinks = function (element) {
				var self = this,
					ui = self._ui,
					tabs = element.querySelectorAll("li"),
					links = element.querySelectorAll("li a"),
					innerText,
					i,
					linksLength,
					link,
					text,
					textRealWidth,
					visibleTextWidth,
					prevTextOverflowVal;

				if (links.length === 0) {
					links = element.querySelectorAll("li div");
				}
				if (links.length === 0) {
					ns.warn("There is no tab element, TabBar wasn't build.");
					return false;
				}
				for (i = 0, linksLength = links.length; i < linksLength; i++) {
					link = links[i];
					text = link.firstChild;
					if (text) {
						innerText = document.createElement("span");
						innerText.classList.add(classes.TABBAR_TEXT);
						innerText.appendChild(link.firstChild);
						link.appendChild(innerText);

						prevTextOverflowVal = innerText.style.overflowX;
						visibleTextWidth = innerText.getBoundingClientRect().width;
						innerText.style.overflowX = "visible";
						textRealWidth = innerText.getBoundingClientRect().width;
						innerText.style.overflowX = prevTextOverflowVal;

						if (textRealWidth > visibleTextWidth) {
							link.classList.add(classes.INACTIVE_TOO_LONG_TEXT);
						}

					} else {
						link.classList.add(classes.TAB_NO_TEXT);
					}
					link.classList.add(classes.ANCHOR);
				}
				ui.links = links;
				ui.tabs = tabs;
				return true;
			};

			/**
			 * Build method
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement|null}
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._build = function (element) {
				var self = this;

				element.classList.add(classes.TABBAR);

				if (!self._buildTabsAndLinks(element)) {
					return null;
				}

				self._detectType(element);

				return element;
			};

			/**
			 * Method read current orientation and set state of widget for correct state;
			 * @param {HTMLElement} element
			 * @method _initOrientation
			 * @member ns.widget.core.TabBar
			 * @protected
			 */
			prototype._initOrientation = function (element) {
				var type = this._type,
					classList = element.classList;

				if (window.innerWidth < window.innerHeight) {
					classList.remove(classes.TABBAR_LANDSCAPE);
					classList.add(classes.TABBAR_PORTRAIT);
					type.orientation = "portrait";
				} else {
					classList.remove(classes.TABBAR_PORTRAIT);
					classList.add(classes.TABBAR_LANDSCAPE);
					type.orientation = "landscape";
				}
			};

			/**
			 * Method init all width of elements and update state of widget.
			 * @param {HTMLElement} element
			 * @method _initStaticAndWidths
			 * @private
			 */
			prototype._initStaticAndWidths = function (element) {
				var self = this,
					isStatic,
					tabs = self._ui.tabs,
					offsetWidth = element.getBoundingClientRect().width,
					length = tabs.length,
					wholeWidth = 0,
					elementWidth,
					i;

				// check that element is visible
				if (offsetWidth) {
					// get from class
					isStatic = element.classList.contains(classes.TABBAR_STATIC);

					// check if we have enough elements to make the list dynamic again
					if (!isStatic && tabs[0]) {
						elementWidth = domUtils.getElementWidth(tabs[0]);
						// check NaN
						if (elementWidth === elementWidth && (elementWidth * length < offsetWidth)) {
							isStatic = true;
						}
					}

					self._type.static = isStatic;

					for (i = 0; i < length; i++) {
						// make the elements "fit"
						if (isStatic) {
							elementWidth = parseInt(offsetWidth / length, 10) || 0;
							tabs[i].style.width = elementWidth + "px";
						} else {
							// just get each element with for scroll support
							elementWidth = domUtils.getElementWidth(tabs[i]);
						}
						wholeWidth += elementWidth;
					}

					self._wholeWidth = wholeWidth;
				}
			};

			/**
			 * Init method
			 * @method _init
			 * @param {HTMLElement} element
			 * @member ns.widget.core.TabBar
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this;

				self._initOrientation(element);
				self._initStaticAndWidths(element);

				self._translatedX = 0;
				self._lastX = 0;

				self._setActive(self.options.active);

				return element;
			};

			/**
			 * Bind events for widget
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._bindEvents = function () {
				var self = this,
					element = self.element,
					tabs = self._ui.tabs;

				events.enableGesture(
					element,
					new events.gesture.Drag()
				);
				events.on(element, "drag dragend", self, false);
				events.on(tabs, "vclick", self, false);
				window.addEventListener("resize", self, false);
			};

			/**
			 * Unbind events for widget
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._unBindEvents = function () {
				var self = this,
					element = self.element,
					tabs = self._ui.tabs;

				events.disableGesture(
					element,
					new events.gesture.Drag()
				);
				events.off(element, "drag dragend", self, false);
				events.off(tabs, "vclick", self, false);
				window.removeEventListener("resize", self, false);
			};

			/**
			 * Handle events
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.TabBar
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "drag":
						self._onDrag(event);
						break;
					case "dragend":
						self._onDragEnd(event);
						break;
					case "vclick":
						self._onClick(event);
						break;
					case "resize":
						self._init(self.element);
				}
			};

			/**
			 * translate tabbar element
			 * @method _translate
			 * @param {number} x position
			 * @param {number} duration of animation
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._translate = function (x, duration) {
				var self = this,
					element = this.element;

				if (duration) {
					domUtils.setPrefixedStyle(element, "transition", domUtils.getPrefixedValue("transform " + duration / 1000 + "s ease-out"));
				}

				domUtils.setPrefixedStyle(element, "transform", "translate3d(" + x + "px, 0px, 0px)");

				self._lastX = x;
			};

			/**
			 * click event handler
			 * @method _onClick
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._onClick = function (event) {
				var self = this,
					ui = self._ui,
					options = self.options,
					selectTab = event.currentTarget.querySelector("A"),
					index,
					i,
					tabLength;

				for (i = 0, tabLength = ui.links.length; i < tabLength; i++) {
					if (ui.links[i] === selectTab) {
						index = i;
						break;
					}
					index = 0;
				}

				if (options.autoChange) {
					self._setActive(index);
				}
			};

			/**
			 * Drag event handler
			 * @method _onDrag
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.TabBar
			 */
			prototype._onDrag = function (event) {
				var self = this,
					element = self.element;

				self._translate(
					Math.max(
						element.parentNode.offsetWidth - element.offsetWidth,
						Math.min(self._translatedX + event.detail.deltaX, 0)
					),
					0
				);
			};
			/**
			 * Dragend event handler
			 * @method _onDragEnd
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._onDragEnd = function () {
				var self = this;

				self._translatedX = self._lastX;
			};

			/**
			 * set the active tab
			 * @method _setActive
			 * @param {number} index
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._setActive = function (index) {
				var self = this,
					options = self.options,
					ui = self._ui,
					link,
					text,
					marquee,
					prevStyleValue,
					textWidth,
					allTextWidth;

				if (ui.links.length === 0) {
					return;
				}
				// disable previous link
				link = ui.links[options.active]
				link.classList.remove(classes.TAB_ACTIVE);
				text = link.querySelector("." + classes.TABBAR_TEXT);
				if (text) {
					marquee = ns.engine.getBinding(text);
					if (marquee) {
						marquee.reset();
						ns.engine.destroyWidget(text);
						link.classList.add(classes.INACTIVE_TOO_LONG_TEXT);
					}
				}

				// enable new link
				link = ui.links[index];
				link.classList.add(classes.TAB_ACTIVE);
				options.active = index;

				// enable Marquee widget on text content for active tab
				// if text content is longer then link
				text = link.querySelector("." + classes.TABBAR_TEXT);
				if (text) {
					prevStyleValue = text.style.overflowX;
					textWidth = text.getBoundingClientRect().width;
					text.style.overflowX = "visible";
					allTextWidth = text.getBoundingClientRect().width;
					text.style.overflowX = prevStyleValue;

					if (allTextWidth > textWidth) {
						link.classList.remove(classes.INACTIVE_TOO_LONG_TEXT);
						ns.widget.Marquee(text, self._marqueeOptions);
					}
				}

				self._setTabbarPosition();
				TabPrototype._setActive.call(self, index);
			};

			/**
			 * set Tabbar position automatically
			 * @method _setTabbarPosition
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._setTabbarPosition = function () {
				var self = this,
					activeIndex = self.options.active,
					tabs = self._ui.tabs,
					tabBarRect = self.element.getBoundingClientRect(),
					parentElementWidth = self.element.parentElement.offsetWidth,
					previousElementLeftPos,
					transformX;

				if (tabBarRect.width >= parentElementWidth) {
					if (activeIndex <= 1) {
						self._translate(0, DEFAULT_NUMBER.DURATION);
					} else if (activeIndex >= (tabs.length - 2)) {
						// Show last element on the right edge.
						self._translate(parentElementWidth - tabBarRect.width, DEFAULT_NUMBER.DURATION);
					} else {
						previousElementLeftPos = tabs[activeIndex - 1].getBoundingClientRect().left;
						transformX = previousElementLeftPos - tabBarRect.left;

						if (tabBarRect.width - transformX >= parentElementWidth) {
							self._translate(-transformX, DEFAULT_NUMBER.DURATION);
						} else {
							// Rest of the elements too narrow to cover whole tabbar.
							// Set scroll to show last element on the right edge.
							self._translate(parentElementWidth - tabBarRect.width, DEFAULT_NUMBER.DURATION);
						}
					}
				}
			};
			/**
			 * Destroy widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.TabBar
			 */
			prototype._destroy = function () {
				var self = this;

				self._unBindEvents();
				self._type = null;
				self._ui = null;
				self.options = null;
			};

			ns.widget.core.TabBar = TabBar;
			engine.defineWidget(
				"TabBar",
				"[data-role='tabbar'], ." + CLASS_PREFIX,
				[
					"setActive",
					"getActive"
				],
				TabBar
			);
			}(window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define, NodeList, HTMLCollection */
/*
 * @author Jadwiga Sosnowska <j.sosnowska@partner.samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
	
			var /**
				 * @property {number} [containerCounter=0]
				 * @member ns.util.DOM
				 * @private
				 * @static
				 */
				containerCounter = 0,
				/**
				 * Alias to Array.slice method
				 * @method slice
				 * @member ns.util.DOM
				 * @private
				 * @static
				 */
				slice = [].slice,
				DOM = ns.util.DOM,
				contentRegex = /(\$\{content\})/gi;

			/**
			 * Checks if element was converted via WebComponentsJS,
			 * this will return false if WC support is native
			 * @param {HTMLElement} node
			 * @return {boolean}
			 * @static
			 * @member ns.util.DOM
			 */
			function isNodeWebComponentPolyfilled(node) {
				var keys;

				if (!node) {
					return false;
				}
				// hacks
				keys = Object.keys(node).join(":");
				return (keys.indexOf("__impl") > -1 || keys.indexOf("__upgraded__") > -1 ||
						keys.indexOf("__attached__") > -1);
			}

			/**
			 * Returns wrapped element which was normal HTML element
			 * by WebComponent polyfill
			 * @param {HTMLElement} element
			 * @return {?HTMLElement}
			 * @member ns.util.DOM
			 * @static
			 */
			function wrapWebComponentPolyfill(element) {
				var wrap = window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrap;

				if (element && wrap) {
					return wrap(element);
				}
				
				return element;
			}

			/**
			 * Returns normal element which was wrapped
			 * by WebComponent polyfill
			 * @param {HTMLElement} element
			 * @return {?HTMLElement}
			 * @member ns.util.DOM
			 * @static
			 */
			function unwrapWebComponentPolyfill(element) {
				var unwrap = window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.unwrap;

				if (element && unwrap) {
					return unwrap(element);
				}

				ns.error("Unwrap method not available");
				return element;
			}
			/**
			 * Creates a selector for given node
			 * @param {HTMLElement} node
			 * @return {string}
			 * @member ns.util.DOM
			 * @method getNodeSelector
			 */
			function getNodeSelector(node) {
				var attributes = node.attributes,
					attributeLength = attributes.length,
					attr,
					i = 0,
					selector = node.tagName.toLowerCase();

				for (; i < attributeLength; ++i) {
					attr = attributes.item(i);
					selector += "[" + attr.name + "=\"" + attr.value + "\"]";
				}
				return selector;
			}

			/**
			 * Creates selector path (node and its parents) for given node
			 * @param {HTMLElement} node
			 * @return {string}
			 * @member ns.util.DOM
			 * @method getNodeSelectorPath
			 */
			function getNodeSelectorPath(node) {
				var path = getNodeSelector(node),
					parent = node.parentNode;

				while (parent) {

					path = getNodeSelector(parent) + ">" + path;

					parent = parent.parentNode;
					if (parent === document) {
						parent = null;
					}
				}
				return path;
			}

			DOM.getNodeSelector = getNodeSelector;
			DOM.getNodeSelectorPath = getNodeSelectorPath;

			/**
			 * Compares a node to another node
			 * note: this is needed because of broken WebComponents node wrapping
			 * @param {HTMLElement} nodeA
			 * @param {HTMLElement} nodeB
			 * @return {boolean}
			 * @member ns.util.DOM
			 * @method isNodeEqual
			 */
			DOM.isNodeEqual = function (nodeA, nodeB) {
				var nodeAPolyfilled,
					nodeBPolyfilled,
					foundNodeA = nodeA,
					foundNodeB = nodeB,
					unwrap = (window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.unwrap); // hack

				if (nodeA === null || nodeB === null) {
					return false;
				} else {
					nodeAPolyfilled = isNodeWebComponentPolyfilled(nodeA);
					nodeBPolyfilled = isNodeWebComponentPolyfilled(nodeB);
				}

				if (nodeAPolyfilled) {
					if (unwrap) {
						foundNodeA = unwrap(nodeA);
					} else {
						foundNodeA = document.querySelector(getNodeSelectorPath(nodeA));
					}
				}
				if (nodeBPolyfilled) {
					if (unwrap) {
						foundNodeB = unwrap(nodeB);
					} else {
						foundNodeB = document.querySelector(getNodeSelectorPath(nodeB));
					}
				}

				return foundNodeA === foundNodeB;
			};

			/**
			 * Checks if element was converted via WebComponentsJS,
			 * this will return false if WC support is native
			 * @method isNodeWebComponentPolyfilled
			 * @param {HTMLElement} node
			 * @return {boolean}
			 * @static
			 * @member ns.util.DOM
			 */
			DOM.isNodeWebComponentPolyfilled = isNodeWebComponentPolyfilled;

			DOM.unwrapWebComponentPolyfill = unwrapWebComponentPolyfill;
			DOM.wrapWebComponentPolyfill = wrapWebComponentPolyfill;

			DOM.isElement = function (element) {
				var raw = element;

				if (!raw) {
					return false;
				}

				// Dirty hack for bogus WebComponent polyfill
				if (typeof raw.localName === "string" && raw.localName.length > 0) {
					return true;
				}

				if (!(element instanceof Element)) {
					if (isNodeWebComponentPolyfilled(element)) {
						raw = unwrapWebComponentPolyfill(element);
					}
				}

				return raw instanceof Element;
			};

			/**
			 * Appends node or array-like node list array to context
			 * @method appendNodes
			 * @member ns.util.DOM
			 * @param {HTMLElement} context
			 * @param {HTMLElement|HTMLCollection|NodeList|Array} elements
			 * @return {HTMLElement|Array|null}
			 * @static
			 * @throws {string}
			 */
			DOM.appendNodes = function (context, elements) {
				var i,
					length,
					arrayElements;

				if (context) {
					if (elements instanceof Array || elements instanceof NodeList ||
						elements instanceof HTMLCollection) {
						arrayElements = slice.call(elements);
						for (i = 0, length = arrayElements.length; i < length; i += 1) {
							context.appendChild(arrayElements[i]);
						}
					} else {
						context.appendChild(elements);
						arrayElements = elements;
					}
					return arrayElements;
				}

				throw "Context empty!";
			};

			/**
			 * Replaces context with node or array-like node list
			 * @method replaceWithNodes
			 * @member ns.util.DOM
			 * @param {HTMLElement} context
			 * @param {HTMLElement|HTMLCollection|NodeList|Array} elements
			 * @return {HTMLElement|Array|null}
			 * @static
			 */
			DOM.replaceWithNodes = function (context, elements) {
				var returnElements;

				if (elements instanceof Array || elements instanceof NodeList ||
					elements instanceof HTMLCollection) {
					returnElements = this.insertNodesBefore(context, elements);
					context.parentNode.removeChild(context);
				} else {
					context.parentNode.replaceChild(elements, context);
					returnElements = elements;
				}
				return returnElements;
			};

			/**
			 * Remove all children
			 * @method removeAllChildren
			 * @member ns.util.DOM
			 * @param {HTMLElement} context
			 * @static
			 */
			DOM.removeAllChildren = function (context) {
				context.innerHTML = "";
			};

			/**
			 * Inserts node or array-like node list before context
			 * @method insertNodesBefore
			 * @member ns.util.DOM
			 * @param {HTMLElement} context
			 * @param {HTMLElement|HTMLCollection|NodeList|Array} elements
			 * @return {HTMLElement|Array|null}
			 * @static
			 * @throws {string}
			 */
			DOM.insertNodesBefore = function (context, elements) {
				var i,
					length,
					parent,
					returnElements;

				if (context) {
					parent = context.parentNode;
					if (elements instanceof Array || elements instanceof NodeList ||
						elements instanceof HTMLCollection) {
						returnElements = slice.call(elements);
						for (i = 0, length = returnElements.length; i < length; ++i) {
							parent.insertBefore(returnElements[i], context);
						}
					} else {
						parent.insertBefore(elements, context);
						returnElements = elements;
					}
					return returnElements;
				}

				throw "Context empty!";

			};

			/**
			 * Inserts node after context
			 * @method insertNodeAfter
			 * @member ns.util.DOM
			 * @param {HTMLElement} context
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @static
			 * @throws {string}
			 */
			DOM.insertNodeAfter = function (context, element) {
				if (context) {
					context.parentNode.insertBefore(element, context.nextSibling);
					return element;
				}
				throw "Context empty!";
			};

			/**
			 * Remove all children of node.
			 * @param {Node} fragment
			 */
			function cleanFragment(fragment) {
				// clean up
				while (fragment.firstChild) {
					fragment.removeChild(fragment.firstChild);
				}
			}

			/**
			 * Move nodes from one node to another.
			 * @param {Node} fromNode
			 * @param {Node} toNode
			 */
			function moveChildren(fromNode, toNode) {
				// move the nodes
				while (fromNode.firstChild) {
					toNode.appendChild(fromNode.firstChild);
				}
			}

			/**
			 * Prepare container for filling template
			 * @param {Node} fragment
			 * @param {string} html
			 * @return {{container: *, contentFlag: boolean}}
			 */
			function prepareContainer(fragment, html) {
				var container = document.createElement("div"),
					contentFlag = false;

				fragment.appendChild(container);

				container.innerHTML = html.replace(contentRegex, function () {
					contentFlag = true;
					return "<span id='temp-container-" + (++containerCounter) + "'></span>";
				});

				return {
					container: container,
					contentFlag: contentFlag
				};
			}

			/**
			 * Wraps element or array-like node list in html markup
			 * @method wrapInHTML
			 * @param {HTMLElement|NodeList|HTMLCollection|Array} elements
			 * @param {string} html
			 * @return {HTMLElement|NodeList|Array} wrapped element
			 * @member ns.util.DOM
			 * @static
			 */
			DOM.wrapInHTML = function (elements, html) {
				var fragment = document.createDocumentFragment(),
					fragment2 = document.createDocumentFragment(),
					elementsLen = elements.length,
					//if elements is nodeList, retrieve parentNode of first node
					originalParentNode = elementsLen ? elements[0].parentNode : elements.parentNode,
					next = elementsLen ? elements[elementsLen - 1].nextSibling : elements.nextSibling,
					innerContainer,
					resultElements,
					containerData;

				containerData = prepareContainer(fragment, html);

				if (containerData.contentFlag === true) {
					innerContainer = containerData.container.querySelector("span#temp-container-" +
						containerCounter);
					resultElements = this.replaceWithNodes(innerContainer, elements);
				} else {
					innerContainer = containerData.container.children[0];
					resultElements = this.appendNodes(innerContainer || containerData.container, elements);
				}

				moveChildren(fragment.firstChild, fragment2);
				cleanFragment(fragment);

				if (originalParentNode) {
					originalParentNode.insertBefore(fragment2, next);
				} else {
					cleanFragment(fragment2);
				}

				return resultElements;
			};
			}(window, window.document, ns));

/*global window, define, ns, HTMLTextAreaElement, HTMLInputElement */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Text Input
 * TextInput component is decorator for input elements.
 *
 * ## Default selectors
 * In default elements matches to :
 *
 *  - INPUT with type "text" or "number" or "password" or "email" or "url" or "tel" or "month" or "week" or "datetime-local" or "color" or without any
 *    type
 *  - TEXTAREA
 *  - HTML elements with class _ui-text-input_
 *
 * ###HTML Examples
 *
 * ####Create simple text input on INPUT element
 *
 *		@example
 *		<form>
 *			<label for="text-1">Text input:</label>
 *			<input type="text" name="text-1" id="text-1" value="">
 *			<input type="text" name="text-1" id="text-1" value="">
 *		</form>
 *
 * ####Create simple text input on TEXTAREA element
 *
 *		@example
 *		<form>
 *			<label for="text-1">Text input:</label>
 *			<textarea name="text-1" id="text-1"></textarea>
 *		</form>
 *
 * ####Create simple text input on INPUT element with class ui-text-input
 *
 *		@example
 *		<form>
 *			<label for="text-1">Text input:</label>
 *			<input name="text-1" id="text-1" class="ui-text-input">
 *		</form>
 *
 * ## Manual constructor
 * For manual creation of TextInput widget you can use constructor of widget
 * from **tau** namespace:
 *
 *		@example
 *		<form>
 *			<label for="text-1">Text input:</label>
 *			<input type="search" name="text-1" id="text-1" value="">
 *		</form>
 *		<script>
 *			var inputElement = document.getElementById("text-1"),
 *				textInput = tau.widget.TextInput(inputElement);
 *		</script>
 *
 * ##Options for widget
 *
 * Options for widget can be defined as _data-..._ attributes or give as
 * parameter in constructor.
 *
 * You can change option for widget using method **option**.
 *
 * ##Methods
 *
 * To call method on widget you can use one of existing API:
 *
 * First API is from tau namespace:
 *
 *		@example
 *		<input id="text-1" />
 *		<script>
 *			var inputElement = document.getElementById('text-1'),
 *				textInput = tau.widget.TextInput(inputElement);
 *			// textInput.methodName(argument1, argument2, ...);
 *			// for example:
 *			textInput.value("text");
 *		</script>
 *
 * @since 2.0
 * @class ns.widget.mobile.TextInput
 * @extends ns.widget.BaseWidget
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Piotr Kusztal <p.kusztal@samsung.com>
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 * @author Heeju Joo <heeju.joo@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				engine = ns.engine,
				util = ns.util,
				domUtils = util.DOM,
				utilSelector = util.selectors,
				objectUtils = util.object,
				utilEvent = ns.event,

				TextInput = function () {
					var self = this;

					self.options = objectUtils.merge({}, TextInput.defaults);
					self._ui = {
						textLineElement: null,
						textClearButtonElement: null
					};
					self._callbacks = {};
				},
				buttonClasses = ns.widget.core.Button.classes,

				listviewClasses = ns.widget.core.Listview.classes,
				popupClasses = ns.widget.core.Popup.classes,

				prototype = new BaseWidget(),

				CLASSES_PREFIX = "ui-text-input",

				/**
				 * Dictionary for TextInput related css class names
				 * @property {Object} classes
				 * @member ns.widget.mobile.TextInput
				 * @static
				 */
				classes = {
					uiTextInput: CLASSES_PREFIX,
					uiTextInputClear: CLASSES_PREFIX + "-clear",
					uiTextInputClearHidden: CLASSES_PREFIX + "-clear-hidden",
					uiTextInputClearActive: CLASSES_PREFIX + "-clear-active",
					uiTextInputTextLine: CLASSES_PREFIX + "-textline",
					uiTextInputDisabled: CLASSES_PREFIX + "-disabled",
					uiTextInputFocused: CLASSES_PREFIX + "-focused",
					HEADER_WITH_SEARCH: "ui-header-searchbar",
					SEARCHINPUT: "ui-search-input",
					HEADER: "ui-header",
					CONTAINER: CLASSES_PREFIX + "-container"
				},
				/**
				 * Selector for clear button appended to TextInput
				 * @property {string} CLEAR_BUTTON_SELECTOR
				 * @member ns.widget.mobile.TextInput
				 * @static
				 * @private
				 * @readonly
				 */
				selector = {
					uiTextInput: "." + classes.uiTextInput,
					uiTextInputClearButton: "." + classes.uiTextInputClear,
					uiTextInputTextLine: "." + classes.uiTextInputTextLine
				},
				/**
				 * Object with default options
				 * @property {Object} options
				 * @property {boolean} [options.clearBtn=false] option indicates that the clear button will be shown
				 * @property {boolean} [options.textLine=true] option indicates that the text underline will be shown
				 * @property {boolean} [options.maxHeight=null] set max height for textarea
				 * @property {boolean} [options.outsideDiv=false] created outsider div as container of input elements
				 * @member ns.widget.mobile.TextInput
				 */
				defaults = {
					clearBtn: false,
					textLine: true,
					maxHeight: null,
					outsideDiv: false
				},
				eventName = {
					SEARCH: "search",
					ANIMATIONEND: "animationend"
				};

			TextInput.prototype = prototype;
			TextInput.classes = classes;
			TextInput.defaults = defaults;

			/**
			 * Resize textarea, called after text input
			 * @method _resizeTextArea
			 * @protected
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._resizeTextArea = function (element) {
				var listviewElement,
					listviewWidget,
					popupElement = util.selectors.getClosestByClass(element, popupClasses.popup),
					popupWidget,
					maxHeight = parseInt(this.options.maxHeight, 10),
					newHeight,
					style = element.style,
					previousHeight = style.height;

				style.height = "auto"; // reset for the browser to recalculate scrollHeight
				newHeight = element.scrollHeight; // apply scrollHeight as new height

				element.scrollTop = newHeight;
				if (maxHeight && newHeight > maxHeight) {
					newHeight = maxHeight;
				}
				style.height = newHeight + "px";

				if ((previousHeight !== (newHeight + "px")) && popupElement && previousHeight !== "") {
					popupWidget = engine.getBinding(popupElement);
					popupWidget.refresh();
				}
				listviewElement = util.selectors.getClosestByClass(element, listviewClasses.LISTVIEW);
				if (listviewElement) {
					listviewWidget = engine.getBinding(listviewElement);
					if (listviewWidget) {
						listviewWidget.refresh();
					}
				}
			};
			/**
			 * Toggle visibility of the clear button
			 * @method _toggleClearButton
			 * @param {HTMLElement} clearBtn
			 * @param {HTMLInputElement} inputElement
			 * @static
			 * @protected
			 */
			prototype._toggleClearButton = function (clearBtn, inputElement) {
				if (clearBtn) {
					if (!inputElement.classList.contains(classes.uiTextInputFocused)) {
						if (!clearBtn.classList.contains("ui-btn-active")) {
							clearBtn.classList.add(classes.uiTextInputClearHidden);
						}
					} else {
						clearBtn.classList.remove(classes.uiTextInputClearHidden);
						inputElement.classList.add(classes.uiTextInputClearActive);
					}
				}
			};
			/**
			 * Method clears text in input field and sets focus
			 * @method _onClearBtnClick
			 * @param {ns.widget.core.Button} self
			 * @static
			 * @protected
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._onClearBtnClick = function (self) {
				self.element.focus();
				self.element.value = "";
				self.trigger(eventName.SEARCH);
			};
			/**
			 * Method hides button after its animation ends
			 * @method _onClearBtnAnimationEnd
			 * @param {ns.widget.core.Button} self
			 * @param {Event} event
			 * @static
			 * @protected
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._onClearBtnAnimationEnd = function (self, event) {
				if (event.animationName === "btn_pressup_animation" && self.element.value === "") {
					event.target.classList.add(classes.uiTextInputClearHidden);
				}
			};

			/**
			 * Method adds class ui-text-input-focused to target element of event.
			 * @method _onFocus
			 * @param {ns.widget.mobile.TextInput} self
			 * @protected
			 * @static
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._onFocus = function (self) {
				var element = self.element;

				element.classList.add(classes.uiTextInputFocused);
				if (element.value !== "" && self._ui.textClearButtonElement) {
					self._ui.textClearButtonElement.classList.remove(classes.uiTextInputClearHidden);
				}
			};

			/**
			 * Method adds event for showing clear button and optional resizing textarea.
			 * @method _onInput
			 * @param {ns.widget.mobile.TextInput} self
			 * @protected
			 * @static
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._onInput = function (self) {
				var element = self.element,
					btn = self._ui.textClearButtonElement;

				if (element.value === "" && btn) {
					btn.classList.add(classes.uiTextInputClearHidden);
					element.classList.remove(classes.uiTextInputClearActive);
				} else {
					self._toggleClearButton(self._ui.textClearButtonElement, element);
				}

				if (element.nodeName.toLowerCase() === "textarea") {
					self._resizeTextArea(element);
				}
			};
			/**
			 * Method removes class ui-text-input-focused from target element of event.
			 * @method _onBlur
			 * @param {ns.widget.mobile.TextInput} self
			 * @private
			 * @static
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._onBlur = function (self) {
				var element = self.element;

				element.classList.remove(classes.uiTextInputFocused);
				self._toggleClearButton(self._ui.textClearButtonElement, element);
			};

			function setAria(element) {
				element.setAttribute("role", "textinput");
				element.setAttribute("aria-label", "Keyboard opened");
			}

			function createTextLine(element) {
				var textLine = document.createElement("span");

				textLine.classList.add(classes.uiTextInputTextLine);
				domUtils.insertNodeAfter(element, textLine);

				return textLine;
			}

			prototype._createClearButton = function (element, header) {
				var clearButton = document.createElement("a");

				clearButton.classList.add(buttonClasses.BTN);
				clearButton.classList.add(buttonClasses.BTN_ICON);
				clearButton.classList.add(buttonClasses.BTN_NOBG);
				clearButton.classList.add(classes.uiTextInputClear);
				clearButton.classList.add(classes.uiTextInputClearHidden);

				clearButton.tabindex = 0;
				if (header) {
					element.parentNode.appendChild(clearButton);
				} else {
					element.parentNode.insertBefore(clearButton, element.nextSibling.nextSibling);
				}

				return clearButton;
			};

			/**
			* build TextInput Widget
			* @method _build
			* @param {HTMLElement} element
			* @member ns.widget.mobile.TextInput
			* @return {HTMLElement}
			* @protected
			*/
			prototype._build = function (element) {
				var self = this,
					options = self.options,
					type = element.type,
					ui = self._ui,
					header;


				self._setOutsideDiv(element, options.outsideDiv);

				/* set Aria and TextLine */
				switch (type) {
					case "text":
					case "password":
					case "number":
					case "email":
					case "url":
					case "tel":
					case "search":
						setAria(element);
						ui.textLineElement = createTextLine(element);
						break;
					default:
						if (element.tagName.toLowerCase() === "textarea") {
							setAria(element);
							if (options.textLine) {
								ui.textLineElement = createTextLine(element);
							}
						}
				}

				element.classList.add(classes.uiTextInput);
				element.tabindex = 0;

				if (options.clearBtn) {
					ui.textClearButtonElement = self._createClearButton(element);
				}

				if (type === "search") {
					header = utilSelector.getClosestByClass(element, classes.HEADER);
					element.classList.add(classes.SEARCHINPUT);

					if (header) {
						header.classList.add(classes.HEADER_WITH_SEARCH);
						if (element.nextElementSibling.classList.contains(classes.uiTextInputTextLine)) {
							element.parentElement.removeChild(element.nextElementSibling);
						}
					}

					if (!options.clearBtn) {
						ui.textClearButtonElement = self._createClearButton(element, header);
					}

					if (!element.getAttribute("placeholder")) {
						element.setAttribute("placeholder", "Search");
					}
				}

				return element;
			};

			prototype._setOutsideDiv = function (element, newDiv) {
				var container = document.createElement("div");

				if (newDiv) {
					container.className = classes.CONTAINER;
					element.parentElement.replaceChild(container, element);
					container.appendChild(element);
				}

				this.options.outsideDiv = newDiv;
			};

			/**
			* Init TextInput Widget
			* @method _init
			* @param {HTMLElement} element
			* @member ns.widget.mobile.TextInput
			* @return {HTMLElement}
			* @protected
			*/
			prototype._init = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options,
					type = element.type,
					parentNode = element.parentNode;

				if (options.clearBtn) {
					ui.textClearButtonElement = ui.textClearButtonElement || parentNode.querySelector(selector.uiTextInputClearButton);
				}
				if (options.textLine) {
					switch (type) {
						case "text":
						case "password":
						case "number":
						case "email":
						case "url":
						case "tel":
							ui.textLineElement = ui.textLineElement || parentNode.querySelector(selector.uiTextInputTextLine);
							break;
						default:
							if (element.nodeName.toLowerCase() === "textarea") {
								ui.textLineElement = ui.textLineElement || parentNode.querySelector(selector.uiTextInputTextLine);
							}
					}
				}

				if (element.nodeName.toLowerCase() === "textarea") {
					if (element.hasAttribute("rows") === false) {
						element.rows = 1;
					}
					self._resizeTextArea(element);
				}

				return element;
			};


			/**
			* Bind events to widget
			* @method _bindEvents
			* @protected
			* @member ns.widget.mobile.TextInput
			*/
			prototype._bindEvents = function () {
				var self = this,
					element = self.element,
					clearBtn = self._ui.textClearButtonElement,
					onInputCallback = self._onInput.bind(null, self),
					onFocusCallback = self._onFocus.bind(null, self),
					onBlurCallback = self._onBlur.bind(null, self),
					onClearBtnClickCallback = self._onClearBtnClick.bind(null, self),
					onClearBtnAnimationEndCallback = self._onClearBtnAnimationEnd.bind(null, self);

				self._callbacks = {
					onInputCallback: onInputCallback,
					onFocusCallback: onFocusCallback,
					onBlurCallback: onBlurCallback,
					onClearBtnClickCallback: onClearBtnClickCallback,
					onClearBtnAnimationEndCallback: onClearBtnAnimationEndCallback
				};

				utilEvent.on(element, "input", onInputCallback);
				utilEvent.on(element, "focus", onFocusCallback);
				utilEvent.on(element, "blur", onBlurCallback);
				if (clearBtn) {
					utilEvent.on(clearBtn, "click", onClearBtnClickCallback);
					utilEvent.on(clearBtn, eventName.ANIMATIONEND, onClearBtnAnimationEndCallback);
				}

			};
			/**
			 * unbind events to widget
			 * @method _unbindEvents
			 * @protected
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._unbindEvents = function () {
				var self = this,
					element = self.element,
					clearBtn = self._ui.textClearButtonElement,
					callbacks = self._callbacks;

				utilEvent.off(element, "input", callbacks.onInputCallback);
				utilEvent.off(element, "focus", callbacks.onFocusCallback);
				utilEvent.off(element, "blur", callbacks.onBlurCallback);
				if (clearBtn) {
					utilEvent.off(clearBtn, "click", callbacks.onClearBtnClickCallback);
					utilEvent.off(clearBtn, eventName.ANIMATIONEND, callbacks.onClearBtnAnimationEndCallback);
				}
			};

			/**
			 * Enables the TextInput
			 *
			 * Method removes disabled attribute on input and changes look of
			 * input to enabled state.
			 *
			 *	@example
			 *	<input id="input" />
			 *	<script>
			 *		var inputElement = document.getElementById("input"),
			 *			textInputWidget = tau.widget.TextInput();
			 *
			 *		textInputWidget.enable();
			 *	</script>
			 *
			 * @method enable
			 * @chainable
			 * @member ns.widget.mobile.TextInput
			 */

			/**
			 * Method enables TextInput.
			 * @method _enable
			 * @member ns.widget.mobile.TextInput
			 * @protected
			 */
			prototype._enable = function () {
				var element = this.element;

				if (element) {
					element.removeAttribute("disabled");
					element.classList.remove(classes.uiTextInputDisabled);
				}
			};

			/**
			 * Disables the TextInput
			 *
			 * Method adds disabled attribute on input and changes look of
			 * input to disable state.
			 *
			 *	@example
			 *	<input id="input" />
			 *	<script>
			 *		var inputElement = document.getElementById("input"),
			 *			textInputWidget = tau.widget.TextInput();
			 *
			 *		textInputWidget.disable();
			 *	</script>
			 *
			 *
			 * @method disable
			 * @chainable
			 * @member ns.widget.mobile.TextInput
			 */

			/**
			 * Method disables TextInput
			 * @method _disable
			 * @member ns.widget.mobile.TextInput
			 * @protected
			 */
			prototype._disable = function () {
				var element = this.element;

				if (element) {
					element.setAttribute("disabled", "disabled");
					element.classList.add(classes.uiTextInputDisabled);
				}
			};

			/**
			 * Get element value
			 * @method _getValue
			 * @return {?string}
			 * @member ns.widget.mobile.TextInput
			 * @protected
			 * @since 2.3.1
			 */
			prototype._getValue = function () {
				var element = this.element;

				if (element) {
					return element.value;
				}
				return null;
			};

			/**
			 * Set element value
			 * @method _setValue
			 * @param {string} value
			 * @member ns.widget.mobile.TextInput
			 * @return {ns.widget.mobile.TextInput}
			 * @protected
			 * @since 2.3.1
			 */
			prototype._setValue = function (value) {
				var element = this.element;

				if (element) {
					element.value = value;
				}
				return this;
			};

			/**
			 * Destroys additional elements created by the widget,
			 * removes classes and event listeners
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.TextInput
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self._ui,
					textLine = ui.textLineElement,
					clearButton = ui.textClearButtonElement;

				self._unbindEvents();

				if (textLine.parentElement) {
					textLine.parentElement.removeChild(ui.textLineElement);
				}

				if (clearButton) {
					clearButton.parentElement.removeChild(ui.textClearButtonElement);
				}
			};

			ns.widget.mobile.TextInput = TextInput;
			engine.defineWidget(
				"TextInput",
				"input[type='text']:not([data-role])" +
					", input[type='number']:not([data-role])" +
					", input[type='password']:not([data-role])" +
					", input[type='email']:not([data-role])" +
					", input[type='url']:not([data-role])" +
					", input[type='tel']:not([data-role])" +
					", input[type='search']:not([data-role]), .ui-search-input" +
					", textarea" +
					", input:not([type])." + classes.uiTextInput,
				[],
				TextInput,
				"mobile",
				false,
				false,
				HTMLInputElement
			);

			ns.widget.mobile.TextArea = TextInput;
			engine.defineWidget(
				"TextArea",
				null,
				[],
				TextInput,
				"mobile",
				false,
				false,
				HTMLTextAreaElement
			);

			engine.defineWidget(
				"SearchInput",
				"",
				[],
				TextInput,
				"mobile"
			);
			}(window.document, ns));

/*global window, ns, define, HTMLSelectElement */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Dropdown Menu
 * Dropdown menu component is used to select one option. It is created as a drop-down list form.
 *
 * ##Default selector
 * In default all select elements are changed to Tizen WebUI DropdownMenu.
 * Additionally elements with _data-native-menu=false_ will use custom popups for option selection
 *
 * ###  HTML Examples
 *
 * ####  Create DropdownMenu
 * Default value of data-native-menu attribute is true and it makes native DropdownMenu.
 * This widget also offers the possibility of having custom DropdownMenu.
 *
 *        @example
 *        <select data-native-menu="false">
 *            <option value="1">Item1</option>
 *            <option value="2">Item2</option>
 *            <option value="3">Item3</option>
 *            <option value="4">Item4</option>
 *        </select>
 *
 * ## Manual constructor
 * For manual creation of DropdownMenu widget you can use constructor of widget.
 *
 *        @example
 *        <select id="dropdownmenu" data-native-menu="false">
 *            <option value="1">Item1</option>
 *            <option value="2">Item2</option>
 *            <option value="3">Item3</option>
 *            <option value="4">Item4</option>
 *        </select>
 *        <script>
 *            var element = document.getElementById("dropdownmenu"),
 *                widget = tau.widget.DropdownMenu(element);
 *        </script>
 *
 *
 * ##Inline type
 * When data-inline attribute is set to true, width of the DropdownMenu is determined by its text.
 * (Default is false.)
 *
 *            @example
 *            <select id="dropdownmenu" data-native-menu="false" data-inline="true">
 *                <option value="1">Item1</option>
 *                <option value="2">Item2</option>
 *                <option value="3">Item3</option>
 *                <option value="4">Item4</option>
 *            </select>
 *
 * ##Placeholder options
 * If you use <option> with data-placeholder="true" attribute, you can make a default placeholder.
 * Default value of data-hide-placeholder-menu-items attribute is true and data-placeholder option
 * is hidden. If you don't want that, you can use data-hide-placeholder-menu-items="false"
 * attribute.
 *
 *        @example
 *        <select id="dropdownmenu" data-native-menu="false"
 *            data-hide-placeholder-menu-items="false">
 *            <option value="choose-one" data-placeholder="true">Choose an option</option>
 *            <option value="1">Item1</option>
 *            <option value="2">Item2</option>
 *            <option value="3">Item3</option>
 *            <option value="4">Item4</option>
 *        </select>
 *
 * ##Methods
 * To call method on widget you can use one of existing API:
 *
 * First API is from tau namespace: RECOMMEND
 *
 *        @example
 *        var element = document.getElementById("dropdownmenu"),
 *            widget = tau.widget.DropdownMenu(element);
 *        widget.methodName(methodArgument1, methodArgument2, ...);
 *
 * Second API is jQuery Mobile API and for call _methodName_ you can use: Support for backward
 * compatibility
 *
 *        @example
 *        $(".selector").dropdownmenu("methodName", methodArgument1, methodArgument2, ...);
 *
 * - "open" - DropdownMenu open
 *
 *        @example
 *        var elDropdownMenu = document.getElementById("dropdownmenu"),
 *            widget = tau.widget.DropdownMenu(elDropdownMenu);
 *        widget.open();
 *
 * - "close" - DropdownMenu close
 *
 *        @example
 *        var elDropdownMenu = document.getElementById("dropdownmenu"),
 *            widget = tau.widget.DropdownMenu(elDropdownMenu);
 *        widget.close();
 *
 * - "refresh" - This method refreshes the DropdownMenu widget.
 *
 *        @example
 *        var elDropdownMenu = document.getElementById("dropdownmenu"),
 *            widget = tau.widget.DropdownMenu(elDropdownMenu);
 *        widget.refresh();
 *
 * @since 2.4
 * @class ns.widget.mobile.DropdownMenu
 * @extends ns.widget.mobile.BaseWidgetMobile
 * @author Hagun Kim <hagun.kim@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				engine = ns.engine,
				domUtils = ns.util.DOM,
				eventUtils = ns.event,
				selectors = ns.util.selectors,
				slice = [].slice,
				Page = ns.widget.core.Page,
				indexOf = [].indexOf,
				DropdownMenu = function () {
					var self = this;
					/**
					 * @property {boolean} _isOpen Open/Close status of DropdownMenu
					 * @member ns.widget.mobile.DropdownMenu
					 */

					self._isOpen = false;
					self._isClosing = false;
					/**
					 * @property {number} _selectedIndex Index of selected option in DropdownMenu
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._selectedIndex = null;
					/**
					 * @property {Object} _ui Object with html elements connected with DropdownMenu
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._ui = {
						elSelectWrapper: null,
						elPlaceHolder: null,
						elSelect: null,
						screenFilter: null,
						elOptionContainer: null,
						elOptions: null,
						elPage: null,
						elContent: null,
						elDefaultOption: null
					};
					/**
					 * @property {Object} options Object with default options
					 * @property {boolean} [options.nativeMenu=true] Sets the DropdownMenu widget as native/custom type.
					 * @property {boolean} [options.inline=false] Sets the DropdownMenu widget as inline/normal type.
					 * @property {boolean} [options.hidePlaceholderMenuItems=true] Hide/Reveal the placeholder option in dropdown list of the DropdownMenu.
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self.options = {
						nativeMenu: true,
						inline: false,
						hidePlaceholderMenuItems: true
					};
					/**
					 * @property {Function|null} _toggleMenuBound callback for select action
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._toggleMenuBound = null;
					/**
					 * @property {Function|null} _changeOptionBound callback for change value
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._changeOptionBound = null;
					/**
					 * @property {Function|null} _onResizeBound callback for throttledresize
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._onResizeBound = null;
					/**
					 * @property {Function|null} _nativeChangeOptionBound callback for change value
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._nativeChangeOptionBound = null;
					/**
					 * @property {Function|null} _focusBound callback for focus action
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._focusBound = null;
					/**
					 * @property {Function|null} _blurBound callback for blur action
					 * @protected
					 * @member ns.widget.mobile.DropdownMenu
					 */
					self._blurBound = null;
					// event callbacks
					self._callbacks = {};
				},
				/**
				 * Dictionary for DropdownMenu related css class names
				 * @property {Object} classes
				 * @member ns.widget.mobile.DropdownMenu
				 * @static
				 */
				classes = {
					selectWrapper: "ui-dropdownmenu",
					optionGroup: "ui-dropdownmenu-optiongroup",
					placeHolder: "ui-dropdownmenu-placeholder",
					optionList: "ui-dropdownmenu-options",
					optionsWrapper: "ui-dropdownmenu-options-wrapper",
					selected: "ui-dropdownmenu-selected",
					active: "ui-dropdownmenu-active",
					opening: "ui-dropdownmenu-options-opening",
					closing: "ui-dropdownmenu-options-closing",
					opened: "ui-dropdownmenu-options-opened",
					filter: "ui-dropdownmenu-overlay",
					filterHidden: "ui-dropdownmenu-overlay-hidden",
					disabled: "ui-dropdownmenu-disabled",
					widgetDisabled: "ui-disabled",
					inline: "ui-dropdownmenu-inline",
					native: "ui-dropdownmenu-native",
					top: "ui-dropdownmenu-options-top",
					bottom: "ui-dropdownmenu-options-bottom",
					focus: "ui-focus"
				},
				prototype = new BaseWidget();

			DropdownMenu.prototype = prototype;
			DropdownMenu.classes = classes;

			/**
			 * vclick to toggle menu event handler
			 * @method toggleMenu
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @param {Event} event
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function toggleMenu(self, event) {
				self._toggleSelect();
				eventUtils.stopPropagation(event);
				eventUtils.preventDefault(event);
			}

			/**
			 * vclick to change option event handler
			 * @method changeOption
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @param {Event} event
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function changeOption(self, event) {
				var target = event.target,
					tag = target.tagName,
					classList = target.classList;

				if (tag === "LI" && !classList.contains(classes.optionGroup) && !classList.contains(classes.disabled)) {
					self._selectedIndex = indexOf.call(self._ui.elOptions, target);
					self._changeOption();
					self._toggleSelect();
				}
				event.stopPropagation();
				event.preventDefault();
			}

			/**
			 * Change option in native DropdownMenu
			 * @method nativeChangeOption
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function nativeChangeOption(self) {
				var ui = self._ui,
					selectedOption = ui.elSelect[ui.elSelect.selectedIndex];

				ui.elPlaceHolder.textContent = selectedOption.textContent;
			}

			/**
			 * Function fires on window resizing
			 * @method onResize
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @param {Event} event
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function onResize(self, event) {
				if (self._isOpen === true) {
					self._isOpen = !self._isOpen;
					self._toggleSelect();
					event.stopPropagation();
					event.preventDefault();
				}
			}

			/**
			 * Function adds ui-focus class on focus
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @param {Event} event
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function onFocus(self, event) {
				var ui = self._ui,
					target = event.target;

				if (target === ui.elSelectWrapper ||
					target.parentNode === ui.elOptionContainer) {
					target.classList.add(classes.focus);
				}
			}

			/**
			 * Function removes ui-focus class on focus
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @param {Event} event
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function onBlur(self, event) {
				var ui = self._ui,
					target = event.target;

				if (target === ui.elSelectWrapper ||
					target.parentNode === ui.elOptionContainer) {
					target.classList.remove(classes.focus);
				}
			}

			/**
			 * Toggle enable/disable DropdownMenu
			 * @method setDisabledStatus
			 * @private
			 * @static
			 * @param {HTMLElement} element
			 * @param {boolean} isDisabled
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function setDisabledStatus(element, isDisabled) {
				var classList = element.classList;

				if (isDisabled) {
					classList.add(classes.disabled);
					classList.add(classes.widgetDisabled);
				} else {
					classList.remove(classes.disabled);
					classList.remove(classes.widgetDisabled);
				}
			}

			/**
			 * Convert option tag to li element
			 * @method _convertOptionToHTML
			 * @protected
			 * @param {boolean} hidePlaceholderMenuItems
			 * @param {HTMLElement} option
			 * @param {boolean} isDisabled
			 * @return {string}
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._convertOptionToHTML = function (hidePlaceholderMenuItems, option, isDisabled) {
				var className = option.className;

				if (!hidePlaceholderMenuItems || !domUtils.getNSData(option, "placeholder")) {
					if (isDisabled) {
						className += " " + classes.disabled;
					}
					return "<li data-value='" + option.value + "'" +
						(className ? " class='" + className + "'" : "") +
						(!isDisabled ? " tabindex='0'" : "") + ">" +
						option.textContent +
						"</li>";
				}
				return "";
			};

			/**
			 * Return offset of element
			 * @method getOffsetOfElement
			 * @private
			 * @static
			 * @param {HTMLElement} element
			 * @param {HTMLElement} container
			 * @return {Object}
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function getOffsetOfElement(element, container) {
				var top = element.offsetTop,
					left = element.offsetLeft,
					offsetParent;

				while (element.offsetParent) {
					offsetParent = element.offsetParent;
					top += offsetParent.offsetTop;
					left += offsetParent.offsetLeft;
					if (element === container) {
						break;
					}
					element = offsetParent;
				}
				return {top: top, left: left};
			}

			/**
			 * Construct element of option of DropdownMenu
			 * @method _constructOption
			 * @protected
			 * @return {string}
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._constructOption = function () {
				var self = this,
					i = 0,
					j,
					forElement,
					tag,
					resultHTML = "",
					optionArray = slice.call(self._ui.elSelect.children),
					optionCount = optionArray.length,
					groupOptionArray,
					groupOptCount,
					isDisabled,
					hidePlaceholderMenuItems = self.options.hidePlaceholderMenuItems;

				// This part is for optgroup tag.
				for (; i < optionCount; i++) {
					forElement = optionArray[i];
					isDisabled = forElement.disabled;
					tag = forElement.tagName;
					// for <option> tag
					if (tag === "OPTION") {
						/* When data-hide-placeholder-menu-items is true,
						 * <option> with data-placeholder="true" is hidden in DropdownMenu.
						 * It means that the <option> doesn't have to be DropdownMenu element.
						 */
						resultHTML += self._convertOptionToHTML(hidePlaceholderMenuItems, forElement, isDisabled);
					} else if (tag === "OPTGROUP") {
						// for <optgroup> tag
						resultHTML += "<li class='" + classes.optionGroup +
							(isDisabled ? (" " + classes.disabled + "'") : "'") + ">" + forElement.label +
							"</li>";
						groupOptionArray = slice.call(forElement.children);
						for (j = 0, groupOptCount = groupOptionArray.length; j < groupOptCount; j++) {
							// If <optgroup> is disabled, all child of the optgroup are also disabled.
							isDisabled = forElement.disabled || groupOptionArray[j].disabled;
							resultHTML += self._convertOptionToHTML(hidePlaceholderMenuItems, groupOptionArray[j],
								isDisabled);
						}
					}
				}
				return resultHTML;
			};

			/**
			 * Setter for option inline
			 * @method _setInline
			 * @protected
			 * @param {HTMLElement} element
			 * @param {boolean} value
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._setInline = function (element, value) {
				var ui = this._ui;

				ui.elSelectWrapper.classList.toggle(classes.inline, value);
				if (value) {
					ui.elPlaceHolder.removeAttribute("style");
				}

				this.options.inline = value;
			};

			prototype._getContainer = function () {
				return this._ui.elSelectWrapper;
			}

			/**
			 * Build structure of DropdownMenu widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._build = function (element) {
				return this._generate(element, true);
			};

			/**
			 * Generate Placeholder and Options elements for DropdownMenu
			 * @method _generate
			 * @param {HTMLElement} element
			 * @param {boolean} create
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._generate = function (element, create) {
				var self = this,
					options = self.options,
					selectedOption,
					elementId = element.id,
					ui = self._ui,
					pageClasses = Page.classes;

				ui.elSelect = element;
				ui.page = selectors.getParentsByClass(element, pageClasses.uiPage)[0] || document.body;
				ui.content = selectors.getParentsByClass(element, pageClasses.uiContent)[0] ||
					selectors.getParentsByClass(element, pageClasses.uiHeader)[0];
				ui.elDefaultOption = element.querySelector("option[data-placeholder='true']");

				self._selectedIndex = element.selectedIndex;

				if (create) {
					selectedOption = ui.elDefaultOption || element[element.selectedIndex] || element.options.item(element.selectedIndex);

					self._buildWrapper(element);
					self._buildPlaceholder(element, ui.elSelectWrapper, elementId,
						selectedOption ? selectedOption.textContent : "");
				}

				self._setNativeMenu(element, options.nativeMenu);
				self._setInline(element, options.inline);

				return element;
			};

			/**
			 * Build wrapper for whole UI structure
			 * @method _buildWrapper
			 * @protected
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._buildWrapper = function (element) {
				var selectWrapperElement = document.createElement("div");

				selectWrapperElement.className = classes.selectWrapper;
				selectWrapperElement.id = element.id + "-dropdownmenu";
				selectWrapperElement.setAttribute("tabindex", "0");

				domUtils.insertNodesBefore(element, selectWrapperElement);
				selectWrapperElement.appendChild(element);

				this._ui.elSelectWrapper = selectWrapperElement;
			};

			/**
			 * Build placeholder HTML structure
			 * @method _buildPlaceholder
			 * @protected
			 * @param {HTMLElement} element
			 * @param {HTMLElement} selectWrapperElement
			 * @param {string} elementId
			 * @param {string} text
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._buildPlaceholder = function (element, selectWrapperElement, elementId, text) {
				var placeholderElement = document.createElement("span");

				placeholderElement.id = elementId + "-placeholder";
				placeholderElement.className = classes.placeHolder;
				placeholderElement.textContent = text;

				selectWrapperElement.insertBefore(placeholderElement, element);
				this._ui.elPlaceHolder = placeholderElement;
			};

			/**
			 * Build HTML for filter structure
			 * @method _buildFilter
			 * @protected
			 * @param {HTMLElement} element
			 * @param {string} elementId
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._buildFilter = function (element, elementId) {
				var ui = this._ui,
					screenFilterElement = ui.screenFilter,
					optionWrapperElement = ui.elOptionWrapper,
					optionContainerElement = ui.elOptionContainer,
					fragment = document.createDocumentFragment();

				if (!screenFilterElement) {
					screenFilterElement = document.createElement("div");
					screenFilterElement.classList.add(classes.filter, classes.filterHidden);
					screenFilterElement.id = elementId + "-overlay";
					fragment.appendChild(screenFilterElement);
				}

				if (!optionWrapperElement) {
					optionWrapperElement = document.createElement("div");
					optionWrapperElement.className = classes.optionsWrapper;
					optionWrapperElement.id = elementId + "-options-wrapper";
					fragment.appendChild(optionWrapperElement);
				}

				if (!optionContainerElement) {
					optionContainerElement = document.createElement("ul"),
					optionContainerElement.className = classes.optionList;
					optionContainerElement.id = elementId + "-options";
					optionWrapperElement.appendChild(optionContainerElement);
				}
				ui.page.appendChild(fragment);

				ui.elOptionContainer = optionContainerElement;
				ui.elOptionWrapper = optionWrapperElement;
				ui.screenFilter = screenFilterElement;
			};

			/**
			 * Setter for option nativeMenu
			 * @method _setNativeMenu
			 * @protected
			 * @param {HTMLElement} element
			 * @param {boolean} value
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._setNativeMenu = function (element, value) {
				var self = this,
					ui = self._ui,
					optionElements,
					elOptionContainer,
					selectWrapperElement = ui.elSelectWrapper,
					optionsAsText;

				if (value) {
					optionElements = element.querySelectorAll("option");
					selectWrapperElement.classList.add(classes.native);
				} else {
					self._buildFilter(element, element.id);
					elOptionContainer = ui.elOptionContainer;
					optionsAsText = self._constructOption();
					elOptionContainer.innerHTML = optionsAsText;
					optionElements = elOptionContainer.querySelectorAll("li[data-value]");
					optionElements[self._selectedIndex].classList.add(classes.selected);
				}

				ui.elOptions = optionElements;
				self.options.nativeMenu = value;
			};

			/**
			 * Init of DropdownMenu widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui,
					elementId = element.id;

				if (!ui.elSelectWrapper) {
					ui.elSelectWrapper = document.getElementById(elementId + "-dropdownmenu");
					ui.elPlaceHolder = document.getElementById(elementId + "-placeholder");
					ui.elOptionWrapper = document.getElementById(elementId + "-options-wrapper");
					ui.elSelect = element;
					if (!self.options.nativeMenu) {
						ui.screenFilter = document.getElementById(elementId + "-overlay");
						ui.elOptionContainer = document.getElementById(elementId + "-options");
						ui.elOptions = ui.elOptionContainer.querySelectorAll("li[data-value]");
					}
				}
			};

			/**
			 * Refresh of DropdownMenu widget
			 * @method _refresh
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._refresh = function () {
				this._generate(this.element, false);
			};

			/**
			 * Enables widget
			 * @method _enable
			 *  @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._enable = function () {
				setDisabledStatus(this._ui.elSelectWrapper, false);
				domUtils.removeAttribute(this.element, "disabled");
			};

			/**
			 * Disables widget
			 * @method _disable
			 *  @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._disable = function () {
				setDisabledStatus(this._ui.elSelectWrapper, true);
				domUtils.setAttribute(this.element, "disabled", true);
			};

			/**
			 * Open DropdownMenu
			 * @method open
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype.open = function () {
				var self = this;

				if (self._isOpen === false) {
					self._toggleSelect();
				}
			};

			/**
			 * Close DropdownMenu
			 * @method close
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype.close = function () {
				var self = this;

				if (self._isOpen === true) {
					self._toggleSelect();
				}
			};

			/**
			 * Bind events of DropdownMenu widget
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._bindEvents = function () {
				var self = this,
					ui = self._ui,
					elOptionContainer = ui.elOptionContainer,
					elSelectWrapper = ui.elSelectWrapper;

				self._toggleMenuBound = toggleMenu.bind(null, self);
				self._changeOptionBound = changeOption.bind(null, self);
				self._onResizeBound = onResize.bind(null, self);
				self._nativeChangeOptionBound = nativeChangeOption.bind(null, self);
				self._focusBound = onFocus.bind(null, self);
				self._blurBound = onBlur.bind(null, self);

				elSelectWrapper.addEventListener("focus", self._focusBound);
				elSelectWrapper.addEventListener("blur", self._blurBound);
				if (!self.options.nativeMenu) {
					elSelectWrapper.addEventListener("vclick", self._toggleMenuBound);
					elOptionContainer.addEventListener("vclick", self._changeOptionBound);
					elOptionContainer.addEventListener("focusin", self._focusBound); // bubble
					elOptionContainer.addEventListener("focusout", self._blurBound); // bubble
					if (ui.screenFilter) {
						ui.screenFilter.addEventListener("vclick", self._toggleMenuBound);
					}
					window.addEventListener("throttledresize", self._onResizeBound, true);
				} else {
					ui.elSelect.addEventListener("change", self._nativeChangeOptionBound);
				}
			};

			/**
			 * Coordinate Option ul element
			 * @method _coordinateOption
			 * @return {string}
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._coordinateOption = function () {
				var self = this,
					offsetTop,
					offsetLeft,
					width,
					placeholderStyle = window.getComputedStyle(self._ui.elPlaceHolder, null),
					areaInfo,
					optionStyle,
					ui = self._ui,
					optionHeight = ui.elOptionContainer.offsetHeight,
					options = self.options,
					scrollTop = ui.elOptionWrapper.parentNode.querySelector(".ui-scrollview-clip").scrollTop,
					height;

				self._offset = getOffsetOfElement(ui.elSelectWrapper, ui.page);
				areaInfo = self._chooseDirection();
				// the option list width is shorter than the placeholder.
				width = ui.elPlaceHolder.offsetWidth - (parseFloat(placeholderStyle.paddingLeft) * 2) + "px;";
				height = optionHeight;
				// This part decides the location and direction of option list.
				offsetLeft = self._offset.left;
				optionStyle = "left: " + offsetLeft + "px; ";

				if (options.inline === true) {
					height = ui.elOptionContainer.children[0].offsetHeight * 5;
					width = "auto;";
				}

				if (areaInfo.direction === "top") {
					offsetTop = 0;
					if (height < areaInfo.topArea) {
						offsetTop = self._offset.top - height - scrollTop;
					} else {
						height = areaInfo.topArea;
					}
					ui.elOptionWrapper.classList.add(classes.top);
				} else {
					offsetTop = self._offset.top + ui.elPlaceHolder.offsetHeight - scrollTop;
					if (height > areaInfo.belowArea) {
						height = areaInfo.belowArea;
					}
					ui.elOptionWrapper.classList.add(classes.bottom);
				}
				optionStyle += "top: " + offsetTop + "px; width: " + width + " max-height: " + height + "px;";
				return optionStyle;
			};

			/**
			 * Choose a spreading direction of option list and calculate area to display the option list
			 * @method _chooseDirection
			 * @return {Object}
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._chooseDirection = function () {
				var self = this,
					ui = self._ui,
					areaInfo = {
						belowArea: 0,
						topArea: 0,
						direction: ""
					},
					currentOffset = self._offset;

				areaInfo.belowArea = ui.page.offsetHeight - currentOffset.top - ui.elPlaceHolder.offsetHeight + ui.content.scrollTop;
				areaInfo.topArea = currentOffset.top - ui.content.scrollTop;

				if ((areaInfo.belowArea < areaInfo.topArea) && (ui.elOptionContainer.offsetHeight > areaInfo.belowArea)) {
					areaInfo.direction = "top";
				} else {
					areaInfo.direction = "bottom";
				}
				return areaInfo;
			};

			/**
			 * Open and Close Option List
			 * @method _toggleSelect
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._toggleSelect = function () {
				var self = this,
					ui = self._ui,
					optionContainer = ui.elOptionContainer,
					optionWrapperClassList = ui.elOptionWrapper.classList;

				if (self._isOpen && !optionWrapperClassList.contains(classes.opening)) {
					optionWrapperClassList.remove(classes.opened);
					self._callbacks.hideAnimationEnd = hideAnimationEndHandler.bind(null, self);
					eventUtils.prefixedFastOn(optionContainer, "animationEnd", self._callbacks.hideAnimationEnd, false);
					self._hide();
					ui.elSelectWrapper.focus();
				} else if (optionWrapperClassList.contains(classes.closing) || optionWrapperClassList.contains(classes.opening)) {
					return;
				} else {
					ui.elSelectWrapper.focus();
					optionWrapperClassList.add(classes.opening);
					self._callbacks.showAnimationEnd = showAnimationEndHandler.bind(null, self);
					eventUtils.prefixedFastOn(optionContainer, "animationEnd", self._callbacks.showAnimationEnd, false);
					self._show();
				}
				self._isOpen = !self._isOpen;
			};

			/**
			 * Function animationEnd event handler when showing
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function showAnimationEndHandler(self) {
				var ui = self._ui;

				ui.elOptionWrapper.classList.add(classes.opened);
				eventUtils.prefixedFastOff(ui.elOptionContainer, "animationEnd", self._callbacks.showAnimationEnd, false);
				ui.elOptionWrapper.classList.remove(classes.opening);
			}

			/**
			 * Function animationEnd event handler when hiding
			 * @private
			 * @static
			 * @param {ns.widget.mobile.DropdownMenu} self
			 * @member ns.widget.mobile.DropdownMenu
			 */
			function hideAnimationEndHandler(self) {
				var wrapper = self._ui.elOptionWrapper,
					wrapperClassList = wrapper.classList,
					optionContainer = self._ui.elOptionContainer;

				wrapperClassList.remove(classes.active);
				wrapper.removeAttribute("style");
				eventUtils.prefixedFastOff(optionContainer, "animationEnd", self._callbacks.hideAnimationEnd, false);
				wrapperClassList.remove(classes.closing);
				wrapperClassList.remove(classes.top);
				wrapperClassList.remove(classes.bottom);
			}

			/**
			 * Hide DropdownMenu options
			 * @method _hide
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._hide = function () {
				var self = this,
					ui = self._ui,
					wrapper = ui.elOptionWrapper;

				if (ui.screenFilter) {
					ui.screenFilter.classList.add(classes.filterHidden);
				}
				self._ui.elSelectWrapper.classList.remove(classes.active);
				wrapper.classList.add(classes.closing);
			};

			/**
			 * Show DropdownMenu options
			 * @method _hide
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._show = function () {
				var self = this,
					ui = self._ui,
					wrapper = ui.elOptionWrapper;

				wrapper.setAttribute("style", self._coordinateOption());
				if (ui.screenFilter) {
					ui.screenFilter.classList.remove(classes.filterHidden);
				}
				ui.elSelectWrapper.classList.add(classes.active);
				wrapper.classList.add(classes.active);
				wrapper.setAttribute("tabindex", "0");
				wrapper.firstElementChild.focus();
			};

			/**
			 * Change Value of Select tag and Placeholder
			 * @method changeOption
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._changeOption = function () {
				var self = this,
					ui = self._ui,
					selectedOption = ui.elOptions[self._selectedIndex],
					previousOption = ui.elOptionContainer.querySelector("." + classes.selected),
					getData = domUtils.getNSData;

				if ((selectedOption !== previousOption) || (ui.elDefaultOption && (ui.elPlaceHolder.textContent === ui.elDefaultOption.textContent))) {
					ui.elPlaceHolder.textContent = selectedOption.textContent;
					ui.elSelect.value = getData(selectedOption, "value");
					if (ui.elSelect.value === "") {
						ui.elSelect.value = getData(previousOption, "value");
						ui.elPlaceHolder.textContent = previousOption.textContent;
						return;
					}
					eventUtils.trigger(ui.elSelect, "change");
					previousOption.classList.remove(classes.selected);
					selectedOption.classList.add(classes.selected);
				}
			};

			/**
			 * Destroy DropdownMenu widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.DropdownMenu
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self._ui,
					elSelectWrapper = ui.elSelectWrapper,
					elOptionContainer = ui.elOptionContainer,
					screenFilter = ui.screenFilter;

				elSelectWrapper.removeEventListener("focus", self._focusBound);
				elSelectWrapper.removeEventListener("blur", self._blurBound);
				domUtils.replaceWithNodes(ui.elSelectWrapper, ui.elSelect);
				if (!self.options.nativeMenu) {
					elSelectWrapper.removeEventListener("vclick", self._toggleMenuBound);
					elOptionContainer.removeEventListener("vclick", self._changeOptionBound);
					elOptionContainer.removeEventListener("focusin", self._focusBound);
					elOptionContainer.removeEventListener("focusout", self._blurBound);
					ui.elOptionWrapper.parentNode.removeChild(ui.elOptionWrapper);
					if (screenFilter) {
						screenFilter.removeEventListener("vclick", self._toggleMenuBound);
						screenFilter.parentNode.removeChild(screenFilter);
					}
					window.removeEventListener("throttledresize", self._onResizeBound, true);
				} else {
					ui.elSelect.removeEventListener("change", self._nativeChangeOptionBound);
				}
			};

			ns.widget.mobile.DropdownMenu = DropdownMenu;

			engine.defineWidget(
				"DropdownMenu",
				"select:not([data-role='slider']):not([data-role='range'])" +
				":not([data-role='toggleswitch']):not(.ui-toggleswitch):not(.ui-slider)," +
				"select.ui-select-menu:not([data-role='slider']):not([data-role='range'])" +
				":not([data-role='toggleswitch'])",
				["open", "close"],
				DropdownMenu,
				"mobile",
				false,
				false,
				HTMLSelectElement
			);

			}(window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Text Enveloper
 * Text enveloper component changes a text item to a button.
 *
 * The TextEnveloper component is that makes text to a chunk divided by delimiter.
 * When you managed various word block, this component is very useful.
 * This component was consisted by input area and word block area.
 * Word block was made after insert text to input area and press enter key.
 * If you want to delete word block, you should press the backspace key.
 * If you focus out the input area, word block is changed to minimize.
 *
 * ##HTML Examples
 * ###Create simple TextEnveloper from div using data-role:
 *
 *		@example
 *			<div data-role="textenveloper"></div>

 * ###Create simple TextEnveloper from div using class:
 *
 *		@example
 *			<div class="ui-text-enveloper"></div>
 *
 * ##Manual constructor
 * ###For manual creation of progressbar component you can use constructor
 * of component:
 *
 *		@example
 *			<div id="TextEnveloper"><div>
 *			 <script>
 *				var textEnveloper = tau.widget.TextEnveloper(
 *					document.getElementById('TextEnveloper')
 *				);
 *			</script>
 *
 * If jQuery library is loaded, it's method can be used:
 *
 *		@example
 *			<div id="TextEnveloper"><div>
 *			 <script>
 *				$("#TextEnveloper").TextEnveloper();
 *			</script>
 *
 * Initialize the component
 *
 *		@example
 *			<script>
 *				var textEnveloperElement = document.getElementById("ns-TextEnveloper"),
 *				TextEnveloper = tau.component.TextEnveloper(textEnveloperElement);
 *			</script>
 *
 * To call method on component you can use one of existing API:
 *
 * First API is from tau namespace:
 *
 *		@example
 *		var textEnveloperElement = document.getElementById("ns-tokentext"),
 *			TextEnveloper = tau.component.TextEnveloper(textEnveloperElement);
 *
 *		TextEnveloper.methodName(methodArgument1, methodArgument2, ...);
 *
 * Second API is jQuery Mobile API and for call _methodName_ you can use:
 *
 *		@example
 *		$(".selector").TextEnveloper("methodName", methodArgument1, ...);
 *
 * ##Events
 *
 * TextEnveloper trigger various events.
 * - newvalue : 'newvalue' event is triggered when user press the ENTER key after insert text to input tag.
 * - added: 'added' event is triggered when textEnveloper button was added.
 * - removed: 'removed' event is triggered when textEnveloper button was removed.
 *
 * @since 2.4
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 * @class ns.widget.mobile.TextEnveloper
 * @extends ns.widget.BaseWidget
 */

(function (window, ns) {
	"use strict";
	
			/**
			 * BaseWidget alias variable
			 * @property {Object} BaseWidget alias variable
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.mobile.BaseWidgetMobile,

				/**
				 * Engine alias variable
				 * @property {ns.engine} engine alias variable
				 * @private
				 * @static
				 * @member ns.widget.mobile.TextEnveloper
				 */
				engine = ns.engine,

				events = ns.event,
				/**
				 * Dictionary object containing commonly used component classes
				 * @property {Object} classes
				 * @static
				 * @private
				 * @readonly
				 * @member ns.widget.mobile.TextEnveloper
				 */
				classes = {
					TEXT_ENVELOPER: "ui-text-enveloper",
					WITH_CONTAINER: "ui-text-enveloper-with-container",
					CONTAINER: "ui-text-enveloper-container",
					TEXT_ENVELOPER_INPUT: "ui-text-enveloper-input",
					TEXT_ENVELOPER_BTN: "ui-text-enveloper-btn",
					BTN_SELECTED: "ui-text-enveloper-btn-selected",
					TEXT_ENVELOPER_BTN_ACTIVE: "ui-text-enveloper-btn-active",
					TEXT_ENVELOPER_BTN_BLUR: "ui-text-enveloper-btn-blur",
					TEXT_ENVELOPER_BTN_EXPANDED: "ui-text-enveloper-btn-expanded",
					TEXT_ENVELOPER_START: "ui-text-enveloper-start",
					TEXT_ENVELOPER_TEXTLINE: "ui-text-input-textline",
					SLASH: "ui-text-enveloper-slash",
					SLASH_HIDDEN: "ui-text-enveloper-slash-hidden",
					TEXT_ENVELOPER_BTN_SLASH: "ui-text-enveloper-btn-separator",
					INPUT_STYLE_PREFIX: "ui-text-enveloper-input-",
					INPUT_BLUR: "ui-text-enveloper-input-blur"
				},

				keyCode = {
					BACKSPACE: 8,
					ENTER: 13
				},

				eventName = {
					NEW_VALUE: "newvalue",
					ADDED: "added",
					REMOVED: "removed",
					SELECT: "select",
					UNSELECT: "unselect",
					RESIZE: "resize",
					EXPAND: "expand",
					FOLD: "fold"
				},
				/**
				 * Local constructor function
				 * @method TextEnveloper
				 * @private
				 * @member ns.widget.mobile.TextEnveloper
				 */
				TextEnveloper = function () {
					var self = this;
					/**
					 * Object with default options
					 * @property {Object} options
					 * @property {boolean} [options.groupOnBlur=true] Group elements when blur form input
					 * @property {string} [options.label=null] Sets a label as a guide for the user
					 * @property {string} [options.link=""] Sets the ID of the page or the URL of other HTML file
					 * @property {string} [options.description="+ {0}"] Manages the message format
					 * @property {boolean} [options.selectable=false] Give possibility of select elements
					 * @property {boolean|string} [options.input=true] Set input or define input style
					 * @property {string} [options.placeholder=null] Input placeholder
					 * @property {string} [options.labelPosition=null] Position of label: "indent" | null
					 * @property {string} [options.selectedItems=null] List with indexes of selected items
					 * @property {string} [options.items=null] List of items
					 * @member ns.widget.mobile.TextEnveloper
					 */

					self.options = {
						groupOnBlur: true,
						label: null,
						link: "",
						description: "+ {0}",
						selectable: false,
						input: true,
						placeholder: null,
						labelPosition: null,
						selectedItems: null,
						items: null
					};
					self._ui = {};
				},

				prototype = new BaseWidget();

			TextEnveloper.prototype = prototype;

			TextEnveloper.classes = classes;

			function bindEvents(self) {
				var inputElement = self._ui.inputElement;

				if (inputElement) {
					events.on(inputElement, "keyup", self);
					if (self.options.groupOnBlur) {
						events.on(inputElement, "blur focus", self);
					}
				}
				self.on("click", self);
			}

			function unbindEvents(self) {
				var inputElement = self._ui.inputElement;

				if (inputElement) {
					events.off(inputElement, "keyup", self);
					if (self.options.groupOnBlur) {
						events.off(inputElement, "blur focus", self);
					}
				}
				self.off("click", self);
			}

			/**
			 * Build component structure
			 * @method _build
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options;

				element.classList.add(classes.TEXT_ENVELOPER);

				self._setLabel(element, options.label);
				self._setLabelPosition(element, options.labelPosition);

				ui.container.classList.add(classes.CONTAINER);
				ui.buttons = [];

				self._setInput(element, options.input);
				self._setPlaceholder(element, options.placeholder);

				return element;
			};

			prototype._setLabelPosition = function (element, labelPosition) {
				var self = this,
					ui = self._ui,
					containerElement;

				if (labelPosition === "indent") {
					containerElement = document.createElement("div");
					element.appendChild(containerElement);
					ui.container = containerElement;
					element.classList.add(classes.WITH_CONTAINER);
				} else {
					ui.container = element;
				}

				self.options.labelPosition = labelPosition;
			};

			prototype._setLabel = function (element, label) {
				var title = element.querySelector("." + classes.TEXT_ENVELOPER_START),
					//if title is defined (usually its described as To, Cc, Bcc)
					//then place it in the proper position
					tempTitle = (title) ? title.cloneNode(true) : null;

				if (tempTitle) {
					element.removeChild(title);
					element.appendChild(tempTitle);
				}

				if (label) {
					tempTitle = tempTitle || document.createElement("div");
					tempTitle.innerText = label;
					tempTitle.classList.add(classes.TEXT_ENVELOPER_START);
					element.appendChild(tempTitle);
				}

				this.options.label = label;
			};

			/**
			 * Init component
			 * @method _init
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._init = function (element) {
				var self = this;

				self._btnActive = false;
				self._isBlurred = false;

				return element;
			};

			/**
			 * Bind event handler
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._bindEvents = function () {
				bindEvents(this);
			};

			/**
			 * Bind event handler
			 * @method handleEvent
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "click":
						self._onClick(event);
						break;
					case "keyup":
						self._onKeyup(event);
						break;
					case "blur":
						self._onBlur(event);
						break;
					case "focus":
						self._onFocus(event);
						break;
				}
			};

			/**
			 * Focus event handler of input element
			 * @method _onFocus
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._onFocus = function () {
				this.expandButtons();
			};

			/**
			 * Method used to show Text Enveloper items
			 * @method expandButtons
			 * @since 3.0
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.expandButtons = function () {
				var self = this,
					ui = self._ui,
					length,
					i;

				if (self._isBlurred) {
					self._isBlurred = false;
					self.remove(self._btnToRemoveIndex);
					ui.inputContainer.classList.remove(classes.INPUT_BLUR);
					length = ui.buttons.length;

					for (i = 0; i < length; i++) {
						ui.buttons[i].classList.remove(classes.TEXT_ENVELOPER_BTN_BLUR);
					}

					self.trigger(eventName.RESIZE);
					self.trigger(eventName.EXPAND);
				}
			};

			/**
			 * Focus event handler of input element
			 * @method _onFocus
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._onClick = function (event) {
				var self = this,
					target = event.target,
					targetClassList = target.classList,
					previousElement = target.previousElementSibling,
					nextElement = target.nextElementSibling,
					nextButtonElement = nextElement && nextElement.nextElementSibling,
					previousButtonElement = previousElement && previousElement.previousElementSibling,
					previousElementClassList,
					nextElementClassList,
					eventNameForTrigger;

				if (!self._isBlurred) {
					if (self.options.selectable && targetClassList.contains(classes.TEXT_ENVELOPER_BTN)) {
						previousElementClassList = previousElement && previousElement.classList;
						nextElementClassList = nextElement && nextElement.classList;
						if (targetClassList.contains(classes.BTN_SELECTED)) {
							if (previousButtonElement &&
								!previousButtonElement.classList.contains(classes.BTN_SELECTED)) {
								previousElementClassList.remove(classes.SLASH_HIDDEN);
							}
							if (nextButtonElement &&
								!nextButtonElement.classList.contains(classes.BTN_SELECTED)) {
								nextElementClassList.remove(classes.SLASH_HIDDEN);
							}
							eventNameForTrigger = eventName.UNSELECT;
						} else {
							if (previousElementClassList) {
								previousElementClassList.add(classes.SLASH_HIDDEN);
							}
							if (nextElementClassList) {
								nextElementClassList.add(classes.SLASH_HIDDEN);
							}
							eventNameForTrigger = eventName.SELECT;
						}
						targetClassList.toggle(classes.BTN_SELECTED);
						self.trigger(eventNameForTrigger, {
							value: target.textContent,
							index: self._ui.buttons.indexOf(target)
						}, false);
						event.preventDefault();
						event.stopPropagation();
					}
				} else {
					self.expandButtons();
					event.preventDefault();
					event.stopPropagation();
				}
			};

			prototype._getItems = function () {
				return this._ui.buttons.map(function (item) {
					return item.textContent;
				});
			};

			prototype._setItems = function (element, value) {
				var self = this,
					itemsArray = typeof value === "string" ? value.split(",") : value;

				if (itemsArray) {
					this._ui.buttons.forEach(function () {
						self.remove(0);
					});
					itemsArray.forEach(function (item) {
						self.add(item);
					});
				}
			};

			prototype._getSelectedItems = function () {
				var result = [];

				this._ui.buttons.forEach(function (item, index) {
					if (item.classList.contains(classes.TEXT_ENVELOPER_BTN_SELECTED)) {
						result.push({
							value: item.textContent,
							index: index
						});
					}
				});

				return result;
			};

			prototype._setSelectedItems = function (element, value) {
				var selectedArray = typeof value === "string" ? value.split(",") : value;

				if (selectedArray) {
					this._ui.buttons.forEach(function (item, index) {
						item.classList.toggle(classes.TEXT_ENVELOPER_BTN_SELECTED,
							selectedArray.indexOf(index) !== -1);
					});
				}
			};

			/**
			 * Blur event handler of input element
			 * @method _onBlur
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._onBlur = function () {
				var self = this,
					input = self._ui.inputElement;

				if (input && input.value) {
					self.trigger(eventName.NEW_VALUE, {
						value: input.value
					}, false);

					input.value = "";
				}
				self.foldButtons();
			};

			/**
			 * Method used to hide Text Enveloper items
			 * @method foldButtons
			 * @since 3.0
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.foldButtons = function () {
				var self = this,
					ui = self._ui,
					buttons = ui.buttons,
					length = buttons.length,
					firstButtonValue = buttons[0] ? buttons[0].textContent : "",
					i,
					button,
					separatorNode,
					lengthTextNode;

				if (!self._isBlurred) {
					for (i = 0; i < length; i++) {
						buttons[i].classList.add(classes.TEXT_ENVELOPER_BTN_BLUR);
					}

					ui.inputContainer.classList.add(classes.INPUT_BLUR);
					button = self._createButton(firstButtonValue, false);
					self._btnToRemoveIndex = buttons.indexOf(button);

					if (length > 1) {
						separatorNode = document.createElement("span");
						separatorNode.classList.add(classes.TEXT_ENVELOPER_BTN_SLASH);

						lengthTextNode = document.createTextNode("+" + (length - 1));

						button.appendChild(separatorNode);
						button.appendChild(lengthTextNode);
					}

					self._isBlurred = true;
					self.trigger(eventName.RESIZE);
					self.trigger(eventName.FOLD);
				}
			};

			/**
			 * keyup event handler of input element
			 * @method _onKeyup
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._onKeyup = function (event) {
				var self = this,
					ui = self._ui,
					input = ui.inputElement,
					value = input.value,
					keyValue = event.keyCode,
					lastIndex = ui.buttons.length - 1;

				if (keyValue === keyCode.ENTER) {
					self.trigger(eventName.NEW_VALUE, {
						value: value
					}, false);
					input.value = "";
					self.trigger(eventName.RESIZE);
				} else if (keyValue === keyCode.BACKSPACE) {
					if (value === "") {
						if (self._btnActive) {
							self.remove(lastIndex);
							self._btnActive = false;
						} else {
							if (ui.buttons.length) {
								ui.buttons[lastIndex].classList.add(classes.TEXT_ENVELOPER_BTN_ACTIVE);
								self._btnActive = true;
							}
						}
					}
					self.trigger(eventName.RESIZE);
				} else {
					if (self._btnActive) {
						ui.buttons[lastIndex].classList.remove(classes.TEXT_ENVELOPER_BTN_ACTIVE);
						self._btnActive = false;
					}
				}
			};

			/**
			 * Create button as used to word block
			 * @method _onKeyup
			 * @param {string} value
			 * @param {boolean} [inline=true] set inline in button
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._createButton = function (value, inline) {
				var self = this,
					ui = self._ui,
					button = document.createElement("div"),
					buttons = ui.buttons;

				if (inline === undefined) {
					inline = true;
				}
				button.innerText = value;
				button.classList.add(classes.TEXT_ENVELOPER_BTN);
				engine.instanceWidget(button, "Button", {
					inline: inline
				});
				ui.container.insertBefore(button, self._ui.inputContainer);
				buttons.push(button);
				self.trigger(eventName.ADDED, {
					value: value,
					index: buttons.length - 1
				}, false);
				return button;
			};

			/**
			 * Create slash to appear after button
			 * @method _createSlash
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._createSlash = function () {
				var ui = this._ui,
					span = document.createElement("span");

				span.classList.add(classes.SLASH);
				ui.container.insertBefore(span, ui.inputContainer);
				return span;
			};

			/**
			 * Method add block
			 *
			 * Method adds new token text component button with specified text
			 * in place of the index. If index isn't set the token will
			 * be inserted at the end.
			 *
			 *		@example
			 *			<div data-role="TextEnveloper" id="ns-tokentext"></div>
			 *			<script>
			 *				var tokenComponent = tau.component.TextEnveloper(
			 *						document.getElementById("ns-tokentext")
			 *				);
			 *				tokenComponent.add("foobar");
			 *
			 *				//or
			 *
			 *				$( "#ns-tokentext" ).TextEnveloper("add", "foobar");
			 *			</script>
			 *
			 * @method add
			 * @param {string} itemText
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.add = function (itemText) {
				var self = this,
					items = self._getItems(self.element);

				if (items.indexOf(itemText) === -1) {
					self._createButton(itemText);
					self._createSlash();
				}
			};

			/**
			 * Method delete token; delete all tokens without parameter
			 *
			 * The remove method is used to remove a token text area component
			 * button at the specified index position. If the parameter
			 * is not defined, all the component buttons are removed.
			 *
			 *		@example
			 *			<div 	data-role="TextEnveloper"
			 *					data-label="Send to: "
			 *					id="ns-tokentext">
			 *			</div>
			 *			<script>
			 *				var tokenComponent = tau.component.TextEnveloper(
			 *						document.getElementById("ns-tokentext")
			 *				);
			 *				tokenComponent.remove(1);
			 *
			 *				//or
			 *
			 *				$( "#ns-tokentext" ).TextEnveloper("remove", "1" );
			 *			</script>
			 *
			 * @method remove
			 * @param {number} index
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.remove = function (index) {
				var self = this,
					ui = self._ui,
					buttons = ui.buttons,
					length = buttons.length,
					innerText = buttons[index].innerText,
					container = ui.container,
					validLength = self._isBlurred ? length - 2 : length - 1;

				if (index < 0 || index > validLength) {
					ns.warn("You insert incorrect index, please check your index value");
				} else {
					if (self._isBlurred) {
						if (length > 2) {
							buttons[length - 1].textContent = buttons[0].textContent + " + " + (length - 2);
						} else if (length === 2) {
							container.removeChild(buttons[length - 1]);
							buttons.pop();
							buttons[0].classList.remove(classes.TEXT_ENVELOPER_BTN_BLUR);
						}
					} else {
						if (buttons[index].nextElementSibling.classList.contains(classes.SLASH)) {
							container.removeChild(buttons[index].nextElementSibling);
						}
						container.removeChild(buttons[index]);
						buttons.splice(index, 1);
					}
				}

				self.trigger(eventName.REMOVED, {
					value: innerText,
					index: index
				});
			};
			/**
			 * Function return blocks count
			 *
			 * The length method is used to retrieve the number of buttons
			 * in the token text area component:
			 *
			 *		@example
			 *			<div data-role="TextEnveloper" id="ns-tokentext"></div>
			 *			<script>
			 *				var tokenComponent = tau.component.TextEnveloper(
			 *						document.getElementById("ns-tokentext")
			 *				);
			 *				tokenComponent.length();
			 *
			 *				//if jQuery is loaded
			 *
			 *				$( "#ns-tokentext" ).TextEnveloper( "length" );
			 *			</script>
			 *
			 * @method length
			 * @return {number}
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype.length = function () {
				return this._ui.buttons.length;
			};

			prototype._setInput = function (element, addInput) {
				var self = this,
					ui = self._ui,
					input,
					textLineElement,
					inputContainer,
					container = ui.container;

				if (addInput) {
					inputContainer = document.createElement("div");
					input = document.createElement("input");
					input.classList.add(classes.TEXT_ENVELOPER_INPUT);

					inputContainer.appendChild(input);
					container.appendChild(inputContainer);

					engine.instanceWidget(input, "TextInput", {
						clearBtn: true
					});
					ui.inputElement = input;
					ui.inputContainer = inputContainer;

					if (typeof addInput === "string") {
						inputContainer.classList.add(classes.INPUT_STYLE_PREFIX + addInput);
					} else {
						textLineElement = element.querySelector("." + classes.TEXT_ENVELOPER_TEXTLINE);
						textLineElement.parentElement.removeChild(textLineElement);
					}
				} else {
					inputContainer = ui.inputContainer;
					if (inputContainer) {
						container.removeChild(inputContainer);

						engine.destroyWidget(ui.inputElement, "TextInput");

						ui.inputContainer = null;
						ui.inputElement = null;
					}
				}

				self.options.input = addInput;
			};

			prototype._setPlaceholder = function (element, placeholder) {
				var input = this._ui.inputElement;

				if (input) {
					input.setAttribute("placeholder", placeholder || "");
				}

				this.options.placeholder = placeholder;
			};


			/**
			 * Destroy component
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.TextEnveloper
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self._ui;

				ui.container.classList.remove(classes.CONTAINER);
				self._setInput(self.element, false);
				unbindEvents(self);
				self._ui = null;
			};

			prototype.getInput = function () {
				return this._ui.inputElement;
			};

			ns.widget.mobile.TextEnveloper = TextEnveloper;
			engine.defineWidget(
				"TextEnveloper",
				"[data-role='textenveloper'], .ui-text-enveloper",
				[
					"add",
					"remove",
					"length"
				],
				TextEnveloper,
				"mobile"
			);

			}(window.document, ns));

/*global window, ns, define*/
/*jslint bitwise: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @class ns.support
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window, document) {
	"use strict";
				var isTizen = !(typeof window.tizen === "undefined");

			function isCircleShape() {
				var testDivElement = document.createElement("div"),
					fakeBodyElement = document.createElement("body"),
					htmlElement = document.getElementsByTagName("html")[0],
					style = getComputedStyle(testDivElement),
					isCircle;

				testDivElement.classList.add("is-circle-test");
				fakeBodyElement.appendChild(testDivElement);
				htmlElement.insertBefore(fakeBodyElement, htmlElement.firstChild);
				isCircle = style.width === "1px";
				htmlElement.removeChild(fakeBodyElement);

				// to support circle in  browser by additional parameter in url
				/* istanbul ignore if */
				if (window.location.search === "?circle") {
					/* istanbul ignore next: we can't test this part because set location is not supported in
					 test environment */
					isCircle = true;
				}

				return isCircle;
			}

			ns.support = {
				cssTransitions: true,
				mediaquery: true,
				cssPseudoElement: true,
				touchOverflow: true,
				cssTransform3d: true,
				boxShadow: true,
				scrollTop: 0,
				dynamicBaseTag: true,
				cssPointerEvents: false,
				boundingRect: true,
				browser: {
					ie: false,
					tizen: isTizen
				},
				shape: {
					circle: isTizen ? window.matchMedia("(-tizen-geometric-shape: circle)").matches :
						isCircleShape()
				},
				gradeA: function () {
					return true;
				},
				isCircleShape: isCircleShape
			};
			}(window, window.document));

/*global window, ns, define */
(function (window, document, ns) {
	"use strict";
				var PI = Math.PI,
				cos = Math.cos,
				sin = Math.sin,
				SVGNS = "http://www.w3.org/2000/svg",
				objectUtils = ns.util.object,
				classes = {
					polar: "ui-polar",
					animated: "ui-animated"
				},
				defaultsArc = {
					x: 180,
					y: 180,
					r: 170,
					arcStart: 0,
					arcEnd: 90,
					width: 5,
					color: "black",
					animation: false,
					linecap: "butt",
					referenceDegree: 0
				},
				defaultsRadius = {
					x: 180,
					y: 180,
					r: 170,
					degrees: 0,
					length: 180,
					direction: "in",
					width: 5,
					color: "black"
				},
				defaultsText = {
					x: 180,
					y: 180,
					text: "Text",
					position: "middle",
					color: "white"
				},
				defaultsCircle = {
					x: 180,
					y: 180,
					r: 170,
					color: "white"
				},
				polar;

			/**
			 * Calculate polar coords to cartesian
			 * @param {number} centerX
			 * @param {number} centerY
			 * @param {number} radius
			 * @param {number} angleInDegrees
			 * @return {{x: number, y: number}}
			 */
			function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
				var angleInRadians = angleInDegrees * PI / 180.0;

				return {
					x: centerX + (radius * sin(angleInRadians)),
					y: centerY - (radius * cos(angleInRadians))
				};
			}

			/**
			 * Create description of path for arc
			 * @param {number} x
			 * @param {number} y
			 * @param {number} radius
			 * @param {number} startAngle Angle in degrees where arc starts
			 * @param {number} endAngle Angle in degrees where arc ends
			 * @return {string}
			 */
			function describeArc(x, y, radius, startAngle, endAngle) {
				var start = polarToCartesian(x, y, radius, endAngle),
					end = polarToCartesian(x, y, radius, startAngle),
					arcSweep = endAngle - startAngle <= 180 ? "0" : "1",
					clockWise = 0;

				return [
					"M", start.x, start.y,
					"A", radius, radius, 0, arcSweep, clockWise, end.x, end.y
				].join(" ");
			}

			function addPath(svg, options) {
				var path = document.createElementNS(SVGNS, "path");

				path.setAttribute("class", options.classes);
				path.setAttribute("fill", "none");
				path.setAttribute("stroke", options.color);
				path.setAttribute("stroke-width", options.width);
				path.setAttribute("d", describeArc(options.x, options.y, options.r,
					options.referenceDegree + options.arcStart, options.referenceDegree + options.arcEnd));
				path.setAttribute("data-initial-degree", options.referenceDegree);
				path.setAttribute("stroke-linecap", options.linecap);

				svg.appendChild(path);
			}

			function addAnimation(element, options) {
				var style = element.style,
					value = options.x + "px " + options.y + "px",
					degrees = (options.referenceDegree + options.arcStart) || options.degrees;

				// phantom not support classList on SVG
				if (element.classList) {
					// add class for transition
					element.classList.add(classes.animated);
				}

				// set transform
				style.webkitTransformOrigin = value;
				style.mozTransformOrigin = value;
				style.transformOrigin = value;

				value = "rotate(" + degrees + "deg)";
				style.webkitTransform = value;
				style.mozTransform = value;
				style.transform = value;
			}

			function addRadius(svg, options) {
				var line = document.createElementNS(SVGNS, "line"),
					positionStart,
					positionEnd;

				line.setAttribute("class", options.classes);
				line.setAttribute("stroke", options.color);
				line.setAttribute("stroke-width", options.width);
				if (options.direction === "out") {
					positionStart = polarToCartesian(options.x, options.y, options.r, options.degrees);
					positionEnd = polarToCartesian(options.x, options.y, options.r - options.length,
						options.degrees);
				} else {
					positionStart = polarToCartesian(options.x, options.y, options.r - options.length,
						options.degrees);
					positionEnd = polarToCartesian(options.x, options.y, options.r, options.degrees);
				}
				line.setAttribute("x1", positionStart.x);
				line.setAttribute("y1", positionStart.y);
				line.setAttribute("x2", positionEnd.x);
				line.setAttribute("y2", positionEnd.y);

				svg.appendChild(line);
				return line;
			}

			function addText(svg, options) {
				var text = document.createElementNS(SVGNS, "text");

				text.setAttribute("x", options.x);
				text.setAttribute("y", options.y);
				text.setAttribute("text-anchor", options.position);
				text.setAttribute("fill", options.color);
				text.setAttribute("transform", options.transform);
				text.textContent = options.text;

				svg.appendChild(text);
			}

			function addCircle(svg, options) {
				var circle = document.createElementNS(SVGNS, "circle");

				circle.setAttribute("stroke", options.color);
				circle.setAttribute("stroke-width", options.width);
				circle.setAttribute("cx", options.x);
				circle.setAttribute("cy", options.y);
				circle.setAttribute("r", options.r);
				circle.setAttribute("fill", options.fill);

				svg.appendChild(circle);
				return circle;
			}

			function updatePathPosition(path, options) {
				var reference;

				if (options.animation) {
					addAnimation(path, options);
				} else {
					if (path) {
						reference = parseInt(path.getAttribute("data-initial-degree"), 10) || options.referenceDegree;
						path.setAttribute("data-initial-degree", reference);
						path.setAttribute("d", describeArc(options.x, options.y, options.r,
							reference + options.arcStart, reference + options.arcEnd));
					}
				}
			}

			function updateLinePosition(line, options) {
				var positionStart,
					positionEnd;

				if (options.animation) {
					addAnimation(line, options);
				} else {
					if (line) {
						positionStart = polarToCartesian(options.x, options.y, options.r, options.degrees);
						positionEnd = polarToCartesian(options.x, options.y, options.r - options.length, options.degrees);

						line.setAttribute("x1", positionStart.x);
						line.setAttribute("y1", positionStart.y);
						line.setAttribute("x2", positionEnd.x);
						line.setAttribute("y2", positionEnd.y);
					}
				}
			}

			polar = {
				default: {
					arc: defaultsArc,
					radius: defaultsRadius,
					text: defaultsText
				},
				classes: classes,

				polarToCartesian: polarToCartesian,

				/**
				 * creates SVG element
				 * @method createSVG
				 * @member ns.util.polar
				 * @param {HTMLElement} element
				 * @return {SVGElement}
				 * @static
				 */
				createSVG: function (element) {
					var svg = document.createElementNS(SVGNS, "svg");

					// phantom not support classList on SVG
					if (svg.classList) {
						// add class to svg element
						svg.classList.add(classes.polar);
					}
					// if element is set, add svg as child node
					if (element) {
						element.appendChild(svg);
					}
					return svg;
				},

				/**
				 * draw arc on the svg element
				 * @method addArc
				 * @member ns.util.polar
				 * @param {SVGElement} svg
				 * @param {Object} options
				 * @return {SVGElement}
				 * @static
				 */
				addArc: function (svg, options) {
					// read or create new svg
					svg = svg || this.createSVG();
					// set options
					options = objectUtils.merge({}, defaultsArc, options || {});
					// add path with arc
					addPath(svg, options);

					return svg;
				},

				/**
				 * draw radius on the svg element
				 * @method addRadius
				 * @member ns.util.polar
				 * @param {SVGElement} svg
				 * @param {Object} options
				 * @return {SVGElement}
				 * @static
				 */
				addRadius: function (svg, options) {
					// read or create new svg
					svg = svg || this.createSVG();
					// add path with radius
					options = objectUtils.merge({}, defaultsRadius, options || {});
					return addRadius(svg, options);
				},

				/**
				 * draw text on the svg element
				 * @method addText
				 * @member ns.util.polar
				 * @param {SVGElement} svg
				 * @param {Object} options
				 * @return {SVGElement}
				 * @static
				 */
				addText: function (svg, options) {
					// read or create new svg
					svg = svg || this.createSVG();
					// add path with radius
					options = objectUtils.merge({}, defaultsText, options || {});
					addText(svg, options);

					return svg;
				},

				/**
				 * updatePosition for path or for line drawings in svg
				 * @method updatePosition
				 * @member ns.util.polar
				 * @param {SVGElement} svg
				 * @param {string} selector
				 * @param {Object} options
				 * @static
				 */
				updatePosition: function (svg, selector, options) {
					var path = svg && svg.querySelector("path" + selector),
						line;

					if (path) {
						// set options
						options = objectUtils.merge({}, defaultsArc, options || {});
						updatePathPosition(path, options);
					} else {
						line = svg && svg.querySelector("line" + selector);
						if (line) {
							updateLinePosition(line, options);
						}
					}
				},

				/**
				 * draw circle on the svg element
				 * @method addCircle
				 * @member ns.util.polar
				 * @param {SVGElement} svg
				 * @param {Object} options
				 * @return {SVGElement}
				 * @static
				 */
				addCircle: function (svg, options) {
					var self = this;

					// read or create svg
					svg = svg || self.createSVG();

					options = objectUtils.merge({}, defaultsCircle, options || {});
					addCircle(svg, options);

					return svg;
				}
			};

			ns.util.polar = polar;
			}(window, window.document, ns));

/* global requestAnimationFrame, define, ns, Math */
/**
 * # JS base scrolling tool
 *
 * This enable fast scrolling on element
 *
 * @class ns.util.scrolling
 */
(function (document, window, ns) {
	"use strict";
				var eventUtil = ns.event,
				polarUtil = ns.util.polar,
				selectorUtil = ns.util.selectors,
				classes = {
					circular: "scrolling-circular",
					direction: "scrolling-direction",
					scrollbar: "scrolling-scrollbar",
					path: "scrolling-path",
					thumb: "scrolling-scrollthumb",
					fadeIn: "fade-in",
					container: "scrolling-container"
				},
				bounceBack = false,
				EVENTS = {
					SCROLL_BEFORE_START: "beforeScrollStart",
					SCROLL_START: "scrollStart",
					SCROLL_END: "scrollEnd",
					SCROLL_FLICK: "flick",
					SCROLL: "scroll"
				},
				// 1px line space from screen edge
				RADIUS = 174,
				// position when was last touch start
				startPosition = 0,
				// current state of scroll position
				scrollPosition = 0,
				lastScrollPosition = 0,
				moveToPosition = 0,
				lastRenderedPosition = 0,
				lastTime = Date.now(),
				elementStyle = null,
				maxScrollPosition = 0,
				// scrolling element
				scrollingElement = null,
				childElement = null,
				// cache of previous overflow style to revert after disable
				previousOverflow = "",
				// cache abs function
				abs = Math.abs,
				// inform that is touched
				isTouch = false,
				isScrollableTarget = false,
				// direction of scrolling, 0 - mean Y, 1 - mean X
				direction = 0,
				// cache of round function
				round = Math.round,
				// cache max function
				max = Math.max,
				min = Math.min,

				// Circular scrollbar config
				CIRCULAR_SCROLL_BAR_SIZE = 60, // degrees
				CIRCULAR_SCROLL_MIN_THUMB_SIZE = 6,

				// Scrollbar is placed after scrolled element
				// that's why normal css values cannot be applied
				// margin needs to be subtracted from position
				SCROLL_MARGIN = 11,

				// ScrollBar variables
				scrollBar = null,
				scrollThumb = null,
				scrollBarPosition = 0,
				maxScrollBarPosition = 0,
				circularScrollBar = ns.support.shape.circle,
				circularScrollThumbSize = CIRCULAR_SCROLL_MIN_THUMB_SIZE,
				svgScrollBar = null,
				scrollBarTimeout = null,
				fromAPI = false,
				virtualMode = false,
				snapSize = null,
				requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;

			/**
			 * Shows scrollbar using fadeIn class and sets timeout to hide it when not used
			 */
			function fadeInScrollBar() {
				if (scrollBar) {
					clearTimeout(scrollBarTimeout);
					scrollBar.classList.add(classes.fadeIn);

					scrollBarTimeout = setTimeout(function () {
						if (scrollBar) {
							scrollBar.classList.remove(classes.fadeIn);
						}
					}, 2000);
				}
			}

			/**
			 * Check that current target is inside scrolling element
			 * @param {HTMLElement} target
			 * @return {boolean}
			 */
			function detectTarget(target) {
				while (target && target !== document) {
					if (target === scrollingElement) {
						return true;
					}
					target = target.parentElement;
				}
				return false;
			}

			/**
			 * Handler for touchstart event
			 * @param {Event} event
			 */
			function touchStart(event) {
				var touches = event.touches,
					touch = touches[0];

				isScrollableTarget = detectTarget(event.target);
				// is is only one touch
				if (isScrollableTarget && touches.length === 1) {
					// save current touch point
					startPosition = direction ? touch.clientX : touch.clientY;
					// save current time for calculate acceleration on touchend
					lastTime = Date.now();
					eventUtil.trigger(scrollingElement, EVENTS.SCROLL_BEFORE_START, {
						scrollLeft: direction ? -scrollPosition : 0,
						scrollTop: direction ? 0 : -scrollPosition,
						fromAPI: fromAPI
					});
				}
			}

			function touchMoveMain(event) {
				var touches = event.touches,
					touch = touches[0],
					// get current position in correct direction
					clientPosition = direction ? touch.clientX : touch.clientY,
					scrollLeft = 0,
					scrollTop = 0;

				fromAPI = false;
				// calculate difference between touch start and current position
				lastScrollPosition = clientPosition - startPosition;
				if (!bounceBack) {
					// normalize value to be in bound [0, maxScroll]
					if (scrollPosition + lastScrollPosition > 0) {
						lastScrollPosition = -scrollPosition;
					}
					if (scrollPosition + lastScrollPosition < -maxScrollPosition) {
						lastScrollPosition = -maxScrollPosition - scrollPosition;
					}
				}

				if (direction) {
					scrollLeft = -(scrollPosition + lastScrollPosition);
				} else {
					scrollTop = -(scrollPosition + lastScrollPosition);
				}

				// trigger event scroll start if it is the first touch move
				if (!isTouch) {
					eventUtil.trigger(scrollingElement, EVENTS.SCROLL_START, {
						scrollLeft: scrollLeft,
						scrollTop: scrollTop,
						fromAPI: fromAPI
					});
				}

				// trigger event scroll
				eventUtil.trigger(scrollingElement, EVENTS.SCROLL, {
					scrollLeft: scrollLeft,
					scrollTop: scrollTop,
					inBounds: (scrollPosition + lastScrollPosition >= -maxScrollPosition) &&
					(scrollPosition + lastScrollPosition <= 0),
					fromAPI: fromAPI
				});
				fadeInScrollBar();
			}

			/**
			 * Handler for touchmove event
			 * @param {Event} event
			 */
			function touchMove(event) {
				var touches = event.touches;

				// if touch start was on scrolled element
				if (isScrollableTarget) {
					// if is only one touch
					if (touches.length === 1) {
						touchMoveMain(event);
					}
					// if this is first touch move
					if (!isTouch) {
						// we need start request loop
						isTouch = true;
					}
					requestAnimationFrame(render);
				}
			}

			function touchEndCalculateSpeed(inBounds) {
				var diffTime = Date.now() - lastTime;

				if (inBounds && abs(lastScrollPosition / diffTime) > 1) {
					// if it was fast move, we start animation of scrolling after touch end
					moveToPosition = max(min(round(scrollPosition + 1000 * lastScrollPosition / diffTime),
						0), -maxScrollPosition);
					if (snapSize) {
						moveToPosition = snapSize * round(moveToPosition / snapSize);
					}
					if (abs(lastScrollPosition / diffTime) > 1) {
						eventUtil.trigger(scrollingElement, EVENTS.SCROLL_FLICK, {
							scrollLeft: direction ? -moveToPosition : 0,
							scrollTop: direction ? 0 : -moveToPosition,
							fromAPI: fromAPI
						});
					}
					requestAnimationFrame(moveTo);
				} else {
					// touch move was slow
					if (snapSize) {
						moveToPosition = snapSize * round(scrollPosition / snapSize);
						requestAnimationFrame(moveTo);
					}
					isTouch = false;
				}
			}

			function touchEndCalculatePosition(inBounds) {
				if (bounceBack) {
					if (!inBounds) {
						// if it was fast move, we start animation of scrolling after touch end
						if (scrollPosition > 0) {
							moveToPosition = 0;
						} else {
							moveToPosition = -maxScrollPosition;
						}
						requestAnimationFrame(moveTo);
					}
				} else {
					// normalize value to be in bound [0, maxScroll]
					if (scrollPosition < -maxScrollPosition) {
						scrollPosition = -maxScrollPosition;
					}
					if (scrollPosition > 0) {
						scrollPosition = 0;
					}
				}
			}

			function touchEndTriggerEvents(details) {
				eventUtil.trigger(scrollingElement, EVENTS.SCROLL, details);
				eventUtil.trigger(scrollingElement, EVENTS.SCROLL_END, details);
			}

			/**
			 * Handler for touchend event
			 */
			function touchEnd() {
				var inBounds,
					scrollLeft = 0,
					scrollTop = 0;
				// only if the event touchmove was noticed before

				if (isTouch) {
					// update state of scrolling
					scrollPosition += lastScrollPosition;
					inBounds = (scrollPosition >= -maxScrollPosition) && (scrollPosition <= 0);

					// calculate speed of touch move
					touchEndCalculateSpeed(inBounds);
					touchEndCalculatePosition(inBounds);

					if (direction) {
						scrollLeft = -(scrollPosition);
					} else {
						scrollTop = -(scrollPosition);
					}

					lastScrollPosition = 0;
					// trigger event scroll
					touchEndTriggerEvents({
						scrollLeft: scrollLeft,
						scrollTop: scrollTop,
						inBounds: inBounds,
						fromAPI: fromAPI
					});
					fadeInScrollBar();
					// we stop scrolling
					isScrollableTarget = false;
					requestAnimationFrame(render);
				}
			}

			/**
			 * Handler for rotary event
			 * @param {Event} event
			 */
			function rotary(event) {
				var eventDirection = event.detail && event.detail.direction;

				// update position by snapSize
				if (eventDirection === "CW") {
					moveToPosition -= snapSize || 50;
				} else {
					moveToPosition += snapSize || 50;
				}
				if (snapSize) {
					moveToPosition = snapSize * round(moveToPosition / snapSize);
				}
				if (moveToPosition < -maxScrollPosition) {
					moveToPosition = -maxScrollPosition;
				}
				if (moveToPosition > 0) {
					moveToPosition = 0;
				}
				requestAnimationFrame(moveTo);
				requestAnimationFrame(render);
				eventUtil.trigger(scrollingElement, EVENTS.SCROLL_START, {
					scrollLeft: direction ? -(moveToPosition) : 0,
					scrollTop: direction ? 0 : -(moveToPosition),
					fromAPI: false
				});
				event.stopImmediatePropagation();
			}

			function moveToCalculatePosition() {
				var diffPosition = moveToPosition - scrollPosition,
					// get absolute value
					absDiffPosition = abs(diffPosition);

				if (absDiffPosition > 10) {
					// we move 10% of difference
					scrollPosition = round(scrollPosition + diffPosition / 10);
					requestAnimationFrame(moveTo);
				} else if (absDiffPosition > 2) {
					// else if is difference < 10 then we move 50%
					scrollPosition = round(scrollPosition + diffPosition / 2);
					requestAnimationFrame(moveTo);
				} else {
					// if difference is <=2 then we move to end value and finish loop
					scrollPosition = moveToPosition;
				}
				if (!bounceBack) {
					// normalize scroll value
					if (scrollPosition < -maxScrollPosition) {
						scrollPosition = -maxScrollPosition;
					}
					if (scrollPosition > 0) {
						scrollPosition = 0;
					}
				}
			}

			/**
			 * Loop function to calculate state in animation after touchend
			 */
			function moveTo() {
				// calculate difference between current position and expected scroll end
				var scrollLeft = 0,
					scrollTop = 0;

				// if difference is big
				if (scrollingElement) {
					moveToCalculatePosition();

					if (direction) {
						scrollLeft = -scrollPosition;
					} else {
						scrollTop = -scrollPosition;
					}
					// trigger event scroll
					eventUtil.trigger(scrollingElement, EVENTS.SCROLL, {
						scrollLeft: scrollLeft,
						scrollTop: scrollTop,
						inBounds: (scrollPosition >= -maxScrollPosition) && (scrollPosition <= 0),
						fromAPI: fromAPI
					});
					if (!isTouch) {
						eventUtil.trigger(scrollingElement, EVENTS.SCROLL_END, {
							scrollLeft: scrollLeft,
							scrollTop: scrollTop,
							fromAPI: fromAPI
						});
					}

					fadeInScrollBar();
				}
			}

			function renderScrollbar() {
				if (circularScrollBar) {
					polarUtil.updatePosition(svgScrollBar, "." + classes.thumb, {
						arcStart: scrollBarPosition,
						arcEnd: scrollBarPosition + circularScrollThumbSize,
						r: RADIUS
					});
				} else {
					if (scrollThumb) {
						if (direction) {
							scrollThumb.style.transform = "translate(" + scrollBarPosition + "px, 0)";
						} else {
							scrollThumb.style.transform = "translate(0, " + scrollBarPosition + "px)";
						}
					}
				}
			}
			/**
			 * Render loop on request animation frame
			 */
			function render() {
				// calculate ne position of scrolling as sum of last scrolling state + move
				var newRenderedPosition = scrollPosition + lastScrollPosition;
				// is position was changed

				if (newRenderedPosition !== lastRenderedPosition) {
					// we update styles
					lastRenderedPosition = newRenderedPosition;
					if (-newRenderedPosition < maxScrollPosition) {
						scrollBarPosition = -newRenderedPosition / maxScrollPosition * maxScrollBarPosition;
					} else {
						scrollBarPosition = maxScrollBarPosition;
					}
					if (scrollBarPosition < 0) {
						scrollBarPosition = 0;
					}

					if (!virtualMode && elementStyle) {
						elementStyle.transform = direction ?
							"translate(" + lastRenderedPosition + "px, 0)" :
							"translate(0, " + lastRenderedPosition + "px)";
					}
					renderScrollbar();

					requestAnimationFrame(render);
				}
			}

			function initPosition() {
				// init internal variables
				startPosition = 0;
				scrollPosition = 0;
				scrollBarPosition = 0;
				lastScrollPosition = 0;
				moveToPosition = 0;
				lastRenderedPosition = 0;
				lastTime = Date.now();
			}

			/**
			 * Enable JS scrolling on element
			 * @method enable
			 * @param {HTMLElement} element element for scrolling
			 * @param {"x"|"y"} [setDirection="y"] direction of scrolling
			 * @param {boolean} setVirtualMode if is set to true then send event without scroll element
			 * @member ns.util.scrolling
			 */
			function enable(element, setDirection, setVirtualMode) {
				var parentRectangle,
					contentRectangle,
					children,
					existingContainerElement;

				virtualMode = setVirtualMode;
				bounceBack = false;
				snapSize = false;

				if (scrollingElement) {
					ns.warn("Scrolling exist on another element, first call disable method");
				} else {
					// detect direction
					direction = (setDirection === "x") ? 1 : 0;

					existingContainerElement = element.querySelector("div." + classes.container);
					if (existingContainerElement) {
						childElement = existingContainerElement;
						childElement.style.transform = "";
					} else {
						// we are creating a container to position transform
						childElement = document.createElement("div");
						// ... and appending all children to it

						children = Array.prototype.slice.call(element.childNodes)

						children.forEach(function (node) {
							if (!ns.support.shape.circle || selectorUtil.matchesSelector(node, ".ui-header:not(.ui-fixed), :not(.ui-footer)")) {
								childElement.appendChild(node);
							}
						});

						element.insertBefore(childElement, element.firstElementChild);
						childElement.classList.add(classes.container);
					}
					// setting scrolling element
					scrollingElement = element;
					// calculate maxScroll
					parentRectangle = element.getBoundingClientRect();
					contentRectangle = childElement.getBoundingClientRect();

					// Max scroll position is determined by size of the content - clip window size
					if (direction) {
						maxScrollPosition = round(contentRectangle.width - parentRectangle.width);
					} else {
						maxScrollPosition = round(contentRectangle.height - parentRectangle.height);
					}

					// cache style element
					elementStyle = childElement.style;

					initPosition();
					// cache current overflow value to restore in disable
					previousOverflow = window.getComputedStyle(element).getPropertyValue("overflow");
					// set overflow hidden
					element.style.overflow = "hidden";
					// add event listeners
					document.addEventListener("touchstart", touchStart, false);
					document.addEventListener("touchmove", touchMove, false);
					document.addEventListener("touchend", touchEnd, false);
					window.addEventListener("rotarydetent", rotary, true);
				}
			}

			/**
			 * @method disable
			 * @member ns.util.scrolling
			 */
			function disable() {
				disableScrollBar();

				// clear event listeners
				document.removeEventListener("touchstart", touchStart, false);
				document.removeEventListener("touchmove", touchMove, false);
				document.removeEventListener("touchend", touchEnd, false);
				window.removeEventListener("rotarydetent", rotary, true);

				// after changed page and removed it this element can not exists
				if (scrollingElement) {
					scrollingElement.style.overflow = previousOverflow;
				}

				elementStyle = null;
				scrollingElement = null;
				childElement = null;
				svgScrollBar = null;
			}

			function enableScrollBarCircular() {
				var arcStartPoint = direction ? 0 : 90;

				scrollBar.classList.add(classes.circular);
				svgScrollBar = polarUtil.createSVG();

				// create background
				polarUtil.addArc(svgScrollBar, {
					arcStart: arcStartPoint - (CIRCULAR_SCROLL_BAR_SIZE / 2),
					arcEnd: arcStartPoint + (CIRCULAR_SCROLL_BAR_SIZE / 2),
					classes: classes.path,
					width: 10,
					r: RADIUS,
					linecap: "round"
				});
				// create thumb
				polarUtil.addArc(svgScrollBar, {
					referenceDegree: arcStartPoint - (CIRCULAR_SCROLL_BAR_SIZE / 2),
					arcStart: 0,
					arcEnd: circularScrollThumbSize,
					classes: classes.thumb,
					width: 10,
					r: RADIUS,
					linecap: "round"
				});

				scrollBar.appendChild(svgScrollBar);
				scrollingElement.parentElement.insertBefore(scrollBar, scrollingElement.nextSibling);
			}

			function enableScrollBarRectangular(scrollBarStyle) {
				var boundingRect,
					childElementRect,
					scrollThumbStyle,
					scrollBarWidth = 0,
					scrollBarHeight = 0;

				scrollBar.classList.add(classes.direction + "-" + (direction ? "x" : "y"));

				scrollThumb = document.createElement("div");
				scrollThumbStyle = scrollThumb.style;
				scrollThumb.classList.add(classes.thumb);

				boundingRect = scrollingElement.getBoundingClientRect();
				childElementRect = childElement.getBoundingClientRect();

				if (direction) {
					scrollBarWidth = (boundingRect.width - (2 * SCROLL_MARGIN));

					scrollBarStyle.width = scrollBarWidth + "px";
					scrollBarStyle.left = (boundingRect.left + SCROLL_MARGIN) + "px";
					scrollThumbStyle.transform = "translate3d(" + scrollBarPosition + "px,0,0)";
					// Calculate size of the thumb (only useful when enabling after content has size > 0)
					scrollThumbStyle.width = (scrollBarWidth / childElementRect.width * scrollBarWidth) +
						"px";
				} else {
					scrollBarHeight = (boundingRect.height - (2 * SCROLL_MARGIN));

					scrollBarStyle.height = scrollBarHeight + "px";
					scrollBarStyle.top = (boundingRect.top + SCROLL_MARGIN) + "px";
					scrollThumbStyle.transform = "translate3d(0," + scrollBarPosition + "px,0)";
					// Calculate size of the thumb (only useful when enabling after content has size > 0)
					scrollThumbStyle.height = (scrollBarHeight / childElementRect.height * scrollBarHeight) +
						"px";
				}

				scrollBar.appendChild(scrollThumb);
				scrollingElement.parentElement.insertBefore(scrollBar, scrollingElement.nextSibling);

				// Get max scrollbar position after appending
				if (direction) {
					maxScrollBarPosition = scrollBarWidth - scrollThumb.getBoundingClientRect().width;
				} else {
					maxScrollBarPosition = scrollBarHeight - scrollThumb.getBoundingClientRect().height;
				}
			}

			function enableScrollBar() {
				scrollBar = document.createElement("div");
				scrollBar.classList.add(classes.scrollbar);

				if (circularScrollBar) {
					enableScrollBarCircular();
				} else {
					enableScrollBarRectangular(scrollBar.style);
				}
			}

			function disableScrollBar() {
				if (scrollBar) {
					scrollBar.parentElement.removeChild(scrollBar);
					scrollBar = null;
					scrollThumb = null;
				}
			}

			/**
			 * Scroll to give position
			 * @method scrollTo
			 * @param {number} value
			 * @member ns.util.scrolling
			 */
			function scrollTo(value) {
				moveToPosition = value;
				fromAPI = true;
				eventUtil.trigger(scrollingElement, EVENTS.SCROLL_BEFORE_START, {
					scrollLeft: direction ? -scrollPosition : 0,
					scrollTop: direction ? 0 : -scrollPosition,
					fromAPI: fromAPI
				});
				requestAnimationFrame(moveTo);
				render();
			}

			/**
			 * Return scroll position
			 * @method getScrollPosition
			 * @member ns.util.scrolling
			 */
			function getScrollPosition() {
				return -scrollPosition;
			}

			/**
			 * Return max scroll position
			 * @method getMaxScroll
			 * @member ns.util.scrolling
			 */
			function getMaxScroll() {
				return maxScrollPosition;
			}

			ns.util.scrolling = {
				getScrollPosition: getScrollPosition,
				enable: enable,
				disable: disable,
				enableScrollBar: enableScrollBar,
				disableScrollBar: disableScrollBar,
				scrollTo: scrollTo,
				/**
				 * Return true is given element is current scrolling element
				 * @method isElement
				 * @param {HTMLElement} element element to check
				 * @return {boolean}
				 * @member ns.util.scrolling
				 */
				isElement: function (element) {
					return scrollingElement === element;
				},

				/**
				 * Update max scrolling position
				 * @method setMaxScroll
				 * @param {number} maxValue
				 * @member ns.util.scrolling
				 */
				setMaxScroll: function (maxValue) {
					var boundingRect = scrollingElement.getBoundingClientRect(),
						directionDimension = direction ? "width" : "height",
						directionSize = boundingRect[directionDimension],
						tempMaxPosition = max(maxValue - directionSize, 0);

					// Change size of thumb only when necessary
					if (tempMaxPosition !== maxScrollPosition) {
						maxScrollPosition = tempMaxPosition || Number.POSITIVE_INFINITY;
						if (scrollBar) {
							if (circularScrollBar) {
								// Calculate new thumb size based on max scrollbar size
								circularScrollThumbSize = max((directionSize / (maxScrollPosition + directionSize)) *
									CIRCULAR_SCROLL_BAR_SIZE, CIRCULAR_SCROLL_MIN_THUMB_SIZE);
								maxScrollBarPosition = CIRCULAR_SCROLL_BAR_SIZE - circularScrollThumbSize;
								polarUtil.updatePosition(svgScrollBar, "." + classes.thumb, {
									arcStart: scrollBarPosition,
									arcEnd: scrollBarPosition + circularScrollThumbSize,
									r: RADIUS
								});
							} else {
								directionSize -= 2 * SCROLL_MARGIN;
								scrollThumb.style[directionDimension] =
									(directionSize / (maxScrollPosition + directionSize) * directionSize) + "px";
								// Cannot use direct value from style here because CSS may override the minimum
								// size of thumb here
								maxScrollBarPosition = directionSize -
									scrollThumb.getBoundingClientRect()[directionDimension];
							}
						}
					}
				},
				getMaxScroll: getMaxScroll,
				setSnapSize: function (setSnapSize) {
					snapSize = setSnapSize;
					if (snapSize) {
						maxScrollPosition = snapSize * round(maxScrollPosition / snapSize);
					}
				},
				setBounceBack: function (setBounceBack) {
					bounceBack = setBounceBack;
				}
			};

			}(document, window, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, white: true, plusplus: true*/
/**
 * #Virtual list
 * Shows a list view for large amounts of data.
 *
 * @class ns.widget.core.VirtualListview
 * @extends ns.core.BaseWidget
 * @since 2.0
 */
(function (document, ns) {
	"use strict";
				/**
			 * @property {Object} Widget Alias for {@link ns.widget.BaseWidget}
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.BaseWidget,
				// Constants definition
				/**
				 * Defines index of scroll `{@link ns.widget.core.VirtualListview#_scroll}.direction`
				 * @property {number} SCROLL_NONE
				 * to retrieve if user is not scrolling
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				SCROLL_NONE = -1,
				/**
				 * Defines index of scroll `{@link ns.widget.core.VirtualListview#_scroll}.direction`
				 * @property {number} SCROLL_UP
				 * to retrieve if user is scrolling up
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				SCROLL_UP = 0,
				/**
				 * Defines index of scroll {@link ns.widget.core.VirtualListview#_scroll}
				 * @property {number} SCROLL_DOWN
				 * to retrieve if user is scrolling down
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				SCROLL_DOWN = 1,
				/**
				 * Defines index of scroll {@link ns.widget.core.VirtualListview#_scroll}
				 * @property {number} SCROLL_LEFT
				 * to retrieve if user is scrolling left
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				SCROLL_LEFT = 2,
				/**
				 * Defines index of scroll `{@link ns.widget.core.VirtualListview#_scroll}.direction`
				 * @property {number} SCROLL_RIGHT
				 * to retrieve if user is scrolling right
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				SCROLL_RIGHT = 3,

				/**
				 * Defines vertical scrolling orientation. It's default orientation.
				 * @property {string} VERTICAL
				 * @private
				 * @static
				 */
				VERTICAL = "y",
				/**
				 * Defines horizontal scrolling orientation.
				 * @property {string} HORIZONTAL
				 * @private
				 * @static
				 */
				HORIZONTAL = "x",
				/**
				 * Determines that scroll event should not be taken into account if scroll event occurs.
				 * @property {boolean} blockEvent
				 * @private
				 * @static
				 */
				blockEvent = false,
				/**
				 * Handle window timeout ID.
				 * @property {number} timeoutHandler
				 * @private
				 * @static
				 */

				/**
				 * Alias for ns.util
				 * @property {Object} util
				 * @private
				 * @static
				 */
				util = ns.util,

				/**
				 * Alias for ns.util.requestAnimationFrame
				 * @property {Function} requestFrame
				 * @private
				 * @static
				 */
				requestFrame = util.requestAnimationFrame,

				selectors = util.selectors,

				utilEvent = ns.event,

				utilScrolling = ns.util.scrolling,

				filter = [].filter,

				/**
				 * Local constructor function
				 * @method VirtualListview
				 * @private
				 * @member ns.widget.core.VirtualListview
				 */
				VirtualListview = function () {
					var self = this;
					/**
					 * VirtualListview widget's properties associated with
					 * @property {Object} ui
					 * User Interface
					 * @property {?HTMLElement} [ui.scrollview=null] Scroll element
					 * @property {?HTMLElement} [ui.spacer=null] HTML element which makes scrollbar proper
					 * size
					 * @property {number} [ui.itemSize=0] Size of list element in pixels. If scrolling is
					 * vertically it's item width in other case it"s height of item element
					 * @member ns.widget.core.VirtualListview
					 */

					self._ui = {
						scrollview: null,
						spacer: null,
						itemSize: 0
					};

					/**
					 * Holds information about scrolling state
					 * @property {Object} _scroll
					 * @property {Array} [_scroll.direction=[0,0,0,0]] Holds current direction of scrolling.
					 * Indexes suit to following order: [up, left, down, right]
					 * @property {number} [_scroll.lastPositionX=0] Last scroll position from top in pixels.
					 * @property {number} [_scroll.lastPositionY=0] Last scroll position from left in pixels.
					 * @property {number} [_scroll.lastJumpX=0] Difference between last and current
					 * position of horizontal scroll.
					 * @property {number} [_scroll.lastJumpY=0] Difference between last and current
					 * position of vertical scroll.
					 * @property {number} [_scroll.clipWidth=0] Width of clip - visible area for user.
					 * @property {number} [_scroll.clipHeight=0] Height of clip - visible area for user.
					 * @member ns.widget.core.VirtualListview
					 */
					self._scroll = {
						direction: [0, 0, 0, 0],
						dir: SCROLL_NONE,
						lastPositionX: 0,
						lastPositionY: 0,
						lastJumpX: 0,
						lastJumpY: 0,
						clipWidth: 0,
						clipHeight: 0
					};

					/**
					 * Name of widget
					 * @property {string} name
					 * @member ns.widget.core.VirtualListview
					 * @static
					 */
					self.name = "VirtualListview";

					/**
					 * Current zero-based index of data set.
					 * @property {number} _currentIndex
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._currentIndex = 0;

					/**
					 * VirtualListview widget options.
					 * @property {Object} options
					 * @property {number} [options.bufferSize=100] Number of items of result set. The default
					 * value is 100.
					 * As the value gets higher, the loading time increases while the system performance
					 * improves. So you need to pick a value that provides the best performance
					 * without excessive loading time. It's recommended to set bufferSize at least 3 times
					 * bigger than number
					 * of visible elements.
					 * @property {number} [options.dataLength=0] Total number of items.
					 * @property {string} [options.orientation=VERTICAL] Scrolling orientation. Default
					 * VERTICAL scrolling enabled.
					 * @property {Object} options.listItemUpdater Holds reference to method which modifies
					 * list item, depended
					 * at specified index from database. **Method should be overridden by developer using
					 * {@link ns.widget.core.VirtualListview#setListItemUpdater} method.** or defined as a
					 * config
					 * object. Method takes two parameters:
					 *  -  element {HTMLElement} List item to be modified
					 *  -  index {number} Index of data set
					 * @member ns.widget.core.VirtualListview
					 */
					self.options = {
						bufferSize: 100,
						dataLength: 0,
						orientation: VERTICAL,
						listItemUpdater: null,
						scrollElement: null,
						optimizedScrolling: false
					};

					/**
					 * Binding for scroll event listener.
					 * @method _scrollEventBound
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._scrollEventBound = null;

					/**
					 * Render function
					 * @method _render
					 * @protected
					 * @member ns.widget.core.VirtualListview
					 */
					self._render = render.bind(null, this);

					/**
					 * Render command list
					 * @property {Array.<*>} _renderList
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._renderList = [];

					/**
					 * Element size cache
					 * @property {Array.<number>} _sizeMap
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._sizeMap = [];

					/**
					 * DocumentFragment buffer for DOM offscreen manipulations
					 * @property {DocumentFragment} _domBuffer
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._domBuffer = document.createDocumentFragment();

					/**
					 * Time for last cleared cache data
					 * @property {number} _lastRenderClearTimestamp
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._lastRenderClearTimestamp = 0;

					/**
					 * Time to lease for clearing render caches
					 * @property {number} _lastRenderClearTTL
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._lastRenderClearTTL = 10000;

					/**
					 * Average list item size cache
					 * @property {number} _avgListItemSize
					 * @member ns.widget.core.VirtualListview
					 * @protected
					 */
					self._avgListItemSize = -1;

					return self;
				},

				// Cached prototype for better minification
				prototype = new BaseWidget();

			/**
			 * Dictionary object containing commonly used widget classes
			 * @property {Object} classes
			 * @static
			 * @readonly
			 * @member ns.widget.core.VirtualListview
			 */
			VirtualListview.classes = {
				uiVirtualListContainer: "ui-virtual-list-container",
				spacer: "ui-virtual-list-spacer"
			};

			/**
			 * Main render function
			 * @method render
			 * @member ns.widget.core.VirtualListview
			 * @param {ns.widget.core.VirtualListview} vList Reference to VirtualListview object
			 * @param {DOMHighResTimeStamp} timestamp The current time of the animation
			 * @private
			 * @static
			 */
			function render(vList, timestamp) {
				var ops = vList._renderList,
					i = 0,
					l = ops.length;

				for (; i < l; i += 4) {
					if (ops[i] === "propset") {
						ops[i + 1][ops[i + 2]] = ops[i + 3];
					}
				}

				if (l === 0 && timestamp - vList._lastRenderClearTimestamp > vList._lastRenderClearTTL) {
					vList._lastRenderClearTimestamp = timestamp;
					// clear
					vList._sizeMap.length = 0;
					vList._avgListItemSize = -1;
				}

				ops.length = 0;
			}

			/**
			 * Updates scroll information about position, direction and jump size.
			 * @method _updateScrollInfo
			 * @param {ns.widget.core.VirtualListview} self VirtualListview widget reference
			 * @param {Event} [event] scroll event object.
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			function _updateScrollInfo(self, event) {
				var scrollInfo = self._scroll,
					scrollDir = SCROLL_NONE,
					scrollViewElement = self._ui.scrollview,
					scrollLastPositionX = scrollInfo.lastPositionX,
					scrollLastPositionY = scrollInfo.lastPositionY,
					scrollviewPosX = scrollViewElement.scrollLeft,
					scrollviewPosY = (event && event.detail && event.detail.scrollTop) ||
						scrollViewElement.scrollTop;

				self._refreshScrollbar();

				//Scrolling UP
				if (scrollviewPosY < scrollLastPositionY) {
					scrollDir = SCROLL_UP;
				}

				//Scrolling RIGHT
				if (scrollviewPosX < scrollLastPositionX) {
					scrollDir = SCROLL_RIGHT;
				}

				//Scrolling DOWN
				if (scrollviewPosY > scrollLastPositionY) {
					scrollDir = SCROLL_DOWN;
				}

				//Scrolling LEFT
				if (scrollviewPosX > scrollLastPositionX) {
					scrollDir = SCROLL_LEFT;
				}

				scrollInfo.lastJumpY = Math.abs(scrollviewPosY - scrollLastPositionY);
				scrollInfo.lastJumpX = Math.abs(scrollviewPosX - scrollLastPositionX);
				scrollInfo.lastPositionX = scrollviewPosX;
				scrollInfo.lastPositionY = scrollviewPosY;
				scrollInfo.dir = scrollDir;
				scrollInfo.clipHeight = scrollViewElement.clientHeight;
				scrollInfo.clipWidth = scrollViewElement.clientWidth;
			}

			/**
			 * Computes list element size according to scrolling orientation
			 * @method _computeElementSize
			 * @param {HTMLElement} element Element whose size should be computed
			 * @param {string} orientation Scrolling orientation
			 * @return {number} Size of element in pixels
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			function _computeElementSize(element, orientation) {
				return parseInt((orientation === VERTICAL) ? element.clientHeight : element.clientWidth, 10);
			}

			/**
			 * Scrolls and manipulates DOM element to destination index. Element at destination
			 * index is the first visible element on the screen. Destination index can
			 * be different from Virtual List's current index, because current index points
			 * to first element in the buffer.
			 * @member ns.widget.core.VirtualListview
			 * @param {ns.widget.core.VirtualListview} self VirtualListview widget reference
			 * @param {number} toIndex Destination index.
			 * @method _orderElementsByIndex
			 * @private
			 * @static
			 */
			function _orderElementsByIndex(self, toIndex) {
				var element = self.element,
					options = self.options,
					scrollInfo = self._scroll,
					scrollClipSize,
					dataLength = options.dataLength,
					indexCorrection,
					bufferedElements,
					avgListItemSize = self._avgListItemSize,
					bufferSize = options.bufferSize,
					i,
					offset,
					index,
					isLastBuffer = false,
					children = filter.call(element.children, isListItem);

				//Get size of scroll clip depended on scroll direction
				scrollClipSize = options.orientation === VERTICAL ? scrollInfo.clipHeight :
					scrollInfo.clipWidth;

				//Compute average list item size
				if (avgListItemSize === -1) {
					self._avgListItemSize = avgListItemSize =
						_computeElementSize(element, options.orientation) / bufferSize;
				}

				//Compute average number of elements in each buffer (before and after clip)
				bufferedElements = Math.floor((bufferSize -
					Math.floor(scrollClipSize / avgListItemSize)) / 2);

				if (toIndex - bufferedElements <= 0) {
					index = 0;
				} else {
					index = toIndex - bufferedElements;
				}

				if (index + bufferSize >= dataLength) {
					index = dataLength - bufferSize;
					if (index < 0) {
						index = 0;
					}
					isLastBuffer = true;
				}
				indexCorrection = toIndex - index;

				self._loadData(index);
				blockEvent = true;
				offset = index * avgListItemSize;
				if (options.orientation === VERTICAL) {
					if (isLastBuffer) {
						offset = self._ui.spacer.clientHeight;
					}
					self._addToRenderList("propset", element.style, "margin-top", offset + "px");
				} else {
					if (isLastBuffer) {
						offset = self._ui.spacer.clientWidth;
					}
					self._addToRenderList("propset", element.style, "margin-left", offset + "px");
				}

				for (i = 0; i < indexCorrection; i += 1) {
					offset += _computeElementSize(children[i], options.orientation);
				}

				if (options.orientation === VERTICAL) {
					//MOBILE: self._ui.scrollview.element.scrollTop = offset;
					if (utilScrolling.isElement(self._ui.scrollview)) {
						utilScrolling.scrollTo(offset);
					} else {
						self._ui.scrollview.scrollTop = offset;
					}
				} else {
					//MOBILE: self._ui.scrollview.element.scrollLeft = offset;
					self._ui.scrollview.scrollLeft = offset;
				}
				blockEvent = false;
				self._currentIndex = index;
			}

			function getFirstElementChild(element) {
				var firstLiElement = element.firstElementChild;

				while (firstLiElement) {
					if (firstLiElement.tagName === "LI") {
						return firstLiElement;
					}
					firstLiElement = firstLiElement.nextElementSibling;
				}
				return null;
			}

			/**
			 * Loads element range into internal list item buffer. Elements are taken off one end of the
			 * children list,
			 *  placed on the other end (i.e.: first is taken and appended ath the end when scrolling
			 *  down)
			 *  and their contents get reloaded. Content reload is delegated to an external function
			 *  (_updateListItem).
			 * Depending on the direction, elements are
			 * @param {VirtualList} self
			 * @param {HTMLElement} element parent widget (the list view) of the (re)loaded element range
			 * @param {HTMLElement} domBuffer an off-document element for temporary storage of processed
			 * elements
			 * @param {Function} sizeGetter a function calculating element size
			 * @param {number} loadIndex element index to start loading at
			 * @param {number} indexDirection -1 when indices decrease with each loaded element, +1
			 * otherwise
			 * @param {number} elementsToLoad loaded element count
			 * @return {number} number of pixels the positions of the widgets in the list moved.
			 *  Repositioning the widget by this amount (along the scroll axis) is needed for the
			 *  remaining children
			 *  elements not to move, relative to the viewport.
			 * @private
			 */
			function _loadListElementRange(self, element, domBuffer, sizeGetter, loadIndex,
				indexDirection, elementsToLoad) {
				var temporaryElement,
					children = filter.call(element.children, isListItem),
					jump = 0,
					i;

				if (indexDirection > 0) {
					for (i = elementsToLoad; i > 0; i--) {
						temporaryElement = children.shift();

						// move to offscreen buffer
						domBuffer.appendChild(temporaryElement);

						//Updates list item using template
						self._updateListItem(temporaryElement, loadIndex);

						// move back to document
						element.appendChild(temporaryElement);

						jump += sizeGetter(temporaryElement, loadIndex++);
					}
					self._currentIndex += elementsToLoad;
				} else {
					for (i = elementsToLoad; i > 0; i--) {
						temporaryElement = children.shift();

						// move to offscreen buffer
						domBuffer.appendChild(temporaryElement);

						//Updates list item using template
						self._updateListItem(temporaryElement, loadIndex);

						element.insertBefore(temporaryElement, getFirstElementChild(element));
						jump -= sizeGetter(temporaryElement, loadIndex--);
					}
					self._currentIndex -= elementsToLoad;
				}
				return jump;
			}

			/**
			 * For a given element style, positioning direction, set the top/left position to
			 *  elementPosition* adjusted by |jump|. In case the resulting position is outside the bounds
			 *  defined by valid index ranges (0..dataLength-1), clamp the position to tha boundary.
			 * @param {VirtualList} self
			 * @param {number} dataLength max valid index
			 * @param {Object} elementStyle style of the element repositioned
			 * @param {number} scrollDir one of the BaseWidget.SCROLL_*
			 * @param {number} jump amount of pixels the repositioned element should be moved
			 * @param {number} elementPositionTop current elementTop
			 * @param {number} elementPositionLeft current elementLeft
			 * @private
			 */
			function _setElementStylePosition(self, dataLength, elementStyle, scrollDir, jump,
				elementPositionTop, elementPositionLeft) {
				var scrolledVertically = (scrollDir & 2) === 0,
					scrolledHorizontally = (scrollDir & 2) === 1,
					newPosition,
					currentIndex = self._currentIndex;

				if (scrolledVertically) {
					newPosition = elementPositionTop + jump;

					if (currentIndex <= 0) {
						self._currentIndex = currentIndex = 0;
						newPosition = 0;
					}

					if (currentIndex >= (dataLength - 1)) {
						newPosition = self._ui.spacer.clientHeight;
					}

					if (newPosition < 0) {
						newPosition = 0;
					}

					self._addToRenderList("propset", elementStyle, "margin-top", newPosition + "px");
				}

				if (scrolledHorizontally) {
					newPosition = elementPositionLeft + jump;

					if (currentIndex <= 0) {
						self._currentIndex = currentIndex = 0;
						newPosition = 0;
					}

					if (currentIndex >= (dataLength - 1)) {
						newPosition = self._ui.spacer.clientWidth;
					}

					if (newPosition < 0) {
						newPosition = 0;
					}

					self._addToRenderList("propset", elementStyle, "margin-left", newPosition + "px");
				}
			}

			/**
			 * Sums numeric properties of an array of objects
			 * @method sumProperty
			 * @member ns.widget.core.VirtualListview
			 * @param {Array.<Object>} elements An array of objects
			 * @param {string} property The property name
			 * @return {number}
			 * @private
			 * @static
			 */
			function sumProperty(elements, property) {
				var result = 0,
					i = elements.length;

				while (--i >= 0) {
					result += elements[i][property];
				}

				return result;
			}

			function isListItem(element) {
				return element.tagName === "LI";
			}

			/**
			 *
			 * @param {Array} sizeMap
			 * @param {boolean} horizontal
			 * @param {HTMLElement} element
			 * @param {number} index
			 * @return {*}
			 * @private
			 */
			function _getElementSize(sizeMap, horizontal, element, index) {
				if (sizeMap[index] === undefined) {
					sizeMap[index] = horizontal ? element.clientWidth : element.clientHeight;
				}
				return sizeMap[index];
			}

			/**
			 * Orders elements. Controls resultset visibility and does DOM manipulation. This
			 * method is used during normal scrolling.
			 * @method _orderElements
			 * @param {ns.widget.core.VirtualListview} self VirtualListview widget reference
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			function _orderElements(self) {
				var element = self.element,
					scrollInfo = self._scroll,
					options = self.options,
					elementStyle = element.style,
					//Current index of data, first element of resultset
					currentIndex = self._currentIndex,
					//Number of items in resultset
					bufferSize = parseInt(options.bufferSize, 10),
					//Total number of items
					dataLength = options.dataLength,
					//Array of scroll direction
					scrollDir = scrollInfo.dir,
					scrollLastPositionY = scrollInfo.lastPositionY,
					scrollLastPositionX = scrollInfo.lastPositionX,
					elementPositionTop = parseInt(elementStyle.marginTop, 10) || 0,
					elementPositionLeft = parseInt(elementStyle.marginLeft, 10) || 0,
					elementsToLoad,
					bufferToLoad,
					elementsLeftToLoad = 0,
					domBuffer = self._domBuffer,
					avgListItemSize = self._avgListItemSize,
					resultsetSize = sumProperty(
						filter.call(element.children, isListItem),
						options.orientation === VERTICAL ? "clientHeight" : "clientWidth"
					),
					sizeMap = self._sizeMap,
					jump = 0,
					hiddenPart = 0,
					indexDirection,
					loadIndex;

				if (avgListItemSize === -1) {
					//Compute average list item size
					self._avgListItemSize = avgListItemSize =
						_computeElementSize(element, options.orientation) / bufferSize;
				}

				switch (scrollDir) {
					case SCROLL_NONE:
						break;
					case SCROLL_DOWN:
						hiddenPart = scrollLastPositionY - elementPositionTop;
						elementsLeftToLoad = dataLength - currentIndex - bufferSize;
						break;
					case SCROLL_UP:
						hiddenPart = (elementPositionTop + resultsetSize) -
							(scrollLastPositionY + scrollInfo.clipHeight);
						elementsLeftToLoad = currentIndex;
						break;
					case SCROLL_RIGHT:
						hiddenPart = scrollLastPositionX - elementPositionLeft;
						elementsLeftToLoad = dataLength - currentIndex - bufferSize;
						break;
					case SCROLL_LEFT:
						hiddenPart = (elementPositionLeft + resultsetSize) -
							(scrollLastPositionX - scrollInfo.clipWidth);
						elementsLeftToLoad = currentIndex;
						break;
				}

				//manipulate DOM only, when at least 1/2 of result set is hidden
				//NOTE: Result Set should be at least 2x bigger then clip size
				if (hiddenPart > 0 && (resultsetSize / hiddenPart) <= 2) {
					//Left half of hidden elements still hidden/cached
					elementsToLoad = ((hiddenPart / avgListItemSize) -
						// |0 = floor the value
						((bufferSize - scrollInfo.clipHeight / avgListItemSize) / 5) | 0) | 0;
					elementsToLoad = Math.min(elementsLeftToLoad, elementsToLoad);
					bufferToLoad = (elementsToLoad / bufferSize) | 0;
					elementsToLoad = elementsToLoad % bufferSize;

					if (scrollDir === SCROLL_DOWN || scrollDir === SCROLL_RIGHT) {
						indexDirection = 1;
					} else {
						indexDirection = -1;
					}

					// Scrolling more then buffer
					if (bufferToLoad > 0) {
						// Load data to buffer according to jumped index
						self._loadData(currentIndex + indexDirection * bufferToLoad * bufferSize);

						// Refresh current index after buffer jump
						currentIndex = self._currentIndex;

						jump += indexDirection * bufferToLoad * bufferSize * avgListItemSize;
					}

					loadIndex = currentIndex + (indexDirection > 0 ? bufferSize : -1);
					// Note: currentIndex is not valid after this call.
					jump += _loadListElementRange(self, element, domBuffer,
						_getElementSize.bind(null, sizeMap, scrollDir & 2),
						loadIndex, indexDirection, elementsToLoad);

					_setElementStylePosition(self, dataLength, elementStyle, scrollDir, jump,
						elementPositionTop, elementPositionLeft);
				}
			}

			/**
			 * Check if scrolling position is changed and updates list if it needed.
			 * @method _updateList
			 * @param {ns.widget.core.VirtualListview} self VirtualListview widget reference
			 * @param {Event} event scroll event triggering this update
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			function _updateList(self, event) {
				var _scroll = self._scroll;

				_updateScrollInfo(self, event);
				if (_scroll.lastJumpY > 0 || _scroll.lastJumpX > 0 && !blockEvent) {
					_orderElements(self);
					utilEvent.trigger(self.element, "vlistupdate");
				}
			}

			/**
			 * Updates list item using user defined listItemUpdater function.
			 * @method _updateListItem
			 * @param {HTMLElement} element List element to update
			 * @param {number} index Data row index
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._updateListItem = function (element, index) {
				this.options.listItemUpdater(element, index);
			};

			prototype._setupScrollview = function (element, orientation) {
				var scrollview = selectors.getClosestByClass(element, "ui-scroller") || element.parentElement,
					scrollviewStyle;
				//Get scrollview instance

				scrollviewStyle = scrollview.style;

				if (orientation === HORIZONTAL) {
					scrollviewStyle.overflowX = "scroll";
					scrollviewStyle.overflowY = "hidden";
				} else {
					scrollviewStyle.overflowX = "hidden";
					scrollviewStyle.overflowY = "scroll";
				}

				return scrollview;
			};

			prototype._getScrollView = function (options, element) {
				var scrollview = null;

				if (options.scrollElement) {
					if (typeof options.scrollElement === "string") {
						scrollview = selectors.getClosestBySelector(element, "." + options.scrollElement);
					} else {
						scrollview = options.scrollElement;
					}
				}

				if (!scrollview) {
					scrollview = this._setupScrollview(element, options.orientation);
				}

				return scrollview;
			};

			/**
			 * Build widget structure
			 * @method _build
			 * @param {HTMLElement} element Widget's element
			 * @return {HTMLElement} Element on which built is widget
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					classes = VirtualListview.classes,
					options = self.options,
					scrollview,
					spacer = document.createElement("div"),
					spacerStyle,
					orientation;

				//Prepare element
				element.style.position = "relative";
				element.classList.add(classes.uiVirtualListContainer);

				//Set orientation, default vertical scrolling is allowed
				orientation = options.orientation.toLowerCase() === HORIZONTAL ? HORIZONTAL : VERTICAL;

				scrollview = self._getScrollView(options, element);

				// Prepare spacer (element which makes scrollBar proper size)
				spacer.classList.add(classes.spacer);

				spacerStyle = spacer.style;
				spacerStyle.display = "block";
				spacerStyle.position = "static";

				if (orientation === HORIZONTAL) {
					spacerStyle.float = "left";
				}

				scrollview.appendChild(spacer);

				// Assign variables to members
				ui.spacer = spacer;
				ui.scrollview = scrollview;
				options.orientation = orientation;

				return element;
			};

			/**
			 * Initialize widget on an element.
			 * @method _init
			 * @param {HTMLElement} element Widget's element
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options,
					scrollview = ui.scrollview || self._getScrollView(options, element),
					elementRect,
					scrollviewRect;

				if (options.dataLength < options.bufferSize) {
					options.bufferSize = options.dataLength;
				}

				if (options.bufferSize < 1) {
					options.bufferSize = 1;
				}

				elementRect = element.getBoundingClientRect();
				scrollviewRect = scrollview.getBoundingClientRect();
				// Assign variables to members
				self._initTopPosition = elementRect.top - scrollviewRect.top;
				self._initLeftPosition = elementRect.left - scrollviewRect.left;

				scrollview.classList.add("ui-has-virtual-list");

				ui.spacer = ui.spacer || scrollview.querySelector("." + VirtualListview.classes.spacer);
				ui.scrollview = scrollview;

				options.orientation = options.orientation.toLowerCase() === HORIZONTAL ? HORIZONTAL : VERTICAL;

				if (options.optimizedScrolling) {
					utilScrolling.enable(scrollview, options.orientation);
					utilScrolling.enableScrollBar();
				}
			};

			/**
			 * Builds Virtual List structure
			 * @method _buildList
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._buildList = function () {
				var self = this,
					listItem,
					list = self.element,
					options = self.options,
					childElementType = (list.tagName === "UL" ||
						list.tagName === "OL" ||
						list.tagName === "TAU-VIRTUALLISTVIEW") ? "li" : "div",
					numberOfItems = options.bufferSize,
					documentFragment = self._domBuffer,
					orientation = options.orientation,
					i;

				for (i = 0; i < numberOfItems; ++i) {
					listItem = document.createElement(childElementType);

					if (orientation === HORIZONTAL) {
						// NOTE: after rebuild this condition check possible duplication from _init method
						listItem.style.float = "left";
					}

					self._updateListItem(listItem, i);
					documentFragment.appendChild(listItem);
				}

				list.appendChild(documentFragment);
				this._refresh();
			};

			/**
			 * Refresh list
			 * @method _refresh
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._refresh = function () {
				//Set default value of variable create
				this._refreshScrollbar();
			};

			/**
			 * Loads data from specified index to result set.
			 * @method _loadData
			 * @param {number} index Index of first row
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._loadData = function (index) {
				var self = this,
					child = self.element.firstElementChild;

				if (self._currentIndex !== index) {
					self._currentIndex = index;
					do {
						self._updateListItem(child, index);
						++index;
						child = child.nextElementSibling;
					} while (child);
				}
			};

			/**
			 * Sets proper scrollbar size: height (vertical), width (horizontal)
			 * @method _refreshScrollbar
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._refreshScrollbar = function () {
				var self = this,
					currentIndex = self._currentIndex,
					element = self.element,
					options = self.options,
					ui = self._ui,
					spacerStyle = ui.spacer.style,
					bufferSizePx,
					listSize;

				if (options.orientation === VERTICAL) {
					//Note: element.clientHeight is variable
					bufferSizePx = parseFloat(element.clientHeight) || 0;
					listSize = bufferSizePx / options.bufferSize * (options.dataLength - currentIndex);

					if (options.optimizedScrolling) {
						utilScrolling.setMaxScroll(listSize);
					} else {
						self._addToRenderList("propset", spacerStyle, "height", (listSize - bufferSizePx) +
							"px");
					}
				} else {
					//Note: element.clientWidth is variable
					bufferSizePx = parseFloat(element.clientWidth) || 0;
					listSize = bufferSizePx / options.bufferSize * options.dataLength;

					if (options.optimizedScrolling) {
						utilScrolling.setMaxScroll(listSize);
					} else {
						self._addToRenderList("propset", spacerStyle, "width", (bufferSizePx /
							options.bufferSize * (options.dataLength - 1) - 4 / 3 * bufferSizePx) + "px");
					}

				}
			};

			/**
			 * Binds VirtualListview events
			 * @method _bindEvents
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._addToRenderList = function () {
				var self = this,
					renderList = self._renderList;

				renderList.push.apply(renderList, arguments);
				requestFrame(self._render);

				//MOBILE: parent_bindEvents.call(self, self.element);
			};

			/**
			 * Binds VirtualListview events
			 * @method _bindEvents
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._bindEvents = function () {
				var scrollEventBound = _updateList.bind(null, this),
					//MOBILE: scrollviewClip = self._ui.scrollview && self._ui.scrollview.element;
					scrollviewClip = this._ui.scrollview;

				if (scrollviewClip) {
					scrollviewClip.addEventListener("scroll", scrollEventBound, false);
					this._scrollEventBound = scrollEventBound;
				}

				//MOBILE: parent_bindEvents.call(self, self.element);
			};

			/**
			 * Cleans widget's resources
			 * @method _destroy
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._destroy = function () {
				var self = this,
					scrollView = self._ui.scrollview,
					uiSpacer = self._ui.spacer,
					element = self.element,
					elementStyle = element.style;

				// Restore start position
				elementStyle.position = "static";
				if (self.options.orientation === VERTICAL) {
					elementStyle.top = "auto";
				} else {
					elementStyle.left = "auto";
				}

				if (scrollView) {
					utilScrolling.disable(scrollView);
					scrollView.removeEventListener("scroll", self._scrollEventBound, false);
				}

				//Remove spacer element
				if (uiSpacer.parentNode) {
					uiSpacer.parentNode.removeChild(uiSpacer);
				}

				//Remove li elements.
				while (element.firstElementChild) {
					element.removeChild(element.firstElementChild);
				}
			};

			/**
			 * This method scrolls list to defined position in pixels.
			 * @method scrollTo
			 * @param {number} position Scroll position expressed in pixels.
			 * @member ns.widget.core.VirtualListview
			 */
			prototype.scrollTo = function (position) {
				var self = this;

				if (utilScrolling.isElement(self._ui.scrollview)) {
					utilScrolling.scrollTo(position);
				} else {
					self._ui.scrollview.scrollTop = position;
				}
			};

			/**
			 * This method scrolls list to defined index.
			 * @method scrollToIndex
			 * @param {number} index Scroll Destination index.
			 * @member ns.widget.core.VirtualListview
			 */
			prototype.scrollToIndex = function (index) {
				if (index < 0) {
					index = 0;
				}
				if (index >= this.options.dataLength) {
					index = this.options.dataLength - 1;
				}
				_updateScrollInfo(this);
				_orderElementsByIndex(this, index);
			};

			/**
			 * This method builds widget and trigger event "draw".
			 * @method draw
			 * @member ns.widget.core.VirtualListview
			 */
			prototype.draw = function () {
				this._buildList();
				this.trigger("draw");
			};

			/**
			 * This method sets list item updater function.
			 * To learn how to create list item updater function please
			 * visit Virtual List User Guide.
			 * @method setListItemUpdater
			 * @param {Object} updateFunction Function reference.
			 * @member ns.widget.core.VirtualListview
			 */
			prototype.setListItemUpdater = function (updateFunction) {
				this.options.listItemUpdater = updateFunction;
			};

			// Assign prototype
			VirtualListview.prototype = prototype;
			ns.widget.core.VirtualListview = VirtualListview;

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */

/**
 * #Virtual List Widget
 * Widget creates special list which can contain big number of items.
 *
 * In the Web environment, it is challenging to display a large amount of data in a list, such as
 * displaying a contact list of over 1000 list items. It takes time to display the entire list in
 * HTML and the DOM manipulation is complex.
 *
 * The virtual list widget is used to display a list of unlimited data elements on the screen
 * for better performance. This widget provides easy access to databases to retrieve and display data.
 * It based on **result set** which is fixed size defined by developer by data-row attribute. Result
 * set should be **at least 3 times bigger** then size of clip (number of visible elements).
 *
 * For now Virtual Lists are based on the **jQuery.template plugin** as described in the jQuery documentation
 * for jQuery.template plugin - but it will change some day...
 *
 * ##Default selectors
 * In _ul_ elements with _data-role=virtuallistview_ or _data-role=virtuallist_.
 *
 * ##Make it work
 * To active and configure virtual list widget with application follow these steps:
 *
 * ####1. Create template and place widget element
 *
 *    @example
 *    <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
 *            <li class="ui-li-3-2-7">
 *            <span class="ui-li-text-main">${NAME}</span>
 *                <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub"/>
 *                <span class="ui-li-text-sub">${ACTIVE}</span>
 *                <span class="ui-li-text-sub2">${FROM}</span>
 *            </li>
 *        </script>
 *        <ul id="vList" data-role="virtuallistview" data-template="tmp-3-2-7" data-row="100"></ul>
 *
 * **NOTE:** Tizen Web UI's data-dbtable attribute and functionality is not supported.
 *
 * ####2. Create template and place widget element
 * Run {@link ns.widget.mobile.VirtualListview#create .create} method to configure iteration function and
 * declare total number of items.
 *
 *    @example
 *    virtuallistview.create({
 *		//Configure iteration function
 *		itemData: function ( idx ) {
 *			return myDATA[idx];
 *		},
 *		//Declare total number of items
 *		numItemData: myDATA.length
 *	});
 *
 * ##Manual constructor
 * To construct VirtualListview widget manually you can use constructor of the widget:
 *
 *    @example
 *    var virtuallistview = ns.engine.instanceWidget(document.getElementById('virtuallistview'), 'VirtualListview');
 *
 * If jQuery library is loaded, its method can be used:
 *
 *    @example
 *    var virtuallistview = $('#virtuallistview').virtuallistview();
 *
 * **NOTE:** after construct of widget should be create method called with proper parameters.
 *
 *
 * @author Michał Szepielak <m.szepielak@samsung.com>
 * @author Mateusz Ciepliński <m.cieplinski@samsung.com> [add jQuery template functionality]
 * @author Piotr Karny <p.karny@samsung.com>
 * @class ns.widget.mobile.VirtualListview
 * @extends ns.widget.mobile.Listview
 */

(function (window, document, ns) {
	"use strict";
				var VirtualListview = ns.widget.core.VirtualListview,
				engine = ns.engine,
				utils = ns.util,
				utilsSelectors = utils.selectors,
				nsData = utils.DOM.nsData,
				prototype = new VirtualListview(),
				//
				// CORE METHODS
				//
				parentInit = prototype._init,
				parentRefresh = prototype._refresh,
				parentUpdateListItem = prototype._updateListItem;

			/**
			 * Initialize widget on an element.
			 * @method _init
			 * @param {HTMLElement} element Widget's element
			 * @member ns.widget.core.VirtualListview
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this;

				engine.instanceWidget(element, "Listview");

				if (nsData(element, "row") !== null) {
					ns.warn("Row option in VirtualListview is deprecated and not supported. Use bufferSize option instead.");
				}

				if (nsData(element, "template") !== null) {
					ns.warn("Template option in VirtualListview is deprecated and not supported.");
				}

				if (nsData(element, "numItemData") !== null) {
					ns.warn("NumItemData option in VirtualListview is deprecated and not supported. Use dataLength option instead.");
				}

				if (typeof self.options.listItemUpdater !== "function") {
					ns.warn(["ListItemUpdater in VirtualListview is not set.",
						"Probably you use selector for automatic creation of this widget.",
						"Selectors for this widget are deprecated and will be removed in future.",
						"Use setListItemUpdater to set list item updater."].join(" "));
				}

				self._ui.listview = engine.instanceWidget(element, "Listview");
				parentInit.call(self, element);
			};

			prototype._refresh = function () {
				this._ui.listview.refresh();
				parentRefresh.call(this);
			};

			prototype._setupScrollview = function (element) {
				var scrollview;

				// @TODO: get class value from static access to class
				scrollview = engine.getBinding(utilsSelectors.getClosestByClass(element, "ui-scrollview-clip"));
				return scrollview.element;
			};

			/**
			 * @method _updateListItem
			 * Updates list item using user defined listItemUpdater function.
			 * @param {HTMLElement} element List element to update
			 * @param {number} index Data row index
			 * @protected
			 */
			prototype._updateListItem = function (element, index) {
				var self = this;

				if (typeof self.options.listItemUpdater === "function") {
					parentUpdateListItem.call(self, element, index);
				} else {
					ns.warn("List item updater must be a function. Using jQuery Template in VirtualListview is deprecated and is not supported");
				}
			};

			/**
			 * Sets iterator function and total number of data based on users arguments.
			 * @method create
			 * @protected
			 * @member ns.widget.mobile.VirtualListview
			 */
			prototype.create = function () {
				ns.warn("VirtualListview.create() method is deprecated and no more supported. Use draw() method instead.");
			};

			VirtualListview.prototype = prototype;

			ns.widget.mobile.VirtualListview = VirtualListview;
			ns.engine.defineWidget(
				"VirtualListview",
				"[data-role='virtuallistview'],[data-role='virtuallist'], .ui-virtuallistview",
				["draw", "setListItemUpdater", "scrollTo", "scrollToIndex", "create"],
				VirtualListview,
				"tizen"
			);
			}(window, window.document, ns));


/*global window, define, ns, screen */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, white: true, plusplus: true*/
(function (document, ns) {
	"use strict";
				/**
			 * @property {Object} Widget Alias for {@link ns.widget.BaseWidget}
			 * @member ns.widget.core.VirtualListview
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.BaseWidget,
				// Constants definition
				/**
				 * Defines index of scroll `{@link ns.widget.core.VirtualListview#_scroll}.direction`
				 * @property {number} SCROLL_NONE
				 * to retrieve if user is not scrolling
				 * @private
				 * @static
				 * @member ns.widget.core.VirtualListview
				 */
				selectors = ns.util.selectors,
				// Scrolling util is responsible for support touches and calculate scrolling position after touch
				// In Virtual List we use scrolling in virtual model which is responsible for calculate touches, send event
				// but don't render scrolled element. For rendering is responsible only Virtual List
				utilScrolling = ns.util.scrolling,
				circularScreen = ns.support.shape.circle,

				SimpleVirtualList = function () {
					var self = this;

					self.options = {
						dataLength: 0,
						listItemUpdater: null,
						scrollElement: null,
						orientation: "vertical",
						snap: false,
						edgeEffect: circularScreen ? null : defaultEdgeEffect,
						infinite: false
					};
					self._ui = {
						edgeEffect: null,
						scrollview: null
					};
					self._scrollBegin = 0;
					self._elementsMap = [];
					self._itemSize = 0;
					self._numberOfItems = 5;
					self._edgeEffectGradientSize = 0;
				},
				abs = Math.abs,
				min = Math.min,
				floor = Math.floor,
				filter = Array.prototype.filter,
				prototype = new BaseWidget(),
				// Current color from changeable style
				// @TODO change to dynamic color
				EDGE_EFFECT_COLOR = "rgba(61, 185, 204, {1})",
				classes = {
					uiVirtualListContainer: "ui-virtual-list-container",
					edgeEffect: "ui-virtual-list-edge-effect"
				};

			SimpleVirtualList.classes = classes;

			/**
			 * Effect for edge scrolling on rectangular screens
			 * @param {number} positionDiff difference from edge to current scroll
			 * @param {string} orientation `vertical` or `horizontal`
			 * @param {string} edge `start` or `end` depending on orientation
			 * @param {number} rawPosition current scroll position
			 * @param {number} widgetInstance current widget instance
			 * @return {number}
			 */
			function defaultEdgeEffect(positionDiff, orientation, edge, rawPosition, widgetInstance) {
				var ui = widgetInstance._ui,
					edgeEffectElement = ui.edgeEffect || ui.scrollview.querySelector("." + classes.edgeEffect),
					edgeEffectStyle = edgeEffectElement.style,
					gradientSize = min(abs(positionDiff / 8) - 1, 10);

				if (orientation === "vertical") {
					edgeEffectStyle.top = (edge === "start") ? "0" : "auto";
					edgeEffectStyle.bottom = (edge === "start") ? "auto" : "0";
				} else {
					edgeEffectStyle.left = (edge === "start") ? "0" : "auto";
					edgeEffectStyle.right = (edge === "start") ? "auto" : "0";
				}

				// Saved reference to later avoid unnecessary style manipulations
				widgetInstance._edgeEffectGradientSize = gradientSize;

				edgeEffectStyle.boxShadow = "0 0 0 " + gradientSize + "px " + EDGE_EFFECT_COLOR.replace("{1}", 0.5) + "," +
					"0 0 0 " + (gradientSize * 2) + "px " + EDGE_EFFECT_COLOR.replace("{1}", 0.4) + "," +
					"0 0 0 " + (gradientSize * 3) + "px " + EDGE_EFFECT_COLOR.replace("{1}", 0.3) + "," +
					"0 0 0 " + (gradientSize * 4) + "px " + EDGE_EFFECT_COLOR.replace("{1}", 0.2) + "," +
					"0 0 0 " + (gradientSize * 5) + "px " + EDGE_EFFECT_COLOR.replace("{1}", 0.1) + "";

				return 0;
			}

			function setupScrollview(element) {
				return selectors.getClosestByClass(element, "ui-scroller") || element.parentElement;
			}

			function getScrollView(options, element) {
				var scrollview = null;

				if (options.scrollElement) {
					if (typeof options.scrollElement === "string") {
						scrollview = selectors.getClosestBySelector(element, "." + options.scrollElement);
					} else {
						scrollview = options.scrollElement;
					}
				}

				if (!scrollview) {
					scrollview = setupScrollview(element);
				}

				return scrollview;
			}

			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					classes = SimpleVirtualList.classes,
					options = self.options,
					scrollview,
					orientation;

				//Prepare element
				element.classList.add(classes.uiVirtualListContainer);

				//Set orientation, default vertical scrolling is allowed
				orientation = options.orientation.toLowerCase() === "horizontal" ? "horizontal" : "vertical";

				scrollview = getScrollView(options, element);

				ui.scrollview = scrollview;
				options.orientation = orientation;

				return element;
			};

			prototype._buildList = function () {
				var self = this,
					listItem,
					ui = self._ui,
					scrollviewWidget,
					options = self.options,
					scrollview = self._ui.scrollview,
					sizeProperty = options.orientation === "vertical" ? "height" : "width",
					list = self.element,
					childElementType = (list.tagName === "UL" || list.tagName === "OL") ? "li" : "div",
					numberOfItems = self._numberOfItems,
					content = selectors.getClosestBySelector(list, ".ui-content").getBoundingClientRect(),
					elementRect = null,
					i,
					scrollInitSize = [].reduce.call(scrollview.children, function (previousValue, currentNode) {
						return previousValue + currentNode.getBoundingClientRect()[sizeProperty];
					}, 0),
					circle = ns.support.shape.circle;

				scrollviewWidget = ns.engine.getBinding(selectors.getClosestBySelector(list,
					".ui-page"), "Scrollview");
				if (scrollviewWidget) {
					scrollviewWidget.option("bouncingEffect", false);
					self._scrollviewWidget = scrollviewWidget;
					options.edgeEffect = function (positionDiff, orientation, edge) {
						scrollviewWidget.showBouncingEffect(edge);
					};
				}

				if (options.dataLength < numberOfItems) {
					numberOfItems = options.dataLength;
				}

				for (i = 0; i < numberOfItems; ++i) {
					listItem = document.createElement(childElementType);
					self._updateListItem(listItem, i);
					list.appendChild(listItem);
					elementRect = self.element.getBoundingClientRect();
					if (elementRect[sizeProperty] < content[sizeProperty]) {
						numberOfItems++;
					}
				}

				if (options.snap && circle) {
					self._snapListviewWidget = ns.engine.instanceWidget(list, "SnapListview", options.snap);
				}

				elementRect = self.element.getBoundingClientRect();
				self._itemSize = numberOfItems > 0 ? Math.round(elementRect[sizeProperty] / numberOfItems) : 0;
				self._numberOfItems = numberOfItems;
				self._containerSize = content[sizeProperty];
				self._numberOfVisibleElements = Math.ceil(content[sizeProperty] / self._itemSize);

				utilScrolling.enable(scrollview, options.orientation === "horizontal" ? "x" : "y", true);
				if (options.infinite) {
					utilScrolling.setMaxScroll(null);
				} else {
					utilScrolling.enableScrollBar();
					if (scrollview.classList.contains("ui-scroller")) {
						utilScrolling.setMaxScroll((options.dataLength + 1) * self._itemSize + scrollInitSize);
					} else {
						utilScrolling.setMaxScroll(options.dataLength * self._itemSize);
					}
				}
				if (options.snap && circle) {
					utilScrolling.setSnapSize(self._itemSize);
				}

				// Add default edge effect
				// @TODO consider changing to :after and :before
				if (options.edgeEffect === defaultEdgeEffect) {
					ui.edgeEffect = document.createElement("div");
					ui.edgeEffect.classList.add(classes.edgeEffect, "orientation-" + options.orientation);

					ui.scrollview.appendChild(ui.edgeEffect);
				}

				utilScrolling.setBounceBack(true);
			};

			prototype._updateListItem = function (element, index) {
				element.setAttribute("data-index", index);
				this.options.listItemUpdater(element, index);
			};

			prototype._refresh = function () {
				var self = this;

				self._buildList();
				if (self._snapListviewWidget) {
					self._snapListviewWidget.refresh();
				}
				self.trigger("draw");
			};

			prototype.draw = function () {
				this.refresh();
			};

			prototype.scrollTo = function (position) {
				utilScrolling.scrollTo(-position);
			};

			prototype.scrollToIndex = function (index) {
				this.scrollTo(Math.floor(this._itemSize * index));
			};

			function filterElement(index, element) {
				return element.getAttribute("data-index") === "" + index;
			}

			function filterNextElement(nextIndex, element, index) {
				return index > nextIndex;
			}

			function filterFreeElements(map, element) {
				return map.indexOf(element) === -1;
			}

			function _updateList(self, event) {
				var list = self.element,
					itemSize = self._itemSize,
					options = self.options,
					beginProperty = options.orientation === "vertical" ? "scrollTop" : "scrollLeft",
					scrollBegin = event.detail && event.detail[beginProperty],
					ui = self._ui,
					scrollChildStyle = ui.scrollview.firstElementChild.style,
					fromIndex = 0,
					dataLength = options.dataLength,
					map = [],
					freeElements,
					numberOfItems = self._numberOfItems,
					i = 0,
					infinite = options.infinite,
					currentIndex = 0,
					listItem,
					correction = 0,
					scroll = {
						scrollTop: 0,
						scrollLeft: 0
					},
					inBoundsDiff = 0,
					nextElement,
					j = 0;

				if (options.edgeEffect) {
					if (event.detail && !event.detail.inBounds) {
						inBoundsDiff = scrollBegin < 0 ? scrollBegin : (scrollBegin + self._containerSize) - (options.dataLength * self._itemSize);

						scrollBegin = scrollBegin - inBoundsDiff + options.edgeEffect(inBoundsDiff, // position diff
							options.orientation, // orientation
							(scrollBegin < 0) ? "start" : "end", // edge
							scrollBegin, // raw position
							self);

					} else if (self._edgeEffectGradientSize > 0) {
						// In some rare cases gradient in default edge effect may stay greater than 0
						// eg. fast flicking down and up without touchend
						(ui.edgeEffect || ui.scrollview.querySelector("." + classes.edgeEffect)).style.boxShadow = "none";
						self._edgeEffectGradientSize = 0;
					} else {
						if (self._scrollviewWidget) {
							self._scrollviewWidget.hideBouncingEffect();
						}
					}
				}

				if (scrollBegin !== undefined) {
					self._scrollBegin = scrollBegin;
					currentIndex = floor(scrollBegin / self._itemSize);
					if (currentIndex !== floor(self._scrollBeginPrev / self._itemSize) && currentIndex >= 0) {
						if (scrollBegin < self._itemSize) {
							fromIndex = 0;
							correction = 0;
						} else if (currentIndex > (dataLength - numberOfItems) && !infinite) {
							fromIndex = dataLength - numberOfItems;
							correction = itemSize * (currentIndex - fromIndex);
						} else {
							fromIndex = currentIndex - 1;
							correction = itemSize;
						}

						// Get elements which are currently presented
						for (i = fromIndex; i < fromIndex + numberOfItems; ++i) {
							map[i - fromIndex] = filter.call(list.children, filterElement.bind(null, i % dataLength))[0];
						}

						// Get elements that should be changed
						freeElements = filter.call(list.children, filterFreeElements.bind(null, map));

						for (i = fromIndex + numberOfItems - 1; i >= fromIndex; --i) {
							j = i % dataLength;
							if ((i >= 0 && i < dataLength) || infinite) {

								// if checked element is not presented
								if (!map[i - fromIndex]) {
									// get first free element
									listItem = freeElements.shift();
									map[i - fromIndex] = listItem;

									if (listItem) {
										self._updateListItem(listItem, j);

										// Get the desired position for the element
										if (i - fromIndex === numberOfItems - 1 || (j < fromIndex && (scrollBegin > self._scrollBeginPrev))) {
											list.appendChild(listItem);
										} else {
											nextElement = map.filter(filterNextElement.bind(null, i - fromIndex))[0];
											if (!nextElement) {
												list.insertBefore(listItem, list.firstElementChild);
											} else {
												list.insertBefore(listItem, nextElement);
											}
										}
									}
								}
							}
						}
						scroll[beginProperty] = correction + scrollBegin % self._itemSize;
					} else {
						// If we are somewhere in the middle of the list
						if (scrollBegin >= 0) {
							if (scrollBegin < self._itemSize) {
								scroll[beginProperty] = scrollBegin % itemSize;
							} else if (currentIndex > (dataLength - numberOfItems) && (!infinite)) {
								fromIndex = dataLength - numberOfItems;
								correction = itemSize * (currentIndex - fromIndex);
								scroll[beginProperty] = correction + scrollBegin % itemSize;
							} else {
								scroll[beginProperty] = itemSize + scrollBegin % itemSize;
							}
						} else {
							// In case we scroll to content before the list
							scroll[beginProperty] = scrollBegin;
						}
					}
					scrollChildStyle.webkitTransform = "translate(" + (-scroll.scrollLeft) + "px, " + (-scroll.scrollTop) + "px)";

					self._scrollBeginPrev = scrollBegin;
					if (self._snapListviewWidget) {
						self._snapListviewWidget.refresh();
					}
				}
			}

			prototype._bindEvents = function () {
				var scrollEventBound = _updateList.bind(null, this),
					scrollview = this._ui.scrollview;

				if (scrollview) {
					scrollview.addEventListener("scroll", scrollEventBound, false);
					this._scrollEventBound = scrollEventBound;
				}

			};

			prototype._destroy = function () {
				utilScrolling.disable();
			};

			prototype.setListItemUpdater = function (updateFunction) {
				this.options.listItemUpdater = updateFunction;
				this.refresh();
			};

			SimpleVirtualList.prototype = prototype;

			ns.engine.defineWidget(
				"VirtualListviewSimple",
				// empty selector because widget require manual build
				"",
				["draw", "setListItemUpdater", "scrollTo", "scrollToIndex"],
				SimpleVirtualList,
				"",
				true
			);
			ns.widget.core.VirtualListviewSimple = SimpleVirtualList;
			}(window.document, ns));

/*global $, ns, define*/
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true, white: true, browser: true */
/**
 * #Virtual Grid Widget
 * Widget creates special grid which can contain big number of items.
 *
 * @class ns.widget.mobile.VirtualGrid
 * @extends ns.widget.mobile.VirtualListview
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var VirtualList = ns.widget.mobile.VirtualListview,
				parentPrototype = VirtualList.prototype,
				engine = ns.engine,
				domUtils = ns.util.DOM,
				/**
				 * @property {Function} utilsObjectMerge
				 * @private
				 * @member ns.widget.mobile.VirtualGrid
				 * @static
				 */
				utilsObjectMerge = ns.util.object.merge,
				// @TODO HORIZONTAL is currently not used, it will be used when horizontal scrolling is ready
				/**
				 * @property {string} HORIZONTAL="x" constant for horizontal virtual grid
				 * @private
				 * @member ns.widget.mobile.VirtualGrid
				 * @static
				 */
				HORIZONTAL = "x",
				/**
				 * @property {string} VERTICAL="y" constant for vertical virtual grid
				 * @private
				 * @member ns.widget.mobile.VirtualGrid
				 * @static
				 */
				VERTICAL = "y",
				classes = {
					WRAP_BLOCK_Y: "ui-virtualgrid-wrapblock-y",
					WRAP_BLOCK_X: "ui-virtualgrid-wrapblock-x",
					ITEM: "virtualgrid-item"
				},
				VirtualGrid = function () {
					return this;
				},
				prototype = new VirtualList();

			prototype._configure = function () {
				var self = this;

				// Call parent _configure
				if (typeof parentPrototype._configure === "function") {
					parentPrototype._configure.apply(self, arguments);
				}

				/**
				 * @property {Object} options
				 * @property {number} options.numItemData number of lines in the grid
				 * @property {number} [options.rawNumItemData] number of items inside data source
				 * @property {string} options.direction='y' direction for scrolling elements
				 * @property {number} options.row number of lines displayed at once
				 * @property {number} options.itemsPerLine number of elements per one line
				 * @property {Function} [options.listItemUpdater=null] Method which modifies list item, depended at specified index from database.
				 * @property {boolean} [options.standalone=false] If true scrollview instance will be created inside of the widget
				 * **Method may be overridden by developer using {@link ns.widget.mobile.VirtualListview#create .create} method with proper arguments.**
				 */
				self.options = utilsObjectMerge({}, self.options, {
					numItemData: 0,
					direction: VERTICAL,
					row: 50,
					itemsPerLine: 1,
					listItemUpdater: null,
					standalone: false
				});

				/**
				 * @property {Object} ui Holds UI elements of the widget
				 * @member ns.widget.mobile.VirtualGrid
				 */
				self._ui = utilsObjectMerge({}, self._ui);

				/**
				 * @property {number} _currentIndex Current zero-based index of data set.
				 * @member ns.widget.mobile.VirtualGrid
				 */
				self._currentIndex = 0;

				// @TODO Developer currently needs to define direction="x" for VirtualGrid and scroll="x" for container with scrollview,
				// @TODO ...it would be better to set scroll="x" for scroll view when it's not forced (defined as HTML attribute)
			};

			prototype._build = function (element) {
				if (typeof parentPrototype._build === "function") {
					parentPrototype._build.apply(this, arguments);
				}

				if (this.options.direction === HORIZONTAL) {
					element.style.height = "100%";
				}

				return element;
			};

			/**
			 * Determines grid sizes, row and column count.
			 * Temporary adds one element into list to fetch it's sizes.
			 * @method _setLineSize
			 * @protected
			 */
			prototype._setLineSize = function () {
				var self = this,
					options = self.options,
					tempElement = document.createElement("div"),
					tempFirstChild,
					tempElementFromTemplate,
					list = self.element,
					size,
					containerSize;

				// Add temporary element for fetching sizes
				list.appendChild(tempElement);
				self._updateListItem(tempElement, 0);
				tempFirstChild = tempElement.firstElementChild;
				tempElementFromTemplate = tempFirstChild.firstElementChild;

				// [NOTE] grid cells are floated to left for vertical scrolling
				// First row child element is the grid cell, it gets styles from CSS stylesheet.
				// We are clearing float to make list row expand to whole list width.
				// To fetch the raw width of template element we are going deeper (tempElementFromTemplate)
				// This ensures we fetch the authored value of width for grid cell element.

				// @TODO consider getting the height/width (.lineSize) each time a new line is parsed, currently all lines are based on the size of first element
				if (options.direction === VERTICAL) {
					// Reset width of first child set by updateListElement
					tempFirstChild.style.width = "";
					tempFirstChild.style.float = "none";
					// Fetch original width with everything (including margins)
					size = domUtils.getElementWidth(tempElementFromTemplate, "outer", true, true);
					// .lineSize is required to set the real height or width of a line
					options.lineSize = tempFirstChild.offsetHeight;
					containerSize = list.offsetWidth;
				} else {
					// @TODO create proper styles for horizontal scrolling
					size = tempElement.offsetHeight;
					if (options.standalone) {
						containerSize = self._ui.scrollview.element.offsetHeight;
					} else {
						containerSize = list.offsetHeight;
					}
					// .lineSize is required to set the real height or width of a line
					options.lineSize = tempFirstChild.offsetWidth;
				}

				// Remove element after fetching sizes
				list.removeChild(tempElement);

				// Calculate item count per line
				options.itemsPerLine = Math.max(Math.floor(containerSize / size), 1);
				// Save original element count
				options.rawNumItemData = options.numItemData;
				// Calculate limited element count
				options.numItemData = Math.ceil(options.numItemData / options.itemsPerLine);
			};

			/**
			 * Configures list. Sets data source and iterator behavior.
			 * @method _configureList
			 * @protected
			 * @param {Object} argumentsArray
			 */
			prototype._configureList = function (argumentsArray) {
				var self = this,
					options = self.options,
					args = argumentsArray[0] || {};

				// @TODO this is easy to use, but the code is confusing
				// and doesn't allow easy merging
				if (typeof args.itemData === "function" && (typeof args.numItemData === "function" || typeof args.numItemData === "number")) {
					if (typeof args.numItemData === "function") {
						options.numItemData = args.numItemData();
					} else {
						options.numItemData = args.numItemData <= 0 ? 0 : args.numItemData;
					}
					self.itemData = args.itemData;
				}

				// @TODO all options should be merged at once without the need of separate defining variables
				options.direction = args.direction || options.direction;

				// @TODO set minimum set size depending on current screen size
				options.row = Math.max(20, options.row);

				self._setLineSize();

				self._buildList();

				//Update scroll info: scroll position etc...
				if (options.standalone) {
					self._updateScrollInfo();
				}
			};

			/**
			 * @method _updateListItem
			 * Prepares list items.
			 * This method is used once while creating widget and later for every list update (on scroll for example)
			 * @param {HTMLElement} element Grid line element (usually a div with proper class)
			 * @param {number} index Index of the item to process
			 * @protected
			 */
			prototype._updateListItem = function (element, index) {
				var self = this,
					options = self.options,
					updateFunction = options.listItemUpdater,
					direction = options.direction,
					itemData = self.itemData,
					$jqTmpl = self._ui.$jqTmpl,
					itemsPerLine = options.itemsPerLine,
					rawNumItemData = options.rawNumItemData,
					elementPercentSize = (100 / itemsPerLine) + "%",
					// Offset for fetching elements from next line
					itemsOffset = itemsPerLine * index,
					templateElement,
					fragment,
					nextItemIndex,
					i = 0;

				// Clean insides before creating new content
				element.innerHTML = "";

				fragment = document.createDocumentFragment();
				nextItemIndex = itemsOffset + i;
				// Add items until line end or data source end
				// rawNumItemData may be undefined for first time size checking
				while (i < itemsPerLine && (rawNumItemData === undefined || nextItemIndex < rawNumItemData)) {
					//@TODO THIS IS A JQUERY INCLUSION IN A TAU WIDGET!!!
					//@TODO FIX THIS!!!
					templateElement = document.createElement("div");
					// Set item-in-line size
					templateElement.style[direction === VERTICAL ? "width" : "height"] = elementPercentSize;

					templateElement.classList.add(classes.ITEM);

					if (typeof updateFunction === "function") {
						updateFunction(templateElement, nextItemIndex);
					} else {
						templateElement.appendChild($.tmpl($jqTmpl, itemData(nextItemIndex))[0]);
					}

					fragment.appendChild(templateElement);

					i++;
					nextItemIndex = itemsOffset + i;
				}

				element.appendChild(fragment);
				engine.createWidgets(element);


				if (options.lineSize) {
					element.style[direction === VERTICAL ? "height" : "width"] = options.lineSize + "px";
				}
				element.classList.add(direction === VERTICAL ? classes.WRAP_BLOCK_Y : classes.WRAP_BLOCK_X);
			};

			/**
			 * List of classes used inside VirtualGrid
			 * @property {Object} classes
			 * @property {string} classes.WRAP_BLOCK_Y="ui-virtualgrid-wrapblock-y"
			 * @property {string} classes.WRAP_BLOCK_X="ui-virtualgrid-wrapblock-x"
			 * @property {string} classes.ITEM="virtualgrid-item"
			 * @static
			 */
			VirtualGrid.classes = classes;

			VirtualGrid.prototype = prototype;

			ns.widget.mobile.VirtualGrid = VirtualGrid;

			engine.defineWidget(
				"VirtualGrid",
				"[data-role=virtualgrid], .ui-virtualgrid",
				["create"],
				VirtualGrid,
				"mobile"
			);

			}(window, window.document, ns));

/*global window, ns, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Loader Widget
 * Widget displaying loader popup on page changes.
 *
 * ##HTML Examples
 * ###Create simple loader pending from div using data-role:
 *
 *		@example
 *			<div data-role="loader" id="ns-loader"></div>
 *
 * ###Create simple loader pending from div using class:
 *
 *		@example
 *			<div class="ui-loader" id="ns-loader"></div>
 *
 * ##Manual constructor
 * For manual creation of loader widget you can use constructor of widget:
 *
 *		@example
 *			<div id="ns-loader"></div>
 *			<script>
 *				var elementLoader = document.getElementById("ns-loader"),
 *				loader = tau.widget.Loader(elementLoader);
 *			</script>
 * If jQuery library is loaded, its method can be used:
 *
 *		@example
 *			<div id="ns-loader"></div>
 *			<script>
 *				$("#ns-loader").loader();
 *			</script>
 *
 * ##Options for Loader Widget
 *
 * Options for widget can be defined as _data-..._ attributes or given as
 * parameter in constructor.
 *
 * You can change option for widget using method **option**.
 *
 * ###Custom html
 * Adds custom html for the inner content of the loading messages
 *
 *		@example
 *			<div data-role="loader" id="ns-loader"
 *			data-html="<span>loading...</span>"></div>
 *
 * ###Custom text
 * Text to be displayed when the loader is shown
 *
 *		@example
 *			<div data-role="loader" id="ns-loader" data-text="loading...">
 *			</div>

 * ###Visibility of text
 * When the loader is shown and text is added, checks whether the text in the
 * loading message is shown;
 *
 *		@example
 *			<div data-role="loader" id="ns-loader" data-text="loading..."
 *			data-text-visible="true"></div>

 * ###Set theme
 * Sets the theme for the loading messages
 *
 *		@example
 *			<div data-role="loader" id="ns-loader" data-theme="a"></div>
 *
 *
 * ##Options for Loader Widget
 *
 * Options for widget can be get/set .
 *
 * ###You can change option for widget using method **option**.
 * Initialize the loader
 *
 *		@example
 *			<script>
 *				var elementLoader = document.getElementById("ns-loader"),
 *				loader = tau.widget.Loader(elementLoader);
 *			</script>
 *
 * ###Custom html
 * Get or set the html option, after initialization
 *
 *		@example
 *			<script>
 *				//getter
 *				loader.option( "html" );
 *
 *				//setter
 *				loader.option( "html", "<span>Loader</span>", );
 *			</script>
 *
 * ###Custom text
 * Get or set the text option, after initialization
 *
 *		@example
 *			<script>
 *				//getter
 *				loader.option( "text" );
 *
 *				//setter
 *				loader.option( "text", "Loader" );
 *			</script>
 *
 * ###Visibility of text
 * Get or set the textVisible option, after initialization
 *
 *		@example
 *			<script>
 *				//getter
 *				loader.option( "textVisible" );
 *
 *				//setter
 *				loader.option( "textVisible", "true" );
 *			</script>
 *
 * ###Set theme
 * Get or set the theme option, after initialization
 *
 *		@example
 *			<script>
 *				//getter
 *				loader.option( "theme" );
 *
 *				//setter
 *				loader.option( "theme", "b" );
 *			</script>
 *
 *
 * @deprecated
 * @extends ns.widget.mobile.BaseWidgetMobile
 * @class ns.widget.mobile.Loader
 * @author Maciej Moczulski <m.moczulski@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Piotr Kusztal <p.kusztal@samsung.com>
 */
(function (window, ns, $) {
	"use strict";

	
			/**
			 * {Object} Widget Alias for {@link ns.widget.mobile.BaseWidgetMobile}
			 * @member ns.widget.mobile.Loader
			 * @private
			 */
			var BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				/**
				 * @property {Object} engine Alias for class ns.engine
				 * @member ns.widget.mobile.Loader
				 * @private
				 */
				engine = ns.engine,
				object = ns.util.object,

				Loader = function () {
					var self = this;

					self.action = "";
					self.label = null;
					self.defaultHtml = "";
					self.options = object.copy(Loader.prototype.options);
				},
				classes = {
					uiLoader: "ui-loader",
					uiLoaderPrefix: "ui-loader-",
					uiCorner: "ui-corner-all",
					uiIcon: "ui-icon",
					uiLoaderIcon: "ui-icon-loading",
					uiLoading: "ui-loading",
					uiTextOnly: "ui-loader-textonly"
				},
				properties = {
					pageLoadErrorMessageTheme: "e",
					pageLoadErrorMessage: "Error Loading Page"
				},
				prototype = new BaseWidget();

			/**
			 * Dictionary for loader related css
			 * @property {Object} classes
			 * @member ns.widget.mobile.Loader
			 * @static
			 */
			Loader.classes = classes;

			/**
			 * Dictionary for loader related properties such as messages and
			 * themes
			 * @property {Object} properties
			 * @member ns.widget.mobile.Loader
			 * @static
			 */
			Loader.properties = properties;

			/**
			 * Object with default options
			 * @property {Object} options
			 * @property {boolean} [options.textVisible=false] whether the text
			 * in the loading message is shown
			 * @property {?string} [options.html=""] custom html for the inner
			 * content of the loading messages
			 * @property {string} [options.text="loading"] the text to be
			 * displayed when the loading is shown
			 * @member ns.widget.mobile.Loader
			 */
			prototype.options = {
				textVisible: false,
				html: "",
				text: "loading"
			};

			/**
			 * Build structure of loader widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.Loader
			 */
			prototype._build = function (element) {
				var options = this.options,
					loaderElementSpan = document.createElement("span"),
					loaderElementTile = document.createElement("h1"),
					elementClassList = element.classList,
					spanClassList = loaderElementSpan.classList;

				loaderElementTile.textContent = options.text;
				spanClassList.add(classes.uiIcon);
				spanClassList.add(classes.uiLoaderIcon);

				element.appendChild(loaderElementSpan);
				element.appendChild(loaderElementTile);
				elementClassList.add(classes.uiLoader);
				elementClassList.add(classes.uiCorner);
				elementClassList.add(classes.uiLoaderPrefix + "default");

				this.defaultHtml = element.innerHTML;

				return element;
			};

			/**
			 * Init structure of loader widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.Loader
			 */
			prototype._init = function (element) {
				this.defaultHtml = element.innerHTML;
				return element;
			};

			/**
			 * Reset HTML
			 *
			 * Method resets contained html of loader
			 *
			 *		@example
			 *			<script>
			 *				var loaderWidget = tau.widget.Loader(
			 *						document.getElementById("ns-loader")
			 *				);
			 *				loaderWidget.resetHtml();
			 *
			 *				//or
			 *
			 *				$( "#ns-loader" ).loader( "resetHtml" );
			 *			</script>
			 *
			 * @method resetHtml
			 * @param {HTMLElement} element
			 * @member ns.widget.mobile.Loader
			 */
			prototype.resetHtml = function (element) {
				element = element || this.element;
				element.innerHTML = this.defaultHtml;
			};

			/**
			 * Show loader
			 *
			 * Method shows loader on the page
			 *
			 *		@example
			 *			<script>
			 *				var loaderWidget = tau.widget.Loader(
			 *						document.getElementById("ns-loader")
			 *					);
			 *				loaderWidget.show();
			 *
			 *				// or
			 *
			 *				$( "#ns-loader" ).loader( "show" );
			 *
			 *				//or with parameters
			 *
			 *				$( "#ns-loader" ).loader( "show", {
			 *					text: "foo",
			 *					textVisible: true,
			 *					theme: "z",
			 *					html: ""
			 *				});
			 *			</script>
			 *
			 * @method show
			 * @param {string} theme
			 * @param {string} msgText
			 * @param {boolean} textonly
			 * @member ns.widget.mobile.Loader
			 */
			prototype.show = function (theme, msgText, textonly) {
				var classes = Loader.classes,
					self = this,
					element = self.element,
					elementClassList = element.classList,
					copySettings = {},
					loadSettings = {},
					textVisible,
					message;

				self.resetHtml(element);

				if (theme !== undefined && theme.constructor === Object) {
					copySettings = object.copy(self.options);
					loadSettings = object.merge(copySettings, theme);
					// @todo remove $.mobile.loadingMessageTheme
					theme = loadSettings.theme || $.mobile.loadingMessageTheme;
				} else {
					loadSettings = self.options;
					// @todo remove $.mobile.loadingMessageTheme
					theme = theme || $.mobile.loadingMessageTheme ||
						loadSettings.theme;
				}

				// @todo remove $.mobile.loadingMessage
				message = msgText || $.mobile.loadingMessage ||
					loadSettings.text;
				document.documentElement.classList.add(classes.uiLoading);

				// @todo remove $.mobile.loadingMessage
				if ($.mobile.loadingMessage === false && !loadSettings.html) {
					element.getElementsByTagName("h1")[0].innerHTML = "";
				} else {
					// @todo remove $.mobile.loadingMessageTextVisible
					if ($.mobile.loadingMessageTextVisible !== undefined) {
						textVisible = $.mobile.loadingMessageTextVisible;
					} else {
						textVisible = loadSettings.textVisible;
					}

					element.className = "";
					elementClassList.add(classes.uiLoader);
					elementClassList.add(classes.uiCorner);
					elementClassList.add(classes.uiBodyPrefix + theme);
					elementClassList.add(classes.uiLoaderPrefix +
						(textVisible || msgText ||
							theme.text ? "verbose" : "default"));

					if ((loadSettings.textonly !== undefined &&
						loadSettings.textonly) || textonly) {
						elementClassList.add(classes.uiTextOnly);
					}

					if (loadSettings.html) {
						element.innerHTML = loadSettings.html;
					} else {
						element.getElementsByTagName("h1")[0].textContent =
							message;
					}
				}

			};

			/**
			 * Hide loader
			 *
			 * Method hides loader on the page
			 *
			 *		@example
			 *			<script>
			 *				var loaderWidget = tau.widget.Loader(
			 *						document.getElementById("ns-loader")
			 *					);
			 *				loaderWidget.hide();
			 *
			 *				// or
			 *
			 *				$( "#ns-loader" ).loader( "hide" );
			 *			</script>
			 *
			 * @method hide
			 * @member ns.widget.mobile.Loader
			 */
			prototype.hide = function () {
				var classes = Loader.classes;

				document.documentElement.classList.remove(classes.uiLoading);
			};

			/**
			 * The function "value" is not supported in this widget.
			 *
			 * @method value
			 * @chainable
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Disable the Loader
			 *
			 * Method adds disabled attribute on loader and changes look of
			 * loader to disabled state.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var elementLoader = tau.widget.Loader(
			 *					document.getElementById("ns-loader")
			 *				);
			 *			elementLoader.disable();
			 *		</script>
			 *
			 * If jQuery is loaded:
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			$("#ns-loader").loader("disable");
			 *		</script>
			 *
			 * @method disable
			 * @chainable
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Enable the loader
			 *
			 * Method removes disabled attribute on loader and changes look of
			 * loader to enabled state.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var elementLoader = tau.widget.Loader(
			 *					document.getElementById("ns-loader")
			 *				);
			 *			elementLoader.enable();
			 *		</script>
			 *
			 * If jQuery is loaded:
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			$("#ns-loader").loader("enable");
			 *		</script>
			 *
			 * @method enable
			 * @chainable
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Trigger an event on widget's element.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var elementLoader = tau.widget.Loader(
			 *					document.getElementById("ns-loader")
			 *				);
			 *			elementLoader.trigger("eventName");
			 *		</script>
			 *
			 * If jQuery is loaded:
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			$("#ns-loader").loader("trigger", "eventName");
			 *		</script>
			 *
			 * @method trigger
			 * @param {string} eventName the name of event to trigger
			 * @param {?*} [data] additional object to be carried with the event
			 * @param {boolean} [bubbles=true] indicating whether the event
			 * bubbles up through the DOM or not
			 * @param {boolean} [cancelable=true] indicating whether the event
			 * is cancelable
			 * @return {boolean} false, if any callback invoked preventDefault
			 * on event object
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Add event listener to widget's element.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var elementLoader = tau.widget.Loader(
			 *					document.getElementById("ns-loader")
			 *				);
			 *			elementLoader.on("eventName", function () {
			 *				console.log("Event fires");
			 *			});
			 *		</script>
			 *
			 * If jQuery is loaded:S
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			$("#ns-loader").loader("on", "eventName", function () {
			 *				console.log("Event fires");
			 *			});
			 *		</script>
			 *
			 * @method on
			 * @param {string} eventName the name of event
			 * @param {Function} listener function call after event will be
			 * trigger
			 * @param {boolean} [useCapture=false] useCapture param tu
			 * addEventListener
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Remove event listener to widget's element.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var elementLoader = tau.widget.Loader(
			 *					document.getElementById("ns-loader")
			 *				),
			 *				callback = function () {
			 *					console.log("Event fires");
			 *				};
			 *			// add callback on event "eventName"
			 *			elementLoader.on("eventName", callback);
			 *			// ...
			 *			// remove callback on event "eventName"
			 *			elementLoader.off("eventName", callback);
			 *		</script>
			 *
			 * If jQuery is loaded:
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var callback = function () {
			 *					console.log("Event fires");
			 *				};
			 *			// add callback on event "eventName"
			 *			$("#ns-loader").loader("on", "eventName", callback);
			 *			// ...
			 *			// remove callback on event "eventName"
			 *			$("#ns-loader").loader("off", "eventName", callback);
			 *		</script>
			 * @method off
			 * @param {string} eventName the name of event
			 * @param {Function} listener function call after event will be
			 * trigger
			 * @param {boolean} [useCapture=false] useCapture param to
			 * addEventListener
			 * @member ns.widget.mobile.Loader
			 */

			/**
			 * Get/Set options of the widget.
			 *
			 * This method can work in many context.
			 *
			 * If first argument is type of object them, method set values for
			 * options given in object. Keys of object are names of options and
			 * values from object are values to set.
			 *
			 * If you give only one string argument then method return value
			 * for given option.
			 *
			 * If you give two arguments and first argument will be a string
			 * then second argument will be intemperate as value to set.
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *			<script>
			 *				//getter
			 *				loader.option("text");
			 *
			 *				//setter
			 *				loader.option("text","Loader");
			 *			</script>
			 *
			 * If jQuery is loaded:
			 *
			 *		@example
			 *		<div data-role="loader" id="ns-loader"></div>
			 *
			 *		<script>
			 *			var value;
			 *
			 * 			// get value
			 *			value = $("#ns-loader").loader("option", "text");
			 *
			 *			// set value
			 *			$("#ns-loader").loader(
			 *				"option", "text", "Loader fires"
			 *			);
			 *		</script>
			 *
			 * @method option
			 * @param {string|Object} [name] name of option
			 * @param {*} value value to set
			 * @member ns.widget.mobile.Loader
			 * @return {*} return value of option or undefined if method is
			 * called in setter context
			 */
			// definition
			Loader.prototype = prototype;
			ns.widget.mobile.Loader = Loader;
			engine.defineWidget(
				"Loader",
				"[data-role='loader'], .ui-loader",
				[
					"show",
					"hide",
					"resetHtml"
				],
				Loader,
				"mobile"
			);

			}(window.document, ns, window.$));

/*global window, ns, define, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Drawer Widget
 * Core Drawer widget is a base for creating Drawer widgets for profiles. It
 * provides drawer functionality - container with ability to open and close with
 * an animation.
 *
 * ### Positioning Drawer left / right (option)
 * To change position of a Drawer please set data-position attribute of Drawer
 * element to:
 *
 * - left (left position, default)
 * - right (right position)
 *
 * ##Opening / Closing Drawer
 * To open / close Drawer one can use open() and close() methods.
 *
 * ##Checking if Drawer is opened.
 * To check if Drawer is opened use widget`s isOpen() method.
 *
 * ##Creating widget
 * Core drawer is a base class - examples of creating widgets are described in
 * documentation of profiles
 *
 * @class ns.widget.core.Drawer
 * @extends ns.widget.BaseWidget
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				/**
			 * @property {Object} Widget Alias for {@link ns.widget.BaseWidget}
			 * @member ns.widget.core.Drawer
			 * @private
			 * @static
			 */
			var BaseWidget = ns.widget.BaseWidget,
				/**
				 * @property {Object} selectors Alias for class ns.util.selectors
				 * @member ns.widget.core.Drawer
				 * @private
				 * @static
				 * @readonly
				 */
				selectors = ns.util.selectors,
				utilDOM = ns.util.DOM,
				events = ns.event,
				history = ns.history,
				Gesture = ns.event.gesture,
				Page = ns.widget.core.Page,
				STATE = {
					CLOSED: "closed",
					OPENED: "opened",
					SLIDING: "sliding",
					SETTLING: "settling"
				},
				/**
				 * Events
				 * @event draweropen Event triggered then the drawer is opened.
				 * @event drawerclose Event triggered then the drawer is closed.
				 * @member ns.widget.core.Drawer
				 */
				CUSTOM_EVENTS = {
					OPEN: "draweropen",
					CLOSE: "drawerclose"
				},
				/**
				 * Default values
				 */
				DEFAULT = {
					WIDTH: 240,
					DURATION: 300,
					POSITION: "left"
				},
				/**
				 * Drawer constructor
				 * @method Drawer
				 */
				Drawer = function () {
					var self = this;
					/**
					 * Drawer field containing options
					 * @property {string} options.position Position of Drawer ("left" or "right")
					 * @property {number} options.width Width of Drawer
					 * @property {number} options.duration Duration of Drawer entrance animation
					 * @property {boolean} options.closeOnClick If true Drawer will be closed on overlay
					 * @property {boolean} options.overlay Sets whether to show an overlay when Drawer is open.
					 * @property {string} options.drawerTarget Set drawer target element as the css selector
					 * @property {boolean} options.enable Enable drawer component
					 * @property {number} options.dragEdge Set the area that can open the drawer as drag gesture in drawer target element
					 * @member ns.widget.core.Drawer
					 */

					self.options = {
						position: DEFAULT.POSITION,
						width: DEFAULT.WIDTH,
						duration: DEFAULT.DURATION,
						closeOnClick: true,
						overlay: true,
						drawerTarget: "." + Page.classes.uiPage,
						enable: true,
						dragEdge: 1
					};

					self._pageSelector = null;

					self._isDrag = false;
					self._state = STATE.CLOSED;
					self._settlingType = STATE.CLOSED;
					self._translatedX = 0;

					self._ui = {};

					self._eventBoundElement = null;
					self._drawerOverlay = null;
				},
				/**
				 * Dictionary object containing commonly used widget classes
				 * @property {Object} classes
				 * @member ns.widget.core.Drawer
				 * @private
				 * @static
				 * @readonly
				 */
				classes = {
					page: Page.classes.uiPage,
					drawer: "ui-drawer",
					left: "ui-drawer-left",
					right: "ui-drawer-right",
					overlay: "ui-drawer-overlay",
					open: "ui-drawer-open",
					close: "ui-drawer-close"
				},
				/**
				 * {Object} Drawer widget prototype
				 * @member ns.widget.core.Drawer
				 * @private
				 * @static
				 */
				prototype = new BaseWidget();

			Drawer.prototype = prototype;
			Drawer.classes = classes;

			/**
			 * Unbind drag events
			 * @method unbindDragEvents
			 * @param {Object} self
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Drawer
			 * @private
			 * @static
			 */
			function unbindDragEvents(self, element) {
				var overlayElement = self._ui.drawerOverlay;

				events.disableGesture(element);
				events.off(element, "drag dragstart dragend dragcancel swipe swipeleft swiperight vmouseup", self, false);
				events.prefixedFastOff(self.element, "transitionEnd", self, false);
				events.off(window, "resize", self, false);
				if (overlayElement) {
					events.off(overlayElement, "vclick", self, false);
				}
			}

			/**
			 * Bind drag events
			 * @method bindDragEvents
			 * @param {Object} self
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Drawer
			 * @private
			 * @static
			 */
			function bindDragEvents(self, element) {
				var overlayElement = self._ui.drawerOverlay;

				self._eventBoundElement = element;

				events.enableGesture(
					element,

					new Gesture.Drag(),
					new Gesture.Swipe({
						orientation: Gesture.Orientation.HORIZONTAL
					})
				);

				events.on(element, "drag dragstart dragend dragcancel swipe swipeleft swiperight vmouseup", self, false);
				events.prefixedFastOn(self.element, "transitionEnd", self, false);
				events.on(window, "resize", self, false);
				if (overlayElement) {
					events.on(overlayElement, "vclick", self, false);
				}
			}

			/**
			 * Handle events
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "drag":
						self._onDrag(event);
						break;
					case "dragstart":
						self._onDragStart(event);
						break;
					case "dragend":
						self._onDragEnd(event);
						break;
					case "dragcancel":
						self._onDragCancel(event);
						break;
					case "vmouseup":
						self._onMouseup(event);
						break;
					case "swipe":
					case "swipeleft":
					case "swiperight":
						self._onSwipe(event);
						break;
					case "vclick":
						self._onClick(event);
						break;
					case "transitionend":
					case "webkitTransitionEnd":
					case "mozTransitionEnd":
					case "oTransitionEnd":
					case "msTransitionEnd":
						self._onTransitionEnd(event);
						break;
					case "resize":
						self._onResize(event);
						break;
				}
			};

			/**
			 * MouseUp event handler
			 * @method _onMouseup
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._onMouseup = function () {
				var self = this;

				if (self._state === STATE.SLIDING) {
					self.close();
				}
			};
			/**
			 * Click event handler
			 * @method _onClick
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._onClick = function () {
				var self = this;

				if (self._state === STATE.OPENED) {
					self.close();
				}
			};

			/**
			 * Resize event handler
			 * @method _onResize
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._onResize = function () {
				var self = this;
				// resize event handler

				self._refresh();
			};

			/**
			 * webkitTransitionEnd event handler
			 * @method _onTransitionEnd
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._onTransitionEnd = function () {
				var self = this,
					position = self.options.position,
					drawerOverlay = self._drawerOverlay;

				if (self._state === STATE.SETTLING) {
					if (self._settlingType === STATE.OPENED) {
						self.trigger(CUSTOM_EVENTS.OPEN, {
							position: position
						});
						self._setActive(true);
						self._state = STATE.OPENED;
					} else {
						self.close();
						self.trigger(CUSTOM_EVENTS.CLOSE, {
							position: position
						});
						self._setActive(false);
						self._state = STATE.CLOSED;
						if (drawerOverlay) {
							drawerOverlay.style.visibility = "hidden";
						}
					}
				}
			};

			/**
			 * Swipe event handler
			 * @method _onSwipe
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype._onSwipe = function (event) {
				var self = this,
					direction,
					options = self.options;

				// Now mobile has two swipe event
				if (event.detail) {
					direction = event.detail.direction === "left" ? "right" : "left";
				} else if (event.type === "swiperight") {
					direction = "left";
				} else if (event.type === "swipeleft") {
					direction = "right";
				}
				if (options.enable && self._isDrag && options.position === direction) {
					self.open();
					self._isDrag = false;
				}
			};
			/**
			 * Dragstart event handler
			 * @method _onDragStart
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype._onDragStart = function (event) {
				var self = this;

				if (self._state === STATE.OPENED) {
					return;
				}
				if (self.options.enable && !self._isDrag && self._state !== STATE.SETTLING && self._checkSideEdge(event)) {
					self._isDrag = true;
				} else {
					self.close();
				}
			};
			/**
			 * Drag event handler
			 * @method _onDrag
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype._onDrag = function (event) {
				var self = this,
					deltaX = event.detail.deltaX,
					options = self.options,
					translatedX = self._translatedX,
					movedX;

				if (options.enable && self._isDrag && self._state !== STATE.SETTLING) {
					if (options.position === "left") {
						movedX = -options.width + deltaX + translatedX;
						if (movedX < 0) {
							self._translate(movedX, 0);
						}
					} else {
						movedX = window.innerWidth + deltaX - translatedX;
						if (movedX > 0 && movedX > window.innerWidth - options.width) {
							self._translate(movedX, 0);
						}
					}
				}
			};
			/**
			 * DragEnd event handler
			 * @method _onDragEnd
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype._onDragEnd = function (event) {
				var self = this,
					options = self.options,
					detail = event.detail;

				if (options.enable && self._isDrag) {
					if (Math.abs(detail.deltaX) > options.width / 2) {
						self.open();
					} else if (self._state !== STATE.SETTLING) {
						self.close();
					}
				}
				self._isDrag = false;
			};

			/**
			 * DragCancel event handler
			 * @method _onDragCancel
			 * @protected
			 * @member ns.widget.core.Drawer
			 */
			prototype._onDragCancel = function () {
				var self = this;

				if (self.options.enable && self._isDrag) {
					self.close();
				}
				self._isDrag = false;
			};
			/**
			 * Drawer translate function
			 * @method _translate
			 * @param {number} x
			 * @param {number} duration
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._translate = function (x, duration) {
				var self = this,
					element = self.element;

				if (self._state !== STATE.SETTLING) {
					self._state = STATE.SLIDING;
				}

				if (duration) {
					utilDOM.setPrefixedStyle(element, "transition", utilDOM.getPrefixedValue("transform " + duration / 1000 + "s ease-out"));
				}

				// there should be a helper for this :(
				utilDOM.setPrefixedStyle(element, "transform", "translate3d(" + x + "px, 0px, 0px)");
				if (self.options.overlay) {
					self._setOverlay(x);
				}
				if (!duration) {
					self._onTransitionEnd();
				}

			};

			/**
			 * Set overlay opacity and visibility
			 * @method _setOverlay
			 * @param {number} x
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._setOverlay = function (x) {
				var self = this,
					options = self.options,
					overlay = self._ui.drawerOverlay,
					overlayStyle = overlay.style,
					absX = Math.abs(x),
					ratio = options.position === "right" ? absX / window.innerWidth : absX / options.width;

				if (ratio < 1) {
					overlayStyle.visibility = "visible";
				} else {
					overlayStyle.visibility = "hidden";
				}
				overlayStyle.opacity = 1 - ratio;
			};

			/**
			 * Set active status in drawer router
			 * @method _setActive
			 * @param {boolean} active
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._setActive = function (active) {
				var self = this,
					route = ns.router.getInstance().getRoute("drawer");

				if (active) {
					route.setActive(self);
				} else {
					route.setActive(null);
				}
			};

			/**
			 * Build structure of Drawer widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement} Returns built element
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._build = function (element) {
				var self = this,
					ui = self._ui,
					options = self.options,
					targetElement;

				element.classList.add(classes.drawer);
				element.style.top = 0;
				targetElement = selectors.getClosestBySelector(element, options.drawerTarget);

				if (targetElement) {
					targetElement.appendChild(element);
					targetElement.style.overflowX = "hidden";
				}

				if (self.options.overlay) {
					ui.drawerOverlay = self._createOverlay(element);
					ui.drawerOverlay.style.visibility = "hidden";
				}

				if (!ui.placeholder) {
					ui.placeholder = document.createComment(element.id + "-placeholder");
					element.parentNode.insertBefore(ui.placeholder, element);
				}
				ui.targetElement = targetElement;
				return element;
			};

			/**
			 * Initialization of Drawer widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui;

				ui.drawerPage = selectors.getClosestByClass(element, classes.page);
				ui.drawerPage.style.overflowX = "hidden";
				self._initLayout();
				return element;
			};

			/**
			 * init Drawer widget layout
			 * @method _initLayout
			 * @protected
			 * @member ns.widget.core.Drawer
			 */
			prototype._initLayout = function () {
				var self = this,
					options = self.options,
					element = self.element,
					elementStyle = element.style,
					ui = self._ui,
					overlayStyle = ui.drawerOverlay ? ui.drawerOverlay.style : null,
					height;

				options.width = options.width || ui.targetElement.offsetWidth;
				height = ui.targetElement.offsetHeight;

				elementStyle.width = (options.width !== 0) ? options.width + "px" : "100%";
				elementStyle.height = (height !== 0) ? height + "px" : "100%";
				elementStyle.top = "0";

				if (overlayStyle) {
					overlayStyle.width = window.innerWidth + "px";
					overlayStyle.height = window.innerHeight + "px";
					overlayStyle.top = 0;
				}
				if (options.position === "right") {
					element.classList.add(classes.right);
					self._translate(window.innerWidth, 0);
				} else {
					// left or default
					element.classList.add(classes.left);
					self._translate(-options.width, 0);
				}

				self._state = STATE.CLOSED;
			};

			/**
			 * Provides translation if position is set to right
			 * @method _translateRight
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._translateRight = function () {
				var self = this,
					options = self.options;

				if (options.position === "right") {
					// If drawer position is right, drawer should be moved right side
					if (self._state === STATE.OPENED) {
						// drawer opened
						self._translate(window.innerWidth - options.width, 0);
					} else {
						// drawer closed
						self._translate(window.innerWidth, 0);
					}
				}
			};

			/**
			 * Check dragstart event whether triggered on side edge area or not
			 * @method _checkSideEdge
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Drawer
			 */
			prototype._checkSideEdge = function (event) {
				var self = this,
					detail = event.detail,
					eventClientX = detail.pointer.clientX - detail.estimatedDeltaX,
					options = self.options,
					position = options.position,
					boundElement = self._eventBoundElement,
					boundElementOffsetWidth = boundElement.offsetWidth,
					boundElementRightEdge = boundElement.offsetLeft + boundElementOffsetWidth,
					dragStartArea = boundElementOffsetWidth * options.dragEdge;

				return ((position === "left" && eventClientX > 0 && eventClientX < dragStartArea) ||
				(position === "right" && eventClientX > boundElementRightEdge - dragStartArea &&
				eventClientX < boundElementRightEdge));
			};
			/**
			 * Refreshes Drawer widget
			 * @method _refresh
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._refresh = function () {
				// Drawer layout has been set by parent element layout
				var self = this;

				self._translateRight();
				self._initLayout();
			};
			/**
			 * Creates Drawer overlay element
			 * @method _createOverlay
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._createOverlay = function (element) {
				var overlayElement = document.createElement("div");

				overlayElement.classList.add(classes.overlay);
				element.parentNode.insertBefore(overlayElement, element);

				return overlayElement;
			};

			/**
			 * Binds events to a Drawer widget
			 * @method _bindEvents
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._bindEvents = function () {
				var self = this,
					targetElement = self._ui.targetElement;

				bindDragEvents(self, targetElement);
			};

			/**
			 * Enable Drawer widget
			 * @method _enable
			 * @protected
			 * @member ns.widget.core.Drawer
			 */
			prototype._enable = function () {
				this._oneOption("enable", true);
			};

			/**
			 * Disable Drawer widget
			 * @method _disable
			 * @protected
			 * @member ns.widget.core.Drawer
			 */
			prototype._disable = function () {
				this._oneOption("enable", false);
			};

			/**
			 * Checks Drawer status
			 * @method isOpen
			 * @member ns.widget.core.Drawer
			 * @return {boolean} Returns true if Drawer is open
			 */
			prototype.isOpen = function () {
				return (this._state === STATE.OPENED);
			};

			/**
			 * Opens Drawer widget
			 * @method open
			 * @param {number} [duration] Duration for opening, if is not set then method take value from options
			 * @member ns.widget.core.Drawer
			 */
			prototype.open = function (duration) {
				var self = this,
					options = self.options,
					drawerClassList = self.element.classList,
					drawerOverlay = self._ui.drawerOverlay;

				if (self._state !== STATE.OPENED) {
					self._state = STATE.SETTLING;
					self._settlingType = STATE.OPENED;
					duration = duration !== undefined ? duration : options.duration;
					if (drawerOverlay) {
						drawerOverlay.style.visibility = "visible";
					}
					drawerClassList.remove(classes.close);
					drawerClassList.add(classes.open);
					if (options.position === "left") {
						self._translate(0, duration);
					} else {
						self._translate(window.innerWidth - options.width, duration);
					}
				}
			};

			/**
			 * Closes Drawer widget
			 * @requires mobile wearable
			 *
			 *
			 * @method close
			 * @param {Object} options This value is router options whether reverse or not.
			 * @param {number} [duration] Duration for closing, if is not set then method take value from options
			 * @member ns.widget.core.Drawer
			 */
			prototype.close = function (options, duration) {
				var self = this,
					reverse = options ? options.reverse : false,
					selfOptions = self.options,
					drawerClassList = self.element.classList;

				if (self._state !== STATE.CLOSED) {
					if (!reverse && self._state === STATE.OPENED && !ns.getConfig("disableRouter")) {
						// This method was fired by JS code or this widget.
						history.back();
						return;
					}
					self._state = STATE.SETTLING;
					self._settlingType = STATE.CLOSED;
					duration = duration !== undefined ? duration : selfOptions.duration;
					drawerClassList.remove(classes.open);
					drawerClassList.add(classes.close);
					if (selfOptions.position === "left") {
						self._translate(-selfOptions.width, duration);
					} else {
						self._translate(window.innerWidth, duration);
					}
				}
			};

			/**
			 * Set Drawer drag handler.
			 * If developer use handler, drag event is bound at handler only.
			 * @method setDragHandler
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Drawer
			 */
			prototype.setDragHandler = function (element) {
				var self = this;

				self.options.dragEdge = 1;
				unbindDragEvents(self, self._eventBoundElement);
				bindDragEvents(self, element);
			};

			/**
			 * Transition Drawer widget.
			 * This method use only positive integer number.
			 * @method transition
			 * @param {number} position
			 * @member ns.widget.core.Drawer
			 */
			prototype.transition = function (position) {
				var self = this,
					options = self.options;

				if (options.position === "left") {
					self._translate(-options.width + position, options.duration);
				} else {
					self._translate(options.width - position, options.duration);
				}
				self._translatedX = position;
			};

			/**
			 * Get state of Drawer widget.
			 */
			prototype.getState = function () {
				return this._state;
			};
			/**
			 * Destroys Drawer widget
			 * @method _destroy
			 * @member ns.widget.core.Drawer
			 * @protected
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self._ui,
					drawerOverlay = ui.drawerOverlay,
					placeholder = ui.placeholder,
					placeholderParent = placeholder.parentNode,
					element = self.element;

				placeholderParent.insertBefore(element, placeholder);
				placeholderParent.removeChild(placeholder);

				if (drawerOverlay) {
					drawerOverlay.removeEventListener("vclick", self._onClickBound, false);
				}
				unbindDragEvents(self, self._eventBoundElement);
			};

			ns.widget.core.Drawer = Drawer;

			}(window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Drawer Widget
 * Drawer widget provide creating drawer widget and managing drawer operation.
 *
 * @since 2.3
 * @class ns.widget.mobile.Drawer
 * @extends ns.widget.core.Drawer
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var CoreDrawer = ns.widget.core.Drawer,
				engine = ns.engine,
				Drawer = function () {
					CoreDrawer.call(this);
				},
				prototype = new CoreDrawer();

			Drawer.prototype = prototype;

			/**
			 * Configure Drawer widget
			 * @method _configure
			 * @protected
			 * @member ns.widget.mobile.Drawer
			 */
			prototype._configure = function () {
				var self = this;
				/**
				 * Widget options
				 * @property {string} [options.drawerTarget="ui-page"] Drawer appended target. Value type is CSS selector type.
				 * @property {string} [options.position="left"] Drawer position. "left" or "right"
				 * @property {boolean} [options.enable=true] Enable drawer component.
				 * @property {Number} [options.dragEdge=0.05] Start dragging gesture possible area ratio of target or handler between 0 and 1.
				 * @member ns.widget.mobile.Drawer
				 */

				self.options.dragEdge = 0.05;
			};

			ns.widget.mobile.Drawer = Drawer;
			engine.defineWidget(
				"Drawer",
				"[data-role='drawer'], .ui-drawer",
				[
					"transition",
					"setDragHandler",
					"open",
					"close",
					"isOpen",
					"getState"
				],
				Drawer,
				"mobile"
			);

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * # Toggle Switch
 * Toggle switch component is a common UI element used for binary on/off or true/false data input.
 *
 * The toggle switch widget shows a 2-state switch on the screen.
 * If defined with select type with options it creates toggles
 * On the toggle its possible to tap one side of the switch.
 *
 * We still support toggles defined with select tag for backward compatibility
 *
 * ## Default selectors
 * INPUT tags with _data-role=toggleswitch_ or SELECT tags
 * with _data-role=toggleswitch_
 * changed to toggle switch
 * To add a toggle switch widget to the application, use the following code:
 *
 *        @example
 *        <input type="checkbox" data-role="toggleswitch">
 *
 *        @example
 *        <select name="flip-11" id="flip-11" data-role="toggleswitch">
 *            <option value="off"></option>
 *            <option value="on"></option>
 *        </select>
 *
 *        @example
 *        <select name="flip-11" id="flip-11" data-role="toggleswitch">
 *            <option value="off">off option</option>
 *            <option value="on">on option</option>
 *        </select>
 *
 * ## Manual constructor
 * For manual creation of toggle switch widget you can use constructor of
 * widget from **tau** namespace:
 *
 *        @example
 *        <select id="toggle" name="flip-11" id="flip-11" data-role="toggleswitch"
 *        data-mini="true">
 *            <option value="off"></option>
 *            <option value="on"></option>
 *        </select>
 *        <script>
 *            var toggleElement = document.getElementById("toggle"),
 *                toggle = tau.widget.ToggleSwitch(toggleElement);
 *        </script>
 *
 * ## JavaScript API
 *
 * ToggleSwitch widget hasn't JavaScript API.
 *
 * @since 2.0
 * @class ns.widget.mobile.ToggleSwitch
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var ToggleSwitch = function () {
					var self = this;

					self._ui = {};
				},
				BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				engine = ns.engine,
				events = ns.event,

				classes = {
					toggleContainer: "ui-toggle-container",
					toggle: "ui-toggle-switch",
					toggleHandler: "ui-switch-handler"
				},
				keyCode = {
					HOME: 36,
					END: 35,
					PAGE_UP: 33,
					PAGE_DOWN: 34,
					UP: 38,
					RIGHT: 39,
					DOWN: 40,
					LEFT: 37
				};

			ToggleSwitch.prototype = new BaseWidget();

			/**
			 * Dictionary for ToggleSwitch related css class names
			 * @property {Object} classes
			 * @member ns.widget.mobile.ToggleSwitch
			 * @static
			 * @readonly
			 */
			ToggleSwitch.classes = classes;

			/**
			 * Dictionary for keyboard codes
			 * @property {Object} keyCode
			 * @member ns.widget.mobile.ToggleSwitch
			 * @static
			 * @readonly
			 */
			ToggleSwitch.keyCode = keyCode;


			/**
			 * Callback change the value of input type=checkbox
			 * (method strictly for toggleswitch based oninput tag)
			 * @method onChangeValue
			 * @param {ns.widget.mobile.ToggleSwitch} self
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function onChangeValue(self) {
				var element = self.element;

				element.selectedIndex = (self._ui.input.checked) ? 1 : 0;

				if (self._type === "select") {
					events.trigger(element, "change");
				}
			}

			/**
			 * Simplify creating dom elements
			 * (method strictly for toggleswitch based oninput tag)
			 * @method createElement
			 * @param {string} name
			 * @return {HTMLElement}
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function createElement(name) {
				return document.createElement(name);
			}

			/**
			 * Creates and set up input element
			 * @method setUpInput
			 * @return {HTMLElement}
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function setUpInput() {
				var inputElement = createElement("input");

				inputElement.type = "checkbox";
				return inputElement;
			}

			/**
			 * Build Toggle based on Select Tag
			 * @method buildToggleBasedOnSelectTag
			 * @param {HTMLElement} element
			 * @param {HTMLElement} divHandler
			 * @param {HTMLElement} toggleContainer
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function buildToggleBasedOnSelectTag(element, divHandler, toggleContainer) {
				var inputElement;

				element.style.display = "none";
				element.parentNode.insertBefore(toggleContainer, element);
				inputElement = setUpInput();

				if (element.hasAttribute("disabled")) {
					inputElement.setAttribute("disabled", "disabled");
				}

				inputElement.className = classes.toggle;
				toggleContainer.className = classes.toggleContainer;

				toggleContainer.appendChild(inputElement);
				toggleContainer.appendChild(divHandler);
				toggleContainer.appendChild(element);

			}

			/**
			 * Build Toggle based on Input Tag
			 * @method buildToggleBasedOnInputTag
			 * @param {HTMLElement} element
			 * @param {HTMLElement} divHandler
			 * @param {HTMLElement} toggleContainer
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function buildToggleBasedOnInputTag(element, divHandler, toggleContainer) {
				element.className = classes.toggle;
				toggleContainer.className = classes.toggleContainer;

				element.parentNode.insertBefore(toggleContainer, element);
				toggleContainer.appendChild(element);
				toggleContainer.appendChild(divHandler);
			}

			/**
			 * Build Toggle based on Custom Element
			 * @method buildToggleBasedOnCustomElement
			 * @param {HTMLElement} divHandler
			 * @param {HTMLElement} toggleContainer
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function buildToggleBasedOnCustomElement(divHandler, toggleContainer) {
				var inputElement = setUpInput();

				inputElement.className = classes.toggle;
				toggleContainer.className = classes.toggleContainer;

				toggleContainer.appendChild(inputElement);
				toggleContainer.appendChild(divHandler);

				if (toggleContainer.hasAttribute("disabled")) {
					inputElement.setAttribute("disabled", "disabled");
				}
			}

			/**
			 * Build ToggleSwitch
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			ToggleSwitch.prototype._build = function (element) {
				var divHandler = createElement("div"),
					toggleContainer = createElement("div"),
					controlType = element.nodeName.toLowerCase();


				if (controlType === "input") {
					buildToggleBasedOnInputTag(element, divHandler, toggleContainer);
				}
				if (controlType === "select") {
					buildToggleBasedOnSelectTag(element, divHandler, toggleContainer);
				}
				if (controlType === "tau-toggleswitch") {
					buildToggleBasedOnCustomElement(divHandler, element);
				}

				divHandler.className = classes.toggleHandler;

				this._type = controlType;

				return element;
			};

			/**
			 * Initiate widget
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 * @instance
			 */
			ToggleSwitch.prototype._init = function (element) {
				this._ui.input = element.parentElement.firstChild;
			};

			/**
			 * Get value of toggle switch. If widget is based on input type
			 * tag otherwise it return index of the element
			 * @method _getValue
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			ToggleSwitch.prototype._getValue = function () {
				var self = this,
					element = self.element;

				return self._type === "input" ?
					parseFloat(element.value) : element.selectedIndex;
			};

			/**
			 * Set value of toggle switch
			 * @method _setValue
			 * @param {string} value
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			ToggleSwitch.prototype._setValue = function (value) {
				var self = this,
					element = self.element;

				if (self._type === "input") {
					element.value = value;
				}
				if (self._type === "select") {
					element.selectedIndex = value;
				}
			};

			/**
			 * Binds events to widget
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 * @instance
			 */
			ToggleSwitch.prototype._bindEvents = function () {
				var self = this;

				self._onChangeValue = onChangeValue.bind(null, self);
				self._ui.input.addEventListener("change",
					self._onChangeValue, true);
			};

			/**
			 * remove attributes when destroyed
			 * @method removeAttributesWhenDestroy
			 * @param {HTMLElement} element
			 * @private
			 * @static
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			function removeAttributesWhenDestroy(element) {
				element.removeAttribute("data-tau-name");
				element.removeAttribute("aria-disabled");
				element.removeAttribute("data-tau-bound");
				element.removeAttribute("data-tau-built");
			}

			/**
			 * Destroy widget
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.ToggleSwitch
			 */
			ToggleSwitch.prototype._destroy = function () {
				var self = this,
					element = self.element,
					tagName = self._type,
					container = element.parentElement;

				self._ui.input.removeEventListener("change",
					self._onChangeValue, true);

				removeAttributesWhenDestroy(element);

				//remove visible representative
				if (tagName === "input" || tagName === "select") {
					container.parentElement.insertBefore(element, container);
					container.parentElement.removeChild(container);
				}

				if (tagName === "input") {
					element.classList.remove(classes.toggle);
				}

				events.trigger(document, "destroyed", {
					widget: "ToggleSwitch",
					parent: element.parentNode
				});
			};

			ns.widget.mobile.ToggleSwitch = ToggleSwitch;
			engine.defineWidget(
				"ToggleSwitch",
				"input[data-role='toggleswitch']," +
				"select[data-role='toggleswitch']," +
				"select.ui-toggleswitch," +
				"input.ui-toggleswitch",
				[],
				ToggleSwitch,
				"mobile"
			);
			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * # Navigation
 * Navigation component is used inside the header to navigate back and forth according to the navigational history array,
 * which is created by the application. By clicking a horizontally-listed element on the navigation bar,
 * the user can move to the target page.
 *
 * By clicking horizontally listed element on the Navigation Bar,
 *  a page is possible to navigate to the target page.
 *
 * ##Default selector
 * You can make the navigation widget as data-role="navigation".
 * To use *NAV* tag is recommended for semantic understanding.
 *
 * ####  Create Navigation Bar using data-role
 *
 * 		@example
 *		<div data-role="page" id="pageId">
 *			<div data-role="header" data-position="fixed">
 *				<h1>title</h1>
 *				<nav data-role="navigation" id="navigation"></nav>
 *			</div>
 *			<div data-role="content"></div>
 *		</div>
 *
 * ##HTML Examples
 *
 * ####How to use Navigation Bar in your code
 *
 * This component is made to support navigational
 * indication on panel change mainly. So Navigation Bar is
 * not only hard to be used alone, but also not efficient with solo usage.
 *
 * The essential function of navigation bar is to add navigational item
 * according to the given element's id. And this component also
 * provides navigational changes on the click of each item.
 *
 *
 * To create a navigation bar, navigation bar needs to be defined in
 * your HTMl with 'data-role="navigation"' or 'class="ui-navigation"'.
 *
 * 		@example
 *		<div data-role="page" id="navigation-bar">
 *			<!-- declare navigation in header -->
 *			<div data-role="header" data-position="fixed">
 *				<h1>Navigation Bar</h1>
 *				<nav data-role="navigation" id="navigation"></nav>
 *			</div>
 *
 *			<!-- you can put several panels to move -->
 *			<div data-role="content">
 *			</div>
 *		</div>
 *
 * @since 2.3
 * @class ns.widget.mobile.Navigation
 * @extends ns.widget.BaseWidget
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 * @author Maciej Moczulski <m.moczulsku@samsung.com>
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 * @author Heeju Joo <heeju.joo@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Koeun Choi <koeun.choi@samsung.com>
 * @author Piort Karny <p.karny@samsung.com>
 * @author Krzysztof Antonszek <k.antonszek@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.mobile.BaseWidgetMobile,
				engine = ns.engine,
				events = ns.event,
				selectors = ns.util.selectors,
				Navigation = function () {
					var self = this;

					self._clickBound = null;
					self._ui = {};
					self._navigationStack = [];
				},

				attributes = {
					POSITION: "data-position"
				},
				/**
				 * Dictionary object containing commonly used widget classes
				 * @property {Object} classes
				 * @static
				 * @member ns.widget.mobile.Navigation
				 */
				classes = {
					NAVIGATION: "ui-navigation",
					NAVIGATION_CONTAINER: "ui-navigation-container",
					NAVIGATION_ITEM: "ui-navigation-item",
					NAVIGATION_ACTIVE: "ui-navigation-active",
					NAVIGATION_HIDE: "ui-navigator-hide",
					NAVIGATION_BACK: "ui-navigator-back",
					NAVIGATION_BACK_HIDE: "ui-navigator-back-hide",
					NAVIGATION_ACTIVE_ANIMATION: "ui-navigator-active-animation"
				},
				prototype = new BaseWidget();

			Navigation.prototype = prototype;
			Navigation.classes = classes;
			Navigation.attributes = attributes;

			/**
			 * Navigation navigateTrigger function
			 * @method navigateTrigger
			 * @private
			 * @static
			 * @param {event} event
			 * @member ns.widget.mobile.Navigation
			 */
			function onClick(event) {
				var self = this,
					target = selectors.getClosestByClass(event.target, classes.NAVIGATION_ITEM),
					position = target && parseInt(target.getAttribute(attributes.POSITION), 10),
					stack = self._navigationStack,
					id = stack[position],
					toRemoveLength = stack.length - 1 - position;

				if (target) {
					setTimeout(function () {
						if (!target.classList.contains(classes.NAVIGATION_ACTIVE) && stack[stack.length - 1] !== id) {
							self.pop(toRemoveLength);
							self.trigger("navigate", {
								id: id,
								//element Id to move
								position: position
							});
						}
					}, 0);
				}
			}

			/**
			 * Build structure of Navigation widget
			 * @method _build
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.Navigation
			 */
			prototype._build = function (element) {
				var container;

				element.classList.add(classes.NAVIGATION);
				container = document.createElement("ul");
				container.classList.add(classes.NAVIGATION_CONTAINER);

				this._ui.container = container;

				element.appendChild(container);
				return element;
			};

			/**
			 * Bind events of Navigation widget
			 * @method _bindEvents
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.Navigation
			 */
			prototype._bindEvents = function (element) {
				var self = this;

				self._clickBound = onClick.bind(self);
				element.addEventListener("vclick", self._clickBound, false);
			};


			/**
			 * Initiate creating navigation bar in old version way
			 * @method create
			 * @public
			 * @deprecated
			 * @member ns.widget.mobile.Navigation
			 */
			prototype.create = function () {
				ns.warn("Create method is deprecated because with 'create' method," +
				"it is hard to meet the newly changed Navigation function. " +
				"To handle Navigation Bar, please utilize 'push' method with " +
				"and 'panelChanger' component, instead.");
			};

			/**
			 * Remove navigation bar item or items
			 * @param {number} count
			 * @method pop
			 * @member ns.widget.mobile.Navigation
			 */
			prototype.pop = function (count) {
				var self = this,
					container = self._ui.container,
					stack = self._navigationStack,
					lastChild = container.lastChild,
					lastChildClassList = lastChild && lastChild.classList,
					previousLastChildClassList = lastChild && lastChild.previousElementSibling && lastChild.previousElementSibling.classList;

				if (count === undefined) {
					count = 1;
				}

				if (lastChildClassList) {
					lastChildClassList.add(classes.NAVIGATION_HIDE);
					if (previousLastChildClassList) {
						previousLastChildClassList.add(classes.NAVIGATION_BACK);
						previousLastChildClassList.add(classes.NAVIGATION_ACTIVE);
						events.one(lastChild, "animationend, webkitAnimationEnd", function () {
							container.removeChild(container.lastChild);
							lastChildClassList.remove(classes.NAVIGATION_BACK);
							if (count > 1) {
								self.pop(count - 1);
							}
						});
					}
					stack.pop();
				}
			};

			/**
			 * Add navigation bar item only one at a time
			 * @method push
			 * @param {string} id
			 * @member ns.widget.mobile.Navigation
			 */
			prototype.push = function (id) {
				var self = this,
					element = self.element,
					stack = self._navigationStack,
					container = self._ui.container,
					itemLength = container.childElementCount,
					lastChild = container.lastElementChild,
					lastChildClassList = lastChild && lastChild.classList,
					listClassList = null,
					list,
					arrow,
					text;

				stack.push(id);
				if (itemLength > 0) {
					lastChildClassList.add(classes.NAVIGATION_BACK_HIDE);
					events.one(lastChild, "animationend webkitAnimationEnd", function () {
						//both animation end events should trigger right after push but often only one triggered
						//and second triggered after using pop method and thus removed unintentionally useful classes
						if (lastChild !== self._ui.container.lastElementChild) {
							lastChildClassList.remove(classes.NAVIGATION_BACK_HIDE);
							lastChildClassList.remove(classes.NAVIGATION_ACTIVE);
						}
					});
				}

				list = document.createElement("li");
				list.setAttribute(attributes.POSITION, itemLength);
				listClassList = list.classList;
				listClassList.add(classes.NAVIGATION_ITEM);

				if (itemLength > 0) {
					arrow = document.createElement("span");
					arrow.classList.add("ui-arrow");
					list.appendChild(arrow);
				}

				text = document.createElement("span");
				text.classList.add("ui-text");
				text.innerHTML = id;
				list.appendChild(text);

				listClassList.add(classes.NAVIGATION_ACTIVE);
				listClassList.add(classes.NAVIGATION_ACTIVE_ANIMATION);
				events.one(list, "animationend webkitAnimationEnd", function () {
					listClassList.remove(classes.NAVIGATION_ACTIVE_ANIMATION);
				});

				container.appendChild(list);
				if (container.offsetWidth > element.offsetWidth) {
					element.scrollLeft = container.offsetWidth - element.offsetWidth;
				}
			};

			/**
			 * Destroy Navigation widget
			 * @method _destroy
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.Navigation
			 */
			prototype._destroy = function (element) {
				var self = this;

				element.removeEventListener("vclick", self._clickBound, false);
				element.removeChild(self._ui.container);
				self._clickBound = null;
				self._ui = null;
				self._navigationStack = null;
			};

			ns.widget.mobile.Navigation = Navigation;
			engine.defineWidget(
				"Navigation",
				"[data-role='navigation'], .ui-navigation",
				[
					"push",
					"pop",
					"create"
				],
				Navigation,
				"mobile"
			);

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (ns) {
	"use strict";
				/** @namespace ns.widget.wearable */
			ns.widget.core.indexscrollbar = ns.widget.core.indexscrollbar || {};
			}(ns));

/*global define, ns, document, window */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #IndexBar widget
 * Widget creates bar with index.
 *
 * @internal
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @class ns.widget.wearable.indexscrollbar.IndexBar
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				utilsDOM = ns.util.DOM;

			function IndexBar(element, options) {
				this.element = element;
				this.options = utilsObject.merge(options, this._options, false);
				this.container = this.options.container;

				this.indices = {
					original: this.options.index,
					merged: []
				};

				this._init();

				return this;
			}

			IndexBar.prototype = {
				_options: {
					container: null,
					offsetLeft: 0,
					index: [],
					verticalCenter: false,
					moreChar: "*",
					moreCharLineHeight: 9,
					indexHeight: 41,
					selectedClass: "ui-state-selected",
					ulClass: null,
					maxIndexLen: 0
				},
				_init: function () {
					this.indices.original = this.options.index;
					this.indexLookupTable = [];
					this.indexElements = null;
					this.selectedIndex = -1;
					this.visiblity = "hidden";

					this._setMaxIndexLen();
					this._makeMergedIndices();
					this._drawDOM();
					this._appendToContainer();
					if (this.options.verticalCenter) {
						this._adjustVerticalCenter();
					}
					this._setIndexCellInfo();
				},

				_clear: function () {
					while (this.element.firstChild) {
						this.element.removeChild(this.element.firstChild);
					}

					this.indices.merged.length = 0;
					this.indexLookupTable.length = 0;
					this.indexElements = null;
					this.selectedIndex = -1;
					this.visiblity = null;
				},

				/**
				 * Refreshes widget.
				 * @method refresh
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				refresh: function () {
					this._clear();
					this._init();
				},

				/**
				 * Destroys widget.
				 * @method destroy
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				destroy: function () {
					this._clear();
				},

				/**
				 * Shows widget.
				 * @method show
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				show: function () {
					this.visibility = "visible";
					this.element.style.visibility = this.visibility;
				},

				/**
				 * Hides widget.
				 * @method hide
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				hide: function () {
					this.visibility = "hidden";
					this.element.style.visibility = this.visibility;
				},

				/**
				 * Get if the visibility status is shown or not
				 * @method isShown
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				isShown: function () {
					return "visible" === this.visibility;
				},

				_setMaxIndexLen: function () {
					var maxIndexLen,
						self = this,
						options = self.options,
						container = self.container,
						indexHeight = options.indexHeight,
						containerHeight = container.offsetHeight;

					maxIndexLen = Math.floor(containerHeight / indexHeight);
					if (maxIndexLen > 0 && maxIndexLen % 2 === 0) {
						maxIndexLen -= 1;	// Ensure odd number
					}
					options.maxIndexLen = options.maxIndexLen > 0 ? Math.min(maxIndexLen, options.maxIndexLen) : maxIndexLen;

				},

				_makeMergedIndices: function () {
					var origIndices = this.indices.original,
						origIndexLen = origIndices.length,
						visibleIndexLen = Math.min(this.options.maxIndexLen, origIndexLen),
						totalLeft = origIndexLen - visibleIndexLen,
						nIndexPerItem = parseInt(totalLeft / parseInt(visibleIndexLen / 2, 10), 10),
						leftItems = totalLeft % parseInt(visibleIndexLen / 2, 10),
						indexItemSize = [],
						mergedIndices = [],
						i,
						len,
						position = 0;

					for (i = 0, len = visibleIndexLen; i < len; i++) {
						indexItemSize[i] = 1;
						if (i % 2) {	// omit even numbers
							indexItemSize[i] += nIndexPerItem + (leftItems-- > 0 ? 1 : 0);
						}
						position += indexItemSize[i];
						mergedIndices.push({
							start: position - 1,
							length: indexItemSize[i]
						});
					}
					this.indices.merged = mergedIndices;
				},

				_drawDOM: function () {
					var origIndices = this.indices.original,
						indices = this.indices.merged,
						indexLen = indices.length,
						indexHeight = this.options.indexHeight,
						moreChar = this.options.moreChar,
						// Height of the last index is total height - all other indexes
						lastElementHeight = this.container.clientHeight - ((indexLen - 1) * indexHeight),
						addMoreCharLineHeight = this.options.moreCharLineHeight,
						text,
						frag,
						li,
						i,
						m;

					frag = document.createDocumentFragment();
					for (i = 0; i < indexLen; i++) {
						m = indices[i];
						text = m.length === 1 ? origIndices[m.start] : moreChar;
						li = document.createElement("li");
						li.innerText = text.toUpperCase();

						li.style.height = ((i === indexLen - 1) ? lastElementHeight : indexHeight) + "px";
						li.style.lineHeight = text === moreChar ? indexHeight + addMoreCharLineHeight + "px" : indexHeight + "px";

						frag.appendChild(li);
					}
					this.element.appendChild(frag);

					if (this.options.ulClass) {
						this.element.classList.add(this.options.ulClass);
					}
				},

				_adjustVerticalCenter: function () {
					var nItem = this.indices.merged.length,
						totalIndexLen = nItem * this.options.indexHeight,
						vPadding = parseInt((this.container.offsetHeight - totalIndexLen) / 2, 10);

					this.element.style.paddingTop = vPadding + "px";
				},

				_appendToContainer: function () {
					var self = this,
						options = self.options,
						element = self.element,
						container = self.container,
						elementStyle = element.style,
						divWithMargin = document.createElement("div"),
						distanceFromBottom = options.paddingBottom + "px";

					container.appendChild(element);
					elementStyle.left = options.offsetLeft + "px";

					if (options.paddingBottom) {
						elementStyle.paddingBottom = distanceFromBottom;
						divWithMargin.classList.add("ui-indexscrollbar-margin");
						divWithMargin.style.height = distanceFromBottom;
						container.appendChild(divWithMargin);
					}
				},

				/**
				 * Sets padding top for element.
				 * @method setPaddingTop
				 * @param {number} paddingTop
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				setPaddingTop: function (paddingTop) {
					var height = this.element.clientHeight,
						oldPaddingTop = this.element.style.paddingTop,
						containerHeight = this.container.clientHeight;

					if (oldPaddingTop === "") {
						oldPaddingTop = 0;
					} else {
						oldPaddingTop = parseInt(oldPaddingTop, 10);
					}

					height = height - oldPaddingTop;

					if (height > containerHeight) {
						paddingTop -= (paddingTop + height - containerHeight);
					}
					this.element.style.paddingTop = paddingTop + "px";

					this._setIndexCellInfo();	// update index cell info
				},

				/**
				 * Returns element's offsetTop of given index.
				 * @method getOffsetTopByIndex
				 * @param {number} index
				 * @return {number}
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				getOffsetTopByIndex: function (index) {
					var cellIndex = this.indexLookupTable[index].cellIndex,
						el = this.indexElements[cellIndex],
						offsetTop = el.offsetTop;

					return offsetTop;
				},

				_setIndexCellInfo: function () {
					var element = this.element,
						mergedIndices = this.indices.merged,
						containerOffsetTop = utilsDOM.getElementOffset(this.container).top,
						listItems = this.element.querySelectorAll("LI"),
						lookupTable = [];

					[].forEach.call(listItems, function (node, idx) {
						var m = mergedIndices[idx],
							i = m.start,
							len = i + m.length,
							top = containerOffsetTop + node.offsetTop,
							height = node.offsetHeight / m.length;

						for (; i < len; i++) {
							lookupTable.push({
								cellIndex: idx,
								top: top,
								range: height
							});
							top += height;
						}
					});
					this.indexLookupTable = lookupTable;
					this.indexElements = element.children;
				},

				/**
				 * Returns index for given position.
				 * @method getIndexByPosition
				 * @param {number} posY
				 * @return {number}
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				getIndexByPosition: function (posY) {
					var table = this.indexLookupTable,
						info,
						i,
						len,
						range;

					// boundary check
					if (table[0]) {
						info = table[0];
						if (posY < info.top) {
							return 0;
						}
					}
					if (table[table.length - 1]) {
						info = table[table.length - 1];
						if (posY >= info.top + info.range) {
							return table.length - 1;
						}
					}
					for (i = 0, len = table.length; i < len; i++) {
						info = table[i];
						range = posY - info.top;
						if (range >= 0 && range < info.range) {
							return i;
						}
					}
					return 0;
				},

				/**
				 * Returns value for given index.
				 * @method getValueByIndex
				 * @param {number} idx
				 * @return {number}
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				getValueByIndex: function (idx) {
					if (idx < 0) {
						idx = 0;
					}
					return this.indices.original[idx];
				},

				/**
				 * Select given index
				 * @method select
				 * @param {number} idx
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				select: function (idx) {
					var cellIndex,
						eCell;

					this.clearSelected();

					if (this.selectedIndex === idx) {
						return;
					}
					this.selectedIndex = idx;

					cellIndex = this.indexLookupTable[idx].cellIndex;
					eCell = this.indexElements[cellIndex];
					eCell.classList.add(this.options.selectedClass);
				},

				/**
				 * Clears selected class.
				 * @method clearSelected
				 * @member ns.widget.wearable.indexscrollbar.IndexBar
				 */
				clearSelected: function () {
					var el = this.element,
						selectedClass = this.options.selectedClass,
						selectedElement = el.querySelectorAll("." + selectedClass);

					[].forEach.call(selectedElement, function (node) {
						node.classList.remove(selectedClass);
					});
					this.selectedIndex = -1;
				}
			};

			ns.widget.core.indexscrollbar.IndexBar = IndexBar;

			}(window.document, ns));

/*global define, ns, document, window */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #IndexIndicator widget
 * Class creates index indicator.
 *
 * @internal
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @class ns.widget.wearable.indexscrollbar.IndexIndicator
 */
(function (document, ns) {
	"use strict";
				var utilsObject = ns.util.object,
				events = ns.event;

			/**
			 * block 'unexpected bouncing effect' on indexscroller indicator.
			 * @param {Event} event
			 */
			function blockEvent(event) {
				event.preventDefault();
				event.stopPropagation();
			}

			function IndexIndicator(element, options) {
				this.element = element;
				this.options = utilsObject.merge(options, this._options, false);
				this.value = null;

				this._init();

				return this;
			}

			IndexIndicator.prototype = {
				_options: {
					className: "ui-indexscrollbar-indicator",
					selectedClass: "ui-selected",
					alignTo: "container",
					container: null
				},
				_init: function () {
					var self = this,
						options = self.options,
						element = self.element;

					element.className = options.className;
					element.innerHTML = "<span></span>";
					events.on(element, ["touchstart", "touchmove"], blockEvent, false);


					// Add to DOM tree
					options.referenceElement.parentNode.insertBefore(element, options.referenceElement);
					self.fitToContainer();
				},

				/**
				 * Fits size to container.
				 * @method fitToContainer
				 * @param {HTMLElement} alignTo align indicator position relative to particular element
				 * @member ns.widget.wearable.indexscrollbar.IndexIndicator
				 */
				fitToContainer: function (alignTo) {
					var self = this,
						element = self.element,
						style = element.style,
						options = self.options,
						container = options.container,
						containerRect = container.getBoundingClientRect();

					alignTo = alignTo || options.alignTo;

					style.width = containerRect.width + "px";
					if (alignTo === "container") {
						style.height = containerRect.height + "px";
					} else {
						style.height = (containerRect.height + containerRect.top) + "px";
					}


					style.top = ((alignTo === "container") ? containerRect.top : 0) + "px";
					style.left = containerRect.left + "px";
				},

				/**
				 * Sets value of widget.
				 * @method setValue
				 * @param {string} value
				 * @member ns.widget.wearable.indexscrollbar.IndexIndicator
				 */
				setValue: function (value) {
					var selected = "",
						remained = "";

					this.value = value;	// remember value
					value = value.toUpperCase();

					selected = value.substr(value.length - 1);
					remained = value.substr(0, value.length - 1);

					this.element.firstChild.innerHTML = "<span>" + remained + "</span><span class=\"ui-selected\">" +
						selected + "</span>";	// Set indicator text
				},

				/**
				 * Shows widget.
				 * @method show
				 * @member ns.widget.wearable.indexscrollbar.IndexIndicator
				 */
				show: function () {
					//this.element.style.visibility="visible";
					this.element.style.display = "block";
				},

				/**
				 * Hides widget.
				 * @method hide
				 * @member ns.widget.wearable.indexscrollbar.IndexIndicator
				 */
				hide: function () {
					this.element.style.display = "none";
				},

				/**
				 * Destroys widget.
				 * @method destroy
				 * @member ns.widget.wearable.indexscrollbar.IndexIndicator
				 */
				destroy: function () {
					var element = this.element;

					while (element.firstChild) {
						element.removeChild(element.firstChild);
					}
					events.off(element, ["touchstart", "touchmove"], blockEvent, false);
					this.element = null;	// unreference element

				}
			};
			ns.widget.core.indexscrollbar.IndexIndicator = IndexIndicator;
			}(window.document, ns));

/*global define, ns, document, window */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * #Index Scrollbar
 * Shows an index scroll bar with indices, usually for the list.
 *
 * The index scroll bar widget shows on the screen a scrollbar with indices,
 * and fires a select event when the index characters are clicked.
 * The following table describes the supported index scroll bar APIs.
 *
 * ## Manual constructor
 * For manual creation of widget you can use constructor of widget from **tau** namespace:
 *
 *        @example
 *        var indexscrollbarElement = document.getElementById('indexscrollbar'),
 *            indexscrollbar = tau.widget.IndexScrollbar(IndexScrollbar, {index: "A,B,C"});
 *
 * Constructor has one require parameter **element** which are base **HTMLElement** to create widget.
 * We recommend get this element by method *document.getElementById*. Second parameter is **options**
 * and it is a object with options for widget.
 *
 * To add an IndexScrollbar widget to the application, use the following code:
 *
 *      @example
 *      <div id="foo" class="ui-indexscrollbar" data-index="A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"></div>
 *      <script>
 *          (function() {
 *              var elem = document.getElementById("foo");
 *              tau.widget.IndexScrollbar(elem);
 *              elem.addEventListener("select", function( event ) {
 *                  var index = event.detail.index;
 *                  console.log(index);
 *              });
 *          }());
 *      </script>
 *
 * The index value can be retrieved by accessing event.detail.index property.
 *
 * In the following example, the list scrolls to the position of the list item defined using
 * the li-divider class, selected by the index scroll bar:
 *
 *      @example
 *         <div id="pageIndexScrollbar" class="ui-page">
 *             <header class="ui-header">
 *                 <h2 class="ui-title">IndexScrollbar</h2>
 *             </header>
 *             <section class="ui-content">
 *                 <div style="overflow-y:scroll;">
 *                     <div id="indexscrollbar1"
 *                          class="ui-indexscrollbar"
 *                          data-index="A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z">
 *                     </div>
 *                     <ul class="ui-listview" id="list1">
 *                         <li class="li-divider">A</li>
 *                         <li>Anton</li>
 *                         <li>Arabella</li>
 *                         <li>Art</li>
 *                         <li class="li-divider">B</li>
 *                         <li>Barry</li>
 *                         <li>Bibi</li>
 *                         <li>Billy</li>
 *                         <li>Bob</li>
 *                         <li class="li-divider">D</li>
 *                         <li>Daisy</li>
 *                         <li>Derek</li>
 *                         <li>Desmond</li>
 *                     </ul>
 *                 </div>
 *             </section>
 *             <script>
 *                 (function () {
 *                     var page = document.getElementById("pageIndexScrollbar");
 *                     page.addEventListener("pagecreate", function () {
 *                         var elem = document.getElementById("indexscrollbar1"), // Index scroll bar element
 *                                 elList = document.getElementById("list1"), // List element
 *                                 elDividers = elList.getElementsByClassName("li-divider"), // List items (dividers)
 *                                 elScroller = elList.parentElement, // List's parent item (overflow-y:scroll)
 *                                 dividers = {}, // Collection of list dividers
 *                                 indices = [], // List of index
 *                                 elDivider,
 *                                 i, idx;
 *
 *                         // For all list dividers
 *                         for (i = 0; i < elDividers.length; i++) {
 *                             // Add the list divider elements to the collection
 *                             elDivider = elDividers[i];
 *                             // li element having the li-divider class
 *                             idx = elDivider.innerText;
 *                             // Get a text (index value)
 *                             dividers[idx] = elDivider;
 *                             // Remember the element
 *
 *                             // Add the index to the index list
 *                             indices.push(idx);
 *                         }
 *
 *                         // Change the data-index attribute to the indexscrollbar element
 *                         // before initializing IndexScrollbar widget
 *                         elem.setAttribute("data-index", indices.join(","));
 *
 *                         // Create index scroll bar
 *                         tau.IndexScrollbar(elem);
 *
 *                         // Bind the select callback
 *                         elem.addEventListener("select", function (ev) {
 *                             var elDivider,
 *                                     idx = ev.detail.index;
 *                             elDivider = dividers[idx];
 *                             if (elDivider) {
 *                                 // Scroll to the li-divider element
 *                                 elScroller.scrollTop = elDivider.offsetTop - elScroller.offsetTop;
 *                             }
 *                         });
 *                     });
 *                 }());
 *             </script>
 *         </div>
 *
 * The following example uses the supplementScroll argument, which shows a level 2 index scroll bar.
 * The application code must contain a level 2 index array for each level 1 index character.
 * The example shows a way to analyze list items and create a dictionary (secondIndex) for level 1
 * indices for the index scroll bar, and a dictionary (keyItem) for moving list items at runtime:
 *
 *      @example
 *         <div id="indexscrollbar2" class="ui-indexscrollbar"
 *              data-index="A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z">
 *         </div>
 *         <ul class="ui-listview" id="iBar2_list2">
 *             <li>Anton</li>
 *             <li>Arabella</li>
 *             <li>Art</li>
 *             <li>Barry</li>
 *             <li>Bibi</li>
 *             <li>Billy</li>
 *             <li>Bob</li>
 *             <li>Carry</li>
 *             <li>Cibi</li>
 *             <li>Daisy</li>
 *             <li>Derek</li>
 *             <li>Desmond</li>
 *         </ul>
 *
 *         <script>
 *             (function () {
 *                 var page = document.getElementById("pageIndexScrollbar2"),
 *                         isb,
 *                         index = [],
 *                         supIndex = {},
 *                         elIndex = {};
 *                 page.addEventListener("pageshow", function () {
 *                     var elisb = document.getElementById("indexscrollbar2"),
 *                             elList = document.getElementById("iBar2_list2"), // List element
 *                             elItems = elList.children,
 *                             elScroller = elList.parentElement, // Scroller (overflow-y:hidden)
 *                             indexData = getIndexData(
 *                                     {
 *                                         array: elItems,
 *                                         getTextValue: function (array, i) {
 *                                             return array[i].innerText;
 *                                         }
 *                                     });
 *
 *                     function getIndexData(options) {
 *                         var array = options.array,
 *                                 getTextValue = options.getTextValue,
 *                                 item,
 *                                 text,
 *                                 firstIndex = [],
 *                                 secondIndex = {},
 *                                 keyItem = {},
 *                                 c1 = null,
 *                                 c2 = null,
 *                                 i;
 *
 *                         for (i = 0; i < array.length; i++) {
 *                             item = array[i];
 *                             text = getTextValue(array, i);
 *                             if (text.length > 0) {
 *                                 if (!c1 || c1 !== text[0]) {
 *                                     // New c1
 *                                     c1 = text[0];
 *                                     firstIndex.push(c1);
 *                                     keyItem[c1] = item;
 *                                     secondIndex[c1] = [];
 *                                     c2 = text[1];
 *                                     if (c2) {
 *                                         secondIndex[c1].push(c2);
 *                                     }
 *                                     else {
 *                                         c2 = '';
 *                                     }
 *                                     keyItem[c1 + c2] = item;
 *                                 }
 *                                 else {
 *                                     // Existing c1
 *                                     if (c2 !== text[1]) {
 *                                         c2 = text[1];
 *                                         secondIndex[c1].push(c2);
 *                                         keyItem[c1 + c2] = item;
 *                                     }
 *                                 }
 *                             }
 *                         }
 *                         return {
 *                             firstIndex: firstIndex,
 *                             secondIndex: secondIndex,
 *                             keyItem: keyItem
 *                         };
 *                     }
 *
 *                     // Update the data-index attribute to the indexscrollbar element, with the index list above
 *                     elisb.setAttribute("data-index", indexData.firstIndex);
 *                     // Create IndexScrollbar
 *                     isb = new tau.IndexScrollbar(elisb, {
 *                         index: indexData.firstIndex,
 *                         supplementaryIndex: function (firstIndex) {
 *                             return indexData.secondIndex[firstIndex];
 *                         }
 *                     });
 *                     // Bind the select callback
 *                     elisb.addEventListener("select", function (ev) {
 *                         var el,
 *                             index = ev.detail.index;
 *                         el = indexData.keyItem[index];
 *                         if (el) {
 *                             // Scroll to the li-divider element
 *                             elScroller.scrollTop = el.offsetTop - elScroller.offsetTop;
 *                         }
 *                     });
 *                 });
 *                 page.addEventListener("pagehide", function () {
 *                     console.log('isb2:destroy');
 *                     isb.destroy();
 *                     index.length = 0;
 *                     supIndex = {};
 *                     elIndex = {};
 *                 });
 *             }());
 *         </script>
 *
 * ##Options for widget
 *
 * Options for widget can be defined as _data-..._ attributes or give as parameter in constructor.
 *
 * You can change option for widget using method **option**.
 *
 * ##Methods
 *
 * To call method on widget you can use tau API:
 *
 * First API is from tau namespace:
 *
 *        @example
 *        var indexscrollbarElement = document.getElementById('indexscrollbar'),
 *            indexscrollbar = tau.widget.IndexScrollbar(indexscrollbarElement);
 *
 *        indexscrollbar.methodName(methodArgument1, methodArgument2, ...);
 *
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Jadwiga Sosnowska <j.sosnowska@samsung.com>
 * @author Tomasz Lukawski <t.lukawski@samsung.com>
 * @class ns.widget.core.IndexScrollbar
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var IndexScrollbar = function () {
					var self = this;// Support calling without 'new' keyword

					self.indicator = null;
					self.indexBar1 = null;	// First IndexBar. Always shown.
					self.indexBar2 = null;	// 2-depth IndexBar. shown if needed.

					self._ui = {};

					self.index = null;
					self.touchAreaOffsetLeft = 0;
					self.indexElements = null;
					self.selectEventTriggerTimeoutId = null;
					self.ulMarginTop = 0;

					self.eventHandlers = {};

				},
				BaseWidget = ns.widget.BaseWidget,
				/**
				 * Alias for class {@link ns.event}
				 * @property {Object} events
				 * @member ns.widget.core.IndexScrollbar
				 * @private
				 * @static
				 */
				events = ns.event,
				selectors = ns.util.selectors,
				/**
				 * Alias for class {@link ns.util.object}
				 * @property {Object} utilsObject
				 * @member ns.widget.core.IndexScrollbar
				 * @private
				 * @static
				 */
				utilsObject = ns.util.object,
				/**
				 * Alias for class ns.util.DOM
				 * @property {ns.util.DOM} doms
				 * @member ns.widget.wearable.IndexScrollbar
				 * @private
				 * @static
				 */
				doms = ns.util.DOM,

				IndexBar = ns.widget.core.indexscrollbar.IndexBar,
				IndexIndicator = ns.widget.core.indexscrollbar.IndexIndicator,
				Page = ns.widget.core.Page,
				pageSelector = ns.engine.getWidgetDefinition("Page").selector,
				EventType = {
					/**
					 * Event triggered after select index by user
					 * @event select
					 * @member ns.widget.core.IndexScrollbar
					 */
					SELECT: "select"
				},

				POINTER_START = "vmousedown",
				POINTER_MOVE = "vmousemove",
				POINTER_END = "vmouseup",

				pointerIsPressed = false,
				prototype = new BaseWidget();

			IndexScrollbar.prototype = prototype;

			utilsObject.merge(prototype, {
				widgetName: "IndexScrollbar",
				widgetClass: "ui-indexscrollbar",

				_configure: function () {
					/**
					 * All possible widget options
					 * @property {Object} options
					 * @property {string} [options.moreChar="*"] more character
					 * @property {string} [options.selectedClass="ui-state-selected"] disabled class name
					 * @property {string} [options.delimiter=","] delimiter in index
					 * @property {string|Array} [options.index=["A","B","C","D","E","F","G","H","I",
					 * "J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1"]]
					 * String with list of letters separate be delimiter or array of letters
					 * @property {boolean} [options.maxIndexLen=0]
					 * @property {boolean} [options.indexHeight=41]
					 * @property {boolean} [options.keepSelectEventDelay=50]
					 * @property {?boolean} [options.container=null]
					 * @property {?boolean} [options.supplementaryIndex=null]
					 * @property {number} [options.supplementaryIndexMargin=1]
					 * @member ns.widget.core.IndexScrollbar
					 */
					this.options = {
						moreChar: "*",
						indexScrollbarClass: "ui-indexscrollbar",
						selectedClass: "ui-state-selected",
						indicatorClass: "ui-indexscrollbar-indicator",
						delimiter: ",",
						index: [
							"A", "B", "C", "D", "E", "F", "G", "H",
							"I", "J", "K", "L", "M", "N", "O", "P", "Q",
							"R", "S", "T", "U", "V", "W", "X", "Y", "Z", "1"
						],
						maxIndexLen: 0,
						indexHeight: 41,
						keepSelectEventDelay: 50,
						container: null,
						supplementaryIndex: null,
						supplementaryIndexMargin: 1,
						moreCharLineHeight: 9,
						verticalCenter: true,
						indicatorAlignTo: "container"
					};
				},

				/**
				 * This method builds widget.
				 * @method _build
				 * @protected
				 * @param {HTMLElement} element
				 * @return {HTMLElement}
				 * @member ns.widget.core.IndexScrollbar
				 */
				_build: function (element) {
					return element;
				},

				/**
				 * This method inits widget.
				 * @method _init
				 * @protected
				 * @param {HTMLElement} element
				 * @return {HTMLElement}
				 * @member ns.widget.core.IndexScrollbar
				 */
				_init: function (element) {
					var self = this,
						options = self.options;

					element.classList.add(options.indexScrollbarClass);

					self._ui.page = selectors.getClosestBySelector(element, pageSelector);
					self._setIndex(element, options.index);
					self._setMaxIndexLen(element, options.maxIndexLen);
					self._setInitialLayout(); // This is needed for creating sub objects
					self._createSubObjects();

					self._updateLayout();

					// Mark as extended
					self._extended(true);
					return element;
				},

				/**
				 * This method refreshes widget.
				 * @method _refresh
				 * @protected
				 * @return {HTMLElement}
				 * @member ns.widget.core.IndexScrollbar
				 */
				_refresh: function () {
					var self = this;

					if (self._isExtended()) {
						self._unbindEvent();
						self.indicator.hide();
						self._extended(false);
					}

					self._updateLayout();
					self.indexBar1.options.index = self.options.index;
					self.indexBar1.refresh();
					self.indicator.fitToContainer();
					self._bindEvents();
					self._extended(true);
				},

				/**
				 * This method destroys widget.
				 * @method _destroy
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_destroy: function () {
					var self = this;

					if (self.isBound()) {
						self._unbindEvent();
						self._extended(false);
						self._destroySubObjects();
						self.indicator = null;
						self.index = null;
						self.eventHandlers = {};
					}
				},

				/**
				 * This method creates indexBar1 and indicator in the indexScrollbar
				 * @method _createSubObjects
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_createSubObjects: function () {
					var self = this,
						options = self.options,
						element = self.element;
					// indexBar1

					self.indexBar1 = new IndexBar(document.createElement("UL"), {
						container: element,
						offsetLeft: 0,
						index: options.index,
						verticalCenter: options.verticalCenter,
						indexHeight: options.indexHeight,
						maxIndexLen: options.maxIndexLen,
						paddingBottom: options.paddingBottom,
						moreCharLineHeight: options.moreCharLineHeight
					});

					// indexBar2
					if (typeof options.supplementaryIndex === "function") {
						self.indexBar2 = new IndexBar(document.createElement("UL"), {
							container: element,
							offsetLeft: -element.clientWidth - options.supplementaryIndexMargin,
							index: [],	// empty index
							indexHeight: options.indexHeight,
							ulClass: "ui-indexscrollbar-supplementary"
						});
						self.indexBar2.hide();
					}

					// indicator
					self.indicator = new IndexIndicator(document.createElement("DIV"), {
						container: self._getContainer(),
						referenceElement: self.element,
						className: options.indicatorClass,
						alignTo: options.indicatorAlignTo
					});

				},

				/**
				 * This method destroys sub-elements: index bars and indicator.
				 * @method _destroySubObjects
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_destroySubObjects: function () {
					var subObjs = {
							iBar1: this.indexBar1,
							iBar2: this.indexBar2,
							indicator: this.indicator
						},
						subObj,
						el,
						i;

					for (i in subObjs) {
						if (subObjs.hasOwnProperty(i)) {
							subObj = subObjs[i];
							if (subObj) {
								el = subObj.element;
								subObj.destroy();
								el.parentNode.removeChild(el);
							}
						}
					}
				},

				/**
				 * This method sets initial layout.
				 * @method _setInitialLayout
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_setInitialLayout: function () {
					var indexScrollbar = this.element,
						container = this._getContainer(),
						containerPosition = window.getComputedStyle(container).position,
						indexScrollbarStyle = indexScrollbar.style;

					// Set the indexScrollbar's position, if needed
					if (containerPosition !== "absolute" && containerPosition !== "relative") {
						indexScrollbarStyle.top = container.offsetTop + "px";
						indexScrollbarStyle.height = container.offsetHeight + "px";
					}
				},

				/**
				 * This method calculates maximum index length.
				 * @method _setMaxIndexLen
				 * @protected
				 * @param {HTMLElement} element
				 * @param {number} value
				 * @member ns.widget.core.IndexScrollbar
				 */
				_setMaxIndexLen: function (element, value) {
					var self = this,
						options = self.options,
						container = self._getContainer(),
						containerHeight = container.offsetHeight;

					if (value <= 0) {
						value = Math.floor(containerHeight / options.indexHeight);
					}
					if (value > 0 && value % 2 === 0) {
						value -= 1;	// Ensure odd number
					}
					options.maxIndexLen = value;
				},

				/**
				 * This method updates layout.
				 * @method _updateLayout
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_updateLayout: function () {
					var self = this;

					self._setInitialLayout();
					self._draw();

					self.touchAreaOffsetLeft = self.element.offsetLeft - 10;
				},

				/**
				 * This method draws additional sub-elements
				 * @method _draw
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_draw: function () {
					this.indexBar1.show();
					return this;
				},

				/**
				 * This method removes indicator.
				 * @method _removeIndicator
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_removeIndicator: function () {
					var indicator = this.indicator,
						parentElem = indicator.element.parentNode;

					parentElem.removeChild(indicator.element);
					indicator.destroy();
					this.indicator = null;
				},

				/**
				 * This method returns the receiver of event by position.
				 * @method _getEventReceiverByPosition
				 * @param {number} posX The position relative to the left edge of the document.
				 * @return {?ns.widget.core.indexscrollbar.IndexBar} Receiver of event
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_getEventReceiverByPosition: function (posX) {
					var windowWidth = window.innerWidth,
						elementWidth = this.element.clientWidth,
						receiver;

					if (this.options.supplementaryIndex) {
						if (windowWidth - elementWidth <= posX && posX <= windowWidth) {
							receiver = this.indexBar1;
						} else {
							receiver = this.indexBar2;
						}
					} else {
						receiver = this.indexBar1;
					}
					return receiver;
				},

				/**
				 * This method updates indicator.
				 * It sets new value of indicator and triggers event "select".
				 * @method _updateIndicatorAndTriggerEvent
				 * @param {number} val The value of indicator
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_updateIndicatorAndTriggerEvent: function (val) {
					this.indicator.setValue(val);
					this.indicator.show();
					if (this.selectEventTriggerTimeoutId) {
						window.clearTimeout(this.selectEventTriggerTimeoutId);
					}
					this.selectEventTriggerTimeoutId = window.setTimeout(function () {
						this.trigger(EventType.SELECT, {index: val});
						this.selectEventTriggerTimeoutId = null;
					}.bind(this), this.options.keepSelectEventDelay);
				},

				/**
				 * This method is executed on event "touchstart"
				 * @method _onTouchStartHandler
				 * @param {Event} event Event
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_onTouchStartHandler: function (event) {
					var touches = event.touches || event._originalEvent && event._originalEvent.touches,
						pos = null,
						// At touchstart, only indexBar1 is shown.
						iBar1 = null,
						idx = 0,
						val = 0;

					pointerIsPressed = true;

					if (touches && (touches.length > 1)) {
						event.preventDefault();
						event.stopPropagation();
						return;
					}
					pos = this._getPositionFromEvent(event);
					// At touchstart, only indexBar1 is shown.
					iBar1 = this.indexBar1;
					idx = iBar1.getIndexByPosition(pos.y);
					val = iBar1.getValueByIndex(idx);

					iBar1.select(idx);	// highlight selected value

					document.addEventListener(POINTER_MOVE, this.eventHandlers.touchMove);
					document.addEventListener(POINTER_END, this.eventHandlers.touchEnd);
					document.addEventListener("touchcancel", this.eventHandlers.touchEnd);

					this._updateIndicatorAndTriggerEvent(val);
				},

				/**
				 * This method is executed on event "touchmove"
				 * @method _onTouchMoveHandler
				 * @param {Event} event Event
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_onTouchMoveHandler: function (event) {
					var touches = event._originalEvent && event._originalEvent.touches,
						pos = null,
						iBar1 = null,
						iBar2 = null,
						idx,
						iBar,
						val;

					if (touches && (touches.length > 1) || !pointerIsPressed) {
						events.preventDefault(event);
						events.stopPropagation(event);
						return;
					}

					pos = this._getPositionFromEvent(event);
					iBar1 = this.indexBar1;
					iBar2 = this.indexBar2;

					// Check event receiver: iBar1 or iBar2
					iBar = this._getEventReceiverByPosition(pos.x);
					if (iBar === iBar2) {
						iBar2.options.index = this.options.supplementaryIndex(iBar1.getValueByIndex(iBar1.selectedIndex));
						iBar2.refresh();
					}

					// get index and value from iBar1 or iBar2
					idx = iBar.getIndexByPosition(pos.y);
					val = iBar.getValueByIndex(idx);
					if (iBar === iBar2) {
						// Update val to make a concatenated string for indexIndicator
						val = iBar1.getValueByIndex(iBar1.selectedIndex) + val;
					} else if (iBar2 && !iBar2.isShown()) {
						// iBar1 is selected.
						// Set iBar2's paddingTop, only when the iBar2 isn't shown
						iBar2.setPaddingTop(iBar1.getOffsetTopByIndex(iBar1.selectedIndex));
					}

					// update iBars
					iBar.select(idx);	// highlight selected value
					iBar.show();
					if (iBar1 === iBar && iBar2) {
						iBar2.hide();
					}

					// update indicator
					this._updateIndicatorAndTriggerEvent(val);

					events.preventDefault(event);
					events.stopPropagation(event);
				},

				/**
				 * This method is executed on event "touchend"
				 * @method _onTouchEndHandler
				 * @param {Event} event Event
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_onTouchEndHandler: function (event) {
					var self = this,
						touches = event._originalEvent && event._originalEvent.touches;

					if (touches && (touches.length === 0) || !touches) {
						pointerIsPressed = false;
					}
					self.indicator.hide();
					self.indexBar1.clearSelected();
					if (self.indexBar2) {
						self.indexBar2.clearSelected();
						self.indexBar2.hide();
					}

					document.removeEventListener(POINTER_MOVE, self.eventHandlers.touchMove);
					document.removeEventListener(POINTER_END, self.eventHandlers.touchEnd);
					document.removeEventListener("touchcancel", self.eventHandlers.touchEnd);
				},

				_bindOnPageShow: function () {
					var self = this;

					self.eventHandlers.onPageShow = self.refresh.bind(self);
					if (self._ui.page) {
						self._ui.page.addEventListener(Page.events.BEFORE_SHOW, self.eventHandlers.onPageShow, false);
					}
				},

				_unbindOnPageShow: function () {
					var self = this;

					if (self.eventHandlers.onPageShow && self._ui.page) {
						self._ui.page.removeEventListener(Page.events.BEFORE_SHOW, self.eventHandlers.onPageShow, false);
					}
				},

				/**
				 * This method binds events to widget.
				 * @method _bindEvents
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_bindEvents: function () {
					var self = this;

					self._bindResizeEvent();
					self._bindEventToTriggerSelectEvent();
					self._bindOnPageShow();
				},

				/**
				 * This method unbinds events to widget.
				 * @method _unbindEvent
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_unbindEvent: function () {
					var self = this;

					self._unbindResizeEvent();
					self._unbindEventToTriggerSelectEvent();
					self._unbindOnPageShow();
				},

				/**
				 * This method binds event "resize".
				 * @method _bindResizeEvent
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_bindResizeEvent: function () {
					this.eventHandlers.onresize = function (/* ev */) {
						this.refresh();
					}.bind(this);

					window.addEventListener("resize", this.eventHandlers.onresize);
				},

				/**
				 * This method unbinds event "resize".
				 * @method _bindResizeEvent
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_unbindResizeEvent: function () {
					if (this.eventHandlers.onresize) {
						window.removeEventListener("resize", this.eventHandlers.onresize);
					}
				},

				/**
				 * This method binds touch events.
				 * @method _bindEventToTriggerSelectEvent
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_bindEventToTriggerSelectEvent: function () {
					var self = this;

					self.eventHandlers.touchStart = self._onTouchStartHandler.bind(self);
					self.eventHandlers.touchEnd = self._onTouchEndHandler.bind(self);
					self.eventHandlers.touchMove = self._onTouchMoveHandler.bind(self);

					self.element.addEventListener(POINTER_START, self.eventHandlers.touchStart);
				},

				/**
				 * This method unbinds touch events.
				 * @method _unbindEventToTriggerSelectEvent
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_unbindEventToTriggerSelectEvent: function () {
					var self = this;

					self.element.removeEventListener(POINTER_START, self.eventHandlers.touchStart);
				},

				/**
				 * This method sets or gets data from widget.
				 * @method _data
				 * @param {string|Object} key
				 * @param {*} val
				 * @return {*} Return value of data or widget's object
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_data: function (key, val) {
					var el = this.element,
						d = el.__data,
						idx;

					if (!d) {
						d = el.__data = {};
					}
					if (typeof key === "object") {
						// Support data collection
						for (idx in key) {
							if (key.hasOwnProperty(idx)) {
								this._data(idx, key[idx]);
							}
						}
						return this;
					} else {
						if ("undefined" === typeof val) {	// Getter
							return d[key];
						} else {	// Setter
							d[key] = val;
							return this;
						}
					}
				},

				/**
				 * This method checks if element is valid element of widget IndexScrollbar.
				 * @method _isValidElement
				 * @param {HTMLElement} el
				 * @return {boolean} True, if element is valid.
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_isValidElement: function (el) {
					return el.classList.contains(this.widgetClass);
				},

				/**
				 * This method checks if widget is extended.
				 * @method _isExtended
				 * @return {boolean} True, if element is extended.
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_isExtended: function () {
					return !!this._data("extended");
				},

				/**
				 * This method sets value of "extended" to widget.
				 * @method _extended
				 * @param {boolean} flag Value for extended
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_extended: function (flag) {
					this._data("extended", flag);
					return this;
				},

				/**
				 * This method gets indices prepared from parameter
				 * or index of widget.
				 * @method _setIndex
				 * @param {HTMLElement} element element
				 * @param {string} value Indices to prepared
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_setIndex: function (element, value) {
					var options = this.options;

					if (typeof value === "string") {
						value = value.split(options.delimiter);	// delimiter
					}
					options.index = value;
				},

				/**
				 * This method gets offset of element.
				 * @method _getOffset
				 * @param {HTMLElement} el Element
				 * @return {Object} Offset with "top" and "left" properties
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_getOffset: function (el) {
					var left = 0,
						top = 0;

					do {
						top += el.offsetTop;
						left += el.offsetLeft;
						el = el.offsetParent;
					} while (el);

					return {
						top: top,
						left: left
					};
				},

				/**
				 * This method returns container of widget.
				 * @method _getContainer
				 * @return {HTMLElement} Container
				 * @protected
				 * @member ns.widget.core.IndexScrollbar
				 */
				_getContainer: function () {
					var container = this.options.container,
						element = this.element,
						parentElement = element.parentNode,
						overflow;

					if (!container) {
						while (parentElement && parentElement !== document.body) {
							overflow = doms.getCSSProperty(parentElement, "overflow-y");
							if (overflow === "scroll" || (overflow === "auto" && parentElement.scrollHeight > parentElement.clientHeight)) {
								return parentElement;
							}
							parentElement = parentElement.parentNode;
						}
						container = element.parentNode;
					}

					return container || element.parentNode;
				},

				/**
				 * Returns position of event.
				 * @method _getPositionFromEvent
				 * @return {Object} Position of event with properties "x" and "y"
				 * @protected
				 * @param {Event} ev
				 * @member ns.widget.core.IndexScrollbar
				 */
				_getPositionFromEvent: function (ev) {
					return ev.type.search(/^touch/) !== -1 ?
						{x: ev.touches[0].clientX, y: ev.touches[0].clientY} :
						{x: ev.clientX, y: ev.clientY};
				},

				/**
				 * Adds event listener to element of widget.
				 * @method addEventListener
				 * @param {string} type Name of event
				 * @param {Function} listener Function to be executed
				 * @member ns.widget.core.IndexScrollbar
				 */
				addEventListener: function (type, listener) {
					this.element.addEventListener(type, listener);
				},

				/**
				 * Removes event listener from element of widget.
				 * @method removeEventListener
				 * @param {string} type Name of event
				 * @param {Function} listener Function to be removed
				 * @member ns.widget.core.IndexScrollbar
				 */
				removeEventListener: function (type, listener) {
					this.element.removeEventListener(type, listener);
				}

			});

			// definition
			ns.widget.core.IndexScrollbar = IndexScrollbar;
			}(window.document, ns));

/*global define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true, plusplus: true */
/**
 * # Index Scrollbar
 * Index scrollbar component shows a shortcut list that is bound to its parent scroll bar and list view.
 *
 * If you move the mouse on the shortcut column then a pop-up with the text currently
 * under the cursor is also displayed.
 *
 *
 * ## Default selectors
 *
 * In default all elements with class _ui-indexscrollbar_ are changed to Tizen Web UI Index Scrollbar
 *
 *		@example
 *		<div data-role="page" id="main">
 *		    <div data-role="indexscrollbar" id="indexscrollbar"></div>
 *			<div data-role="content">
 *				<ul data-role="listview">
 *					<li data-role="list-divider">A</li>
 *					<li>Anton</li>
 *					<li>Arabella</li>
 *					<li data-role="list-divider">B</li>
 *					<li>Barry</li>
 *					<li>Billy</li>
 *				</ul>
 *			</div>
 *		</div>
 *
 * #### Create Index Scrollbar widget using tau method:
 *
 *		@example
 *		<div data-role="page" id="main">
 *		    <div data-role="indexscrollbar" id="indexscrollbar"></div>
 *			<div data-role="content">
 *				<ul id="list">
 *					<li data-role="list-divider">A</li>
 *					<li>Anton</li>
 *					<li>Arabella</li>
 *					<li data-role="list-divider">B</li>
 *					<li>Barry</li>
 *					<li>Billy</li>
 *				</ul>
 *			</div>
 *		</div>
 *		<script>
 *			var isb = tau.widget.IndexScrollbar(document.getElementById("indexscrollbar"));
 *		</script>
 *
 * #### Create Index Scrollbar widget using jQueryMobile notation:
 *
 *		@example
 *		<div data-role="page" id="main">
 *		    <div data-role="indexscrollbar" id="indexscrollbar"></div>
 *			<div data-role="content">
 *				<ul id="list">
 *					<li data-role="list-divider">A</li>
 *					<li>Anton</li>
 *					<li>Arabella</li>
 *					<li data-role="list-divider">B</li>
 *					<li>Barry</li>
 *					<li>Billy</li>
 *				</ul>
 *			</div>
 *		</div>
 *		<script>
 *			var isb = $("#indexscrollbar").IndexScrollbar();
 *		</script>
 *
 * ## Options
 *
 * ### Index Scrollbar
 * _data-fastscroll_ option set to true, creates a fast scroll using the HTML unordered list (&lt;ul&gt;) element.
 *
 *		@example
 *		<div data-role="page" id="main">
 *		    <div data-role="indexscrollbar" id="indexscrollbar"></div>
 *			<div data-role="content">
 *				<ul id="contacts" data-role="listview">
 *					<li data-role="list-divider">A</li>
 *					<li>Anton</li>
 *					<li>Arabella</li>
 *					<li data-role="list-divider">B</li>
 *					<li>Barry</li>
 *					<li>Billy</li>
 *				</ul>
 *			</div>
 *		</div>
 *
 * ## Methods
 *
 * To call method on widget you can use tau API:
 *
 *		@example
 *		<div data-role="page" id="main">
 *		    <div data-role="indexscrollbar" id="indexscrollbar"></div>
 *			<div data-role="content">
 *				<ul id="contacts">
 *					<li data-role="list-divider">A</li>
 *					<li>Anton</li>
 *					<li>Arabella</li>
 *					<li data-role="list-divider">B</li>
 *					<li>Barry</li>
 *					<li>Billy</li>
 *				</ul>
 *			</div>
 *		</div>
 *		<script>
 *			var element = document.getElementById("contacts"),
 *				contacts = tau.widget.IndexScrollbar(element);
 *
 *			contacts.methodName(methodArgument1, methodArgument2, ...);
 *
 *			// or JQueryMobile notation:
 *			$(element).contacts("methodName", methodArgument1, methodArgument2, ...);
 *		</script>
 *
 * @class ns.widget.mobile.IndexScrollbar
 * @extends ns.widget.core.IndexScrollbar
 * @since 2.0
 */
(function () {
	"use strict";
	
			var engine = ns.engine,
				selectors = ns.util.selectors,
				Page = ns.widget.core.Page,
				Scrollview = ns.widget.core.Scrollview,
				CoreIndexScrollbar = ns.widget.core.IndexScrollbar,
				CoreISBPrototype = CoreIndexScrollbar.prototype,
				prototype = new CoreIndexScrollbar(),
				IndexScrollbar = function () {
					var self = this;

					self._ui = {};
					CoreIndexScrollbar.call(self);
				},
				classes = {
					PAGE: Page.classes.uiPage,
					CONTENT: Page.classes.uiContent,
					SCROLLVIEW_CLIP: Scrollview.classes.clip,
					FLOATING_BUTTON_CONTAINER: "ui-floatingactions",
					GROUP_INDEX: "ui-group-index"
				},
				DEFAULT = {
					INDEX_HEIGHT: 20,
					MORE_CHAR_LINEHEIGHT: 4
				};

			IndexScrollbar.classes = classes;

			function getIndices(elements) {
				var indices = [],
					i,
					len;

				len = elements.length;
				for (i = 0; i < len; i++) {
					indices.push(elements[i].textContent);
				}
				return indices;
			}
			/**
			 * Configure IndexScrollbar component
			 * @method _configure
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.IndexScrollbar
			 */
			prototype._configure = function (element) {
				var self = this,
					page = selectors.getClosestByClass(element, classes.PAGE),
					clip = page.querySelector("." + classes.CONTENT),
					indices = getIndices(page.getElementsByClassName(classes.GROUP_INDEX));

				CoreISBPrototype._configure.call(self);
				if (!self.options.container) {
					self.options.container = clip || element.parentNode;
				}
				if (indices.length) {
					self.options.index = indices;
				}
				self.options.indexHeight = DEFAULT.INDEX_HEIGHT;
				self.options.moreCharLineHeight = DEFAULT.MORE_CHAR_LINEHEIGHT;
				self.options.verticalCenter = false;
			};

			/**
			 * Init IndexScrollbar component
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.IndexScrollbar
			 */
			prototype._init = function (element) {
				var self = this;

				CoreISBPrototype._init.call(self, element);
				self._fitHeight();
			};

			/**
			 * Set IndexScrollbar layout
			 * @method _setInitialLayout
			 * @protected
			 * @member ns.widget.mobile.IndexScrollbar
			 */
			prototype._setInitialLayout = function () {
				var self = this,
					indexScrollbar = self.element,
					options = self.options,
					container = options.container,
					indexScrollbarStyle = indexScrollbar.style,
					containFloating = container.parentElement.querySelector("." + classes.FLOATING_BUTTON_CONTAINER);

				//if we need to shorten the height of the index, for example when
				//floatingButton is presented
				if (containFloating) {
					options.paddingBottom = container.offsetHeight -
							containFloating.offsetTop + container.offsetTop;
				}

				indexScrollbarStyle.height = container.offsetHeight + "px";
				indexScrollbarStyle.top = container.offsetTop + "px";
			};

			/**
			 * Fit IndexScrollbar height
			 * @method _fitHeight
			 * @protected
			 * @member ns.widget.mobile.IndexScrollbar
			 */
			prototype._fitHeight = function () {
				var self = this,
					element = self.element,
					wrapper = element.getElementsByTagName("ul")[0],
					lastChild = wrapper.lastChild,
					space;

				space = element.offsetHeight - wrapper.offsetHeight;
				if (lastChild) {
					lastChild.style.height = lastChild.offsetHeight + space + "px";
				}
			};

			// definition
			IndexScrollbar.prototype = prototype;
			ns.widget.mobile.IndexScrollbar = IndexScrollbar;

			engine.defineWidget(
				"IndexScrollbar",
				"[data-role='indexscrollbar'], .ui-indexscrollbar",
				[],
				IndexScrollbar,
				"mobile"
			);

			}());

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, define, ns */
/**
 * #Button
 * Button component changes the default browser buttons to special buttons with additional features, such as icons, corners, and shadows.
 *
 * @example
 *    <button>Button element</button>
 *
 * @since 2.0
 * @class ns.widget.mobile.Button
 * @extends ns.widget.core.Button
 */
(function (window, document, ns) {
	"use strict";
				var Button = ns.widget.core.Button;

			ns.widget.mobile.Button = Button;

			ns.engine.defineWidget(
				"Button",
				"button, [data-role='button'], .ui-btn, input[type='button']",
				[],
				Button,
				"mobile",
				true
			);
			}(window, window.document, ns));

/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * # Floating Actions
 * Floating actions component creates a floating button at the bottom of the screen.
 *
 * ##Default Selectors
 * By default, all elements with the class="ui-floatingactions" or data-role="floatingactions" attribute are displayed as floating actions components.
 *
 * ##Manual constructor
 *      @example
 *      <div class="ui-floatingactions" id="floating">
 *          <button class="ui-floatingactions-item" data-icon="floating-add"/>
 *          <button class="ui-floatingactions-item" data-icon="floating-search"/>
 *      </div>
 *      <script>
 *          var elFloatingActions = document.getElementById("floating"),
 *                floatingActions = tau.widget.FloatingActions(elFloatingActions);
 *      </script>
 *
 * @since 2.4
 * @class ns.widget.mobile.FloatingActions
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
	
			var BaseWidget = ns.widget.BaseWidget,
				PageClasses = ns.widget.core.Page.classes,
				engine = ns.engine,
				utilsEvents = ns.event,
				selectorUtils = ns.util.selectors,
				prototype = new BaseWidget(),
				MATRIX_REGEXP = /matrix\((.*), (.*), (.*), (.*), (.*), (.*)\)/,
				SNAP_WIDTH = 19,
				FloatingActions = function () {
					this.element = null;
					this.options = {};
					this._style = null;
					this._startX = 0;
					this._currentX = 0;
					this._hasSingle = true;
					this._padding = {};
					this._position = {};
					this._scope = {};
				},
				WIDGET_CLASS = "ui-floatingactions",
				classes = {
					WIDGET: WIDGET_CLASS,
					TRANSITIONS: WIDGET_CLASS + "-transitions",
					EXPAND_TO_LEFT: WIDGET_CLASS + "-expand-to-left",
					EXPAND_TO_RIGHT: WIDGET_CLASS + "-expand-to-right",
					PAGE_WITH_FLOATING_ACTIONS: "ui-page-floatingactions"
				};


			/**
			* Configure component
			* @method _configure
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._configure = function () {
				/**
				 * @property {Object} options Object with default options
				 * @property {number} [options.duration=300] animation duration for color and opacity (unit of time : millisecond)
				 * @property {string} [options.position='right-min'] widget position [right-min | right-1st-button | right-2nd-button | center | left-1st-button | left-2nd-button | left-min]
				 * @member ns.widget.mobile.FloatingActions
				 */
				this.options = {
					duration: 300,
					position: "right-1st-icon"
				};
			};

			/**
			* Init component
			* @method _init
			* @param {HTMLElement} element
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._init = function (element) {
				var self = this;

				self._style = element.style;
				self._hasSingle = element.children.length <= 1;
				self._buildInsideButtons();
				self._positionCalculation();
				self._setScope();
				self._updatePosition();
				self._toggleParentClasses();
				return element;
			};

			/**
			* Bind events
			* @method _bindEvents
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._bindEvents = function () {
				var self = this,
					element = self.element;

				utilsEvents.enableGesture(
					element,

					new utilsEvents.gesture.Drag({
						blockVertical: true
					})
				);

				utilsEvents.on(element, "drag dragstart dragend dragcancel", self);
			};

			/**
			* Unbind events
			* @method _unbindEvents
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._unbindEvents = function () {
				utilsEvents.disableGesture(this.element);
				utilsEvents.off(this.element, "drag dragstart dragend dragcancel", this);
			};

			/**
			* Refresh component
			* @method _refresh
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._refresh = function () {
				var self = this,
					element = self.element;

				self._hasSingle = element.children.length <= 1;

				self._positionCalculation();
				self._setScope();
				self._updatePosition();
			};

			/**
			* Destroy component
			* @method _destroy
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._destroy = function () {
				var self = this;

				if (self.isBound()) {
					self._unbindEvents();
					self._style = null;
					self._position = null;
					self._scope = null;
					self._padding = null;
					self._toggleParentClasses(true);
				}
			};

			prototype._buildInsideButtons = function () {
				var i = 0,
					self = this,
					element = self.element,
					elementChildren = element.children,
					length = elementChildren.length;

				for (; i < length; i++) {
					ns.widget.Button(elementChildren[i]);
				}
			};

			/**
			* Set position for move effect
			* @method _positionCalculation
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._positionCalculation = function () {
				var self = this,
					element = self.element,
					elementStyle = window.getComputedStyle(element),
					position = self._position,
					padding = self._padding,
					paddingLeft,
					paddingRight,
					elementWidth = element.offsetWidth;

				paddingLeft = parseInt(elementStyle.paddingLeft, 10);
				paddingLeft = parseInt(elementStyle.paddingLeft, 10);
				paddingRight = parseInt(elementStyle.paddingRight, 10);

				position.min = -window.innerWidth + paddingLeft;
				position.max = elementWidth - paddingRight;
				position.center = (position.max + position.min) / 2;
				position.left = position.min + elementWidth - (paddingLeft + paddingRight);
				position.leftOneButton = position.min + (position.left - position.min) / 2;
				position.right = position.max - elementWidth + (paddingRight + paddingLeft);
				position.rightOneButton = position.right + (position.max - position.right) / 2;

				padding.left = paddingLeft;
				padding.right = paddingRight;
				padding.ratioInShow = SNAP_WIDTH / (position.center - position.left);
				padding.ratioInHide = SNAP_WIDTH / (position.left - position.min);
			};

			/**
			* Set scope for move effect
			* @method _setScope
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._setScope = function () {
				var self = this,
					position = self._position,
					scope = self._scope,
					padding = self._padding,
					hasSingle = self._hasSingle;

				scope.min = position.min + padding.left / 2;
				scope.leftOneButton = !hasSingle ? position.min + (position.left - position.min) * 3 / 4 : null;
				scope.left = position.left + (position.center - position.left) / 2;
				scope.center = position.center + (position.right - position.center) / 2;
				scope.right = position.right + padding.right / 2;
				scope.rightOneButton = !hasSingle ? position.right + (position.max - position.right) * 3 / 4 : null;
				scope.max = position.max;
			};

			/**
			 * Dragstart event handler
			 * @method _start
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._start = function (event) {
				var self = this,
					element = self.element;

				self._startX = event.detail.pointer.clientX;
				// get current x value of translated3d
				self._currentX =
					parseInt(window.getComputedStyle(element).webkitTransform.match(MATRIX_REGEXP)[5], 10);
				element.classList.remove(classes.TRANSITIONS);
				self._clearExpandWidget();
			};

			prototype._clearExpandWidget = function () {
				var classList = this.element.classList;

				classList.remove(classes.EXPAND_TO_LEFT);
				classList.remove(classes.EXPAND_TO_RIGHT);
			};

			prototype._expandWidget = function (name) {
				var self = this,
					classList = self.element.classList;

				switch (name) {
					case "left-min" :
					case "left-1st-icon" :
					case "left-2nd-icon" :
						classList.add(classes.EXPAND_TO_LEFT);
						classList.remove(classes.EXPAND_TO_RIGHT);
						break;
					case "center" :
						self._clearExpandWidget();
						break;
					case "right-min" :
					case "right-1st-icon" :
					case "right-2nd-icon" :
						classList.remove(classes.EXPAND_TO_LEFT);
						classList.add(classes.EXPAND_TO_RIGHT);
						break;
				}
			};

			/**
			* Drag event handler
			* @method _move
			 * @param {Event} event
			* @protected
			* @member ns.widget.mobile.FloatingActions
			*/
			prototype._move = function (event) {
				var self = this,
					style = self._style,
					moveX = event.detail.estimatedX - self._startX + self._currentX,
					position = self._position,
					transform;

				if (moveX >= position.min && moveX <= position.max) {
					// for component position
					transform = "translate3d(" + moveX + "px, 0, 0)";
					style.webkitTransform = transform;
					style.transform = transform;
				}
			};

			/**
			 * Set widget position by position name
			 * @method _setPosition
			 * @param {HTMLElement} element widget html element
			 * @param {string} name name of preset position
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._setPosition = function (element, name) {
				var self = this,
					hasSingle = self._hasSingle;

				if (hasSingle && name === "left-2nd-icon") {
					name = "left-1st-icon";
					ns.warn("Cannot set 2nd icon when widget has 1 icon");
				}
				if (hasSingle && name === "right-2nd-icon") {
					name = "right-1st-icon";
					ns.warn("Cannot set 2nd icon when widget has 1 icon");
				}

				self.options.position = name;

				self._updatePosition();
			};

			/**
			 * Get widget position by position name
			 * @method _getPositionByName
			 * @param {string} name name of preset position
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._getPositionByName = function (name) {
				var position = this._position;

				switch (name) {
					case "left-min": return position.min;
					case "left-2nd-icon": return position.leftOneButton;
					case "left-1st-icon": return position.left;
					case "center": return position.center;
					case "right-1st-icon": return position.right;
					case "right-2nd-icon": return position.rightOneButton;
					case "right-min": return position.max;
					default: return position.max;
				}
			};


			/**
			 * Find parent element and add/remove widget class
			 * @method _updateParentClasses
			 * @param {boolean} [remove=false] add or remove floating action class from page element
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._toggleParentClasses = function (remove) {
				var self = this,
					parentElement = selectorUtils.getClosestByClass(self.element, PageClasses.uiPage);

				if (parentElement) {
					parentElement.classList.toggle(classes.PAGE_WITH_FLOATING_ACTIONS, !remove);
				}
			};

			/**
			 * Set widget position by position name
			 * @method _getPositionByName
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._updatePosition = function () {
				var self = this,
					style = self.element.style,
					positionName = self.options.position,
					transform;

				self.element.classList.add(classes.TRANSITIONS);

				transform = "translate3d(" + self._getPositionByName(positionName) + "px, 0, 0)";
				style.webkitTransform = transform;
				style.transform = transform;

				self._expandWidget(positionName);
			};

			/**
			 * Get widget position name by position X
			 * @method _getPositionNameByPosition
			 * @param {number} positionX current widget position
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._getPositionNameByPosition = function (positionX) {
				var self = this,
					scope = self._scope,
					hasSingle = self._hasSingle;

				if (positionX < scope.min) {
					return "left-min";
				} else if (!hasSingle && positionX < scope.leftOneButton) {
					return "left-2nd-icon";
				} else if (positionX < scope.left) {
					return "left-1st-icon";
				} else if (positionX < scope.center) {
					return "center";
				} else if (positionX < scope.right) {
					return "right-1st-icon";
				} else if (!hasSingle && positionX < scope.rightOneButton) {
					return "right-2nd-icon";
				} else {
					return "right-min";
				}
			};

			/**
			 * Move widget to position X
			 * @method _moveTo
			 * @param {number} positionX current widget position
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._moveTo = function (positionX) {
				var self = this;

				self.options.position = self._getPositionNameByPosition(positionX);
				self._updatePosition();
			};

			/**
			 * Dragend event handler
			 * @method _end
			 * @param {Event} event
			 * @protected
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype._end = function (event) {
				var self = this;

				self._moveTo(
					event.detail.estimatedX - self._startX + self._currentX
				);
			};

			/**
			 * Handle events
			 * @method handleEvent
			 * @public
			 * @param {Event} event Event
			 * @member ns.widget.mobile.FloatingActions
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "dragstart":
						self._start(event);
						break;
					case "drag":
						self._move(event);
						break;
					case "dragend":
					case "dragcancel":
						self._end(event);
						break;
				}
			};

			prototype.onAttach = function () {
				this.refresh();
			};

			// definition
			FloatingActions.prototype = prototype;
			FloatingActions.classes = classes;
			ns.widget.mobile.FloatingActions = FloatingActions;

			engine.defineWidget(
				"FloatingActions",
				"[data-role='floatingactions'], ." + WIDGET_CLASS,
				[],
				FloatingActions,
				"mobile"
			);

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * # Scroll Handler
 * Extension for Scroll View Widget, adds scroll handler.
 *
 * ## Default selectors
 * All scrollview selectors with have a class _.ui-scrollhandler_
 * or _data-handler=[DIRECTION]_ will become be enhanced
 *
 * ### HTML examples
 *
 * #### Enhanced scrollview using data-handler attribute
 *
 *		@example
 *		<div data-role="page">
 *			<div data-role="content" data-handler="true">
 *				page content
 *			</div>
 *		</div>
 *
 * #### Enhanced scrollview using css .ui-scrollhandler class
 *
 *		@example
 *		<div data-role="page">
 *			<div data-role="content" class="ui-scrollhandler">
 *				page content
 *			</div>
 *		</div>
 *
 * ## Manual constructor
 * To create the widget manually you can use 2 different APIs, the TAU
 * API or jQuery API
 *
 * ### Enhanced scrollview by using TAU API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			var handlerElement = document.getElementById("myPage")
 *						.querySelector("[data-role=content]");
 *			tau.widget.ScrollHandler(handlerElement);
 *		</script>
 *
 * ### Enhanced scrollview by using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role=content]").scrollhandler();
 *		</script>
 *
 * ## Options for ScrollHandler
 *
 * Options can be set by using data-* attributes or by passing them to
 * the constructor.
 *
 * There is also a method **option** for changing them after widget
 * creation
 *
 * jQuery mobile API is also supported.
 *
 * ### Enable handler
 *
 * This option sets the handler status. The default value is true.
 *
 * You can change this option by all available methods for options
 * changing
 *
 * #### By data-handler attribute
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content" data-handler="true">
 *				page content
 *			<div>
 *		</div>
 *
 * #### By passing object to constructor
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			var handlerElement = document.getElementById("myPage")
 *						.querySelector("[data-role=content]");
 *			tau.widget.ScrollHandler(handlerElement, {
 *				"handler": true
 *			});
 *		</script>
 *
 * #### By using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role=content]").scrollhandler({
 *				"handler": "true"
 *			});
 *		</script>
 *
 * ### handlerTheme
 *
 * This option sets the handler theme. The default value is inherited
 * or "s" if none found.
 *
 * You can change this option by all available methods for options
 * changing
 *
 * #### By data-handler-theme attribute
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content" data-handler-theme="s" handler="true">
 *				page content
 *			<div>
 *		</div>
 *
 *
 * #### By passing object to constructor
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			var handlerElement = document.getElementById("myPage")
 *						.querySelector("[data-role=content]");
 *			tau.widget.ScrollHandler(handlerElement, {
 *				"handlerTheme": "s"
 *			});
 *		</script>
 *
 * #### By using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role=content]").scrollhandler({
 *				"handlerTheme": "s"
 *			});
 *		</script>
 *
 * ### direction
 *
 * This option sets the the direction of which the handler is presented.
 * The default value is "y" meaning vertical scroll button.
 *
 * You can change this option by all available methods for options
 * changing
 *
 * #### By data-handler-direction attribute
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content" data-direction="y" handler="true">
 *				page content
 *			<div>
 *		</div>
 *
 * #### By passing object to constructor
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			var handlerElement = document.getElementById("myPage")
 *						.querySelector("[data-role=content]"),
 *			tau.widget.ScrollHandler(handlerElement, {
 *				"scroll": "y"
 *			});
 *		</script>
 *
 * #### By using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role=content]").scrollhandler({
 *				"scroll": "y"
 *			});
 *		</script>
 *
 * ### scroll
 *
 * This option sets the the direction of which the handler is scrolling.
 * The default value is "y" which means vertical.
 *
 * You can change this option by all available methods for options
 * changing
 *
 * #### By data-handler-scroll attribute
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content" data-scroll="x" handler="true">
 *				page content
 *			<div>
 *		</div>
 *
 * #### By passing object to constructor
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			var handlerElement = document.getElementById("myPage")
 *						.querySelector("[data-role=content]"),
 *			tau.widget.ScrollHandler(handlerElement, {
 *				"scroll": "x"
 *			});
 *		</script>
 *
 * #### By using jQuery API
 *
 *		@example
 *		<div data-role="page" id="myPage">
 *			<div data-role="content">
 *				page content
 *			<div>
 *		</div>
 *		<script>
 *			$("#myPage > div[data-role=content]").scrollhandler({
 *				"scroll": "x"
 *			});
 *		</script>
 *
 * ## Methods
 *
 * ScrollHandler methods can be called through 2 APIs: TAU API and jQuery
 * API (jQuery Mobile-like API). Since this widget extends Scrollview,
 * all the Scrollview methods can be called also.
 *
 * @class ns.widget.mobile.ScrollHandler
 * @extends ns.widget.mobile.Scrollview
 *
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 * @author Piotr Karny <p.karny@samsung.com>
 * @author Hyunkook Cho <hk0713.cho@samsung.com>
 * @author Junhyeon Lee <juneh.lee@samsung.com>
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var ScrollHandler = function () {
					var self = this;
					/**
					 * Widget options
					 * @property {Object} options
					 * @property {boolean} [options.handler=true] Enabled flag
					 * @property {string} [options.handlerTheme="s"] Handler theme
					 * @property {"x"|"y"} [options.direction="y"] The direction of the handler
					 * @property {"x"|"y"|"xy"} [options.scroll="y"] The direction of scrolling
					 * @property {number} [options.delay=1500] Time in ms after which the scrollhandler disappears.
					 * @member ns.widget.mobile.ScrollHandler
					 */

					self.options = {
						handler: true,
						handlerTheme: "s",
						direction: "y",
						scroll: "y",
						delay: 1500
					};
					/**
					 * A collection of handler UI elements
					 * @property {Object} ui
					 * @member ns.widget.mobile.ScrollHandler
					 * @instance
					 */
					self.ui = {
						handler: null,
						thumb: null,
						track: null,
						handle: null,
						expander: null,
						page: null
					};
					/**
					 * Event listeners for various events
					 * @property {Object} _callbacks
					 * @property {Function} _callbacks.scrollstart Start handler
					 * @property {Function} _callbacks.scrollupdate Scrolling handler
					 * @property {Function} _callbacks.scrollend Scroll end handler
					 * @property {Function} _callbacks.touchstart Start handler
					 * @property {Function} _callbacks.touchmove Touch move  handler
					 * @property {Function} _callbacks.touchend Touch end handler
					 * @property {Function} _callbacks.resize Window resize handler
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._callbacks = {
						scrolstart: null,
						scrollupdate: null,
						scrollend: null,
						touchstart: null,
						touchmove: null,
						touchend: null,
						resize: null
					};
					/**
					 * A drag indicator flag
					 * @property {boolean} [_dragging=false]
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._dragging = false;
					/**
					 * Collection of scroll bounds params
					 * @property {Object} _offsets
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._offsets = {
						x: 0,
						y: 0,
						maxX: 0,
						maxY: 0
					};
					/**
					 * Holds original pointer events state
					 * @property {string} [_lastPointerEvents=""]
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._lastPointerEvents = "";
					/**
					 * Holds information about scrollviews available offset
					 * @property {number} [_availableOffsetX=0]
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._availableOffsetX = 0;
					/**
					 * Holds information about scrollviews available offset
					 * @property {number} [_availableOffsetX=0]
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._availableOffsetY = 0;
					/**
					 * @property {?number} [_hideTimer=null]
					 * Holds timer ID
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._hideTimer = null;
					/**
					 * Holds last mouse position
					 * @property {Object} _lastMouse
					 * @member ns.widget.mobile.ScrollHandler
					 * @protected
					 */
					self._lastMouse = {
						x: 0,
						y: 0
					};
				},
				engine = ns.engine,
				tauEvent = ns.event,
				CSSUtils = ns.util.DOM,
				selectors = ns.util.selectors,
				PageClasses = ns.widget.core.Page.classes,
				Scrollview = ns.widget.core.Scrollview,
				ScrollviewPrototype = Scrollview.prototype,
				ScrollviewBuild = ScrollviewPrototype._build,
				ScrollviewInit = ScrollviewPrototype._init,
				ScrollviewBindEvents = ScrollviewPrototype._bindEvents,
				ScrollviewDestroy = ScrollviewPrototype._destroy,
				max = Math.max,
				min = Math.min,
				floor = Math.floor,
				/**
				 * A collection of ScrollHandlers classes
				 * @property {Object} classes
				 * @property {string} [classes.handler="ui-handler"] Handler main class
				 * @property {string} [classes.directionPrefix="ui-handler-direction"] Direction class prefix
				 * @property {string} [classes.track="ui-handler-track"] Handler track class
				 * @property {string} [classes.thumb="ui-handler-thumb"] Handler thumb button prefix
				 * @property {string} [classes.themePrefix="ui-handler-"] Handler theme class prefix
				 * @property {string} [classes.scrollbarDisabled="scrollbar-disabled"] Scrollview scrollbar disabled class
				 * @property {string} [classes.disabled="disabled"] Disabled class
				 * @property {string} [classes.hideNativeScrollbar="ui-hide-scrollbar"] Hides native scrollbar in scrollview
				 * @member ns.widget.mobile.ScrollHandler
				 * @static
				 * @readonly
				 */
				classes = {
					handler: "ui-handler",
					directionPrefix: "ui-handler-direction-",
					track: "ui-handler-track",
					handle: "ui-handler-handle",
					thumb: "ui-handler-thumb",
					expander: "ui-handler-expander",
					visible: "ui-handler-visible",
					themePrefix: "ui-handler-",
					scrollbarDisabled: "scrollbar-disabled",
					disabled: "disabled",
					hideNativeScrollbar: "ui-hide-scrollbar"
				},
				prototype = new Scrollview();

			ScrollHandler.classes = classes;

			/**
			 * Translates objects position to a new position
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @param {number} xOffset
			 * @param {number} yOffset
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function translate(self, xOffset, yOffset) {
				var style = null,
					translateString = null;

				if (self.options.handler) {
					style = self.ui.handle.style;
					translateString = "translate3d(" + (xOffset || 0) + "px, " + (yOffset || 0) + "px, 0px)";

					style.webkitTransform = translateString;
					style.mozTransform = translateString;
					style.msTransform = translateString;
					style.oTransform = translateString;
					style.transform = translateString;
				}
			}

			/**
			 * Sets handler position according to scrollviews position
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function syncHandleWithScroll(self) {
				var position = self.getScrollPosition(),
					offsets = self._offsets,
					direction = self.options.direction,
					x = floor(min(position.x, self._availableOffsetX) / self._availableOffsetX * offsets.maxX),
					y = floor(min(position.y, self._availableOffsetY) / self._availableOffsetY * offsets.maxY);

				if (isNaN(x) === true) {
					x = offsets.x;
				}

				if (isNaN(y) === true) {
					y = offsets.y;
				}

				translate(
					self,
					direction === "y" ? 0 : x,
					direction === "x" ? 0 : y
				);

				offsets.x = x;
				offsets.y = y;
			}

			/**
			 * Handles scroll start event
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleScrollstart(self) {
				if (self._dragging === false) {
					syncHandleWithScroll(self);
					if (self._hideTimer) {
						window.clearTimeout(self._hideTimer);
					}
					self.ui.handler.classList.add(classes.visible);
				}
			}

			/**
			 * Handles scroll update event
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleScrollupdate(self) {
				if (self._dragging === false) {
					if (self._hideTimer) {
						window.clearTimeout(self._hideTimer);
					}
					syncHandleWithScroll(self);
				}
			}

			/**
			 * Handles scroll stop event
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleScrollstop(self) {
				if (self._dragging === false) {
					syncHandleWithScroll(self);
					if (self._hideTimer) {
						window.clearTimeout(self._hideTimer);
					}
					self._hideTimer = window.setTimeout(function () {
						self.ui.handler.classList.remove(classes.visible);
					}, self.options.delay);
				}
			}

			/**
			 * Handles dragging
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @param {number} x
			 * @param {number} y
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleDragging(self, x, y) {
				var lastMouse = self._lastMouse,
					offsets = self._offsets,
					direction = self.options.direction,
					diffX = lastMouse.x - x,
					diffY = lastMouse.y - y;

				lastMouse.x = x;
				lastMouse.y = y;

				// translate with direction locking
				offsets.x += -diffX;
				offsets.y += -diffY;

				// cap to between limits
				offsets.x = max(0, offsets.x);
				offsets.y = max(0, offsets.y);
				offsets.x = min(offsets.maxX, offsets.x);
				offsets.y = min(offsets.maxY, offsets.y);

				translate(
					self,
					direction === "y" ? 0 : offsets.x,
					direction === "x" ? 0 : offsets.y
				);

				self.scrollTo(
					direction === "y" ? 0 : offsets.x / offsets.maxX * self._availableOffsetX,
					direction === "x" ? 0 : offsets.y / offsets.maxY * self._availableOffsetY
				);

				if (self._hideTimer) {
					window.clearTimeout(self._hideTimer);
				}
			}

			/**
			 * Handles touch start event
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @param {MouseEvent|TouchEvent} event
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleTouchstart(self, event) {
				var lastMouse = self._lastMouse,
					touches = event.touches,
					touch = touches && touches[0];

				// remove timer
				if (self._hideTimer) {
					window.clearTimeout(self._hideTimer);
				}

				self._dragging = true;
				lastMouse.x = touch ? touch.clientX : event.clientX;
				lastMouse.y = touch ? touch.clientY : event.clientY;

				self.ui.handle.classList.add("ui-active");

				tauEvent.stopImmediatePropagation(event);
				tauEvent.preventDefault(event);
			}

			/**
			 * Handles touch move events
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @param {MouseEvent|TouchEvent} event
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleTouchmove(self, event) {
				var touches = event.touches,
					touch = touches && touches[0],
					x = 0,
					y = 0;
				// check for exactly 1 touch event
				// or a mouse event

				if (self._dragging && (touches === undefined || touches.length <= 1)) {
					tauEvent.stopImmediatePropagation(event);
					tauEvent.preventDefault(event);

					x = touch ? touch.clientX : event.clientX;
					y = touch ? touch.clientY : event.clientY;
					handleDragging(self, x, y);
				}
			}

			/**
			 * Handles touch end event
			 * @param {ns.widget.mobile.ScrollHandler} self
			 * @param {MouseEvent|TouchEvent} event
			 * @member ns.widget.mobile.ScrollHandler
			 * @private
			 * @static
			 */
			function handleTouchend(self, event) {
				var ui = self.ui;

				if (self._dragging) {
					self._dragging = false;

					tauEvent.stopImmediatePropagation(event);
					tauEvent.preventDefault(event);

					ui.handle.classList.remove("ui-active");

					if (self._hideTimer) {
						window.clearTimeout(self._hideTimer);
					}
					self._hideTimer = window.setTimeout(function () {
						ui.handler.classList.remove(classes.visible);
					}, self.options.delay);
				}
			}

			/**
			 * Build the scrollhander and scrollview DOM
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @method _build
			 * @member ns.widget.mobile.ScrollHandler
			 * @protected
			 */
			prototype._build = function (element) {
				var node,
					nodeStyle,
					scrollviewViewStyle,
					handler = document.createElement("div"),
					handle = document.createElement("a"),
					expander = document.createElement("span"),
					track = document.createElement("div"),
					thumb = document.createElement("span"),
					options = this.options,
					ui = this.ui;

				// Set scroll option for scrollview
				options.scroll = options.direction === "y" ? "y" : "x";
				node = ScrollviewBuild.call(this, element);

				handler.className = classes.handler + " " + classes.themePrefix + options.handlerTheme + " " + classes.directionPrefix + options.direction;
				expander.className = classes.expander;
				handle.className = classes.handle;
				thumb.className = classes.thumb;
				track.className = classes.track;

				handle.setAttribute("aria-label", (options.direction === "y" ? "Vertical" : "Horizontal") + " handler, double tap and move to scroll");

				expander.appendChild(thumb);
				handle.appendChild(expander);
				track.appendChild(handle);
				handler.appendChild(track);

				node.appendChild(handler);

				// Force scrollview to be full width of container
				nodeStyle = node.style;
				scrollviewViewStyle = node.firstElementChild.style;

				// NOTE: to hide native scrollbar, make sure that theme includes
				// *display* property set to *none* for
				// .ui-content.ui-scrollview-clip.ui-hide-scrollbar::-webkit-scrollbar
				element.classList.add(classes.hideNativeScrollbar);

				if (options.direction === "x") {
					scrollviewViewStyle.display = "inline-block";
					scrollviewViewStyle.minWidth = "100%";
				}
				if (options.direction === "y") {
					scrollviewViewStyle.display = "block";
					nodeStyle.minWidth = "100%";
				}

				ui.handler = handler;
				ui.handle = handle;
				ui.expander = expander;
				ui.track = track;
				ui.thumb = thumb;

				return node;
			};

			/**
			 * Init the scrollhander and scrollview
			 * @param {HTMLElement} element
			 * @method _init
			 * @protected
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self.ui,
					page = ui.page;

				ScrollviewInit.call(self, element);

				if (ui.handler === null) {
					ui.handler = element.querySelector("." + classes.handler);
				}

				if (ui.track === null) {
					ui.track = element.querySelector("." + classes.track);
				}

				if (ui.handle === null) {
					ui.handle = element.querySelector("." + classes.handle);
				}

				if (ui.thumb === null) {
					ui.thumb = element.querySelector("." + classes.thumb);
				}

				if (page === null) {
					page = selectors.getClosestByClass(element, PageClasses.uiPage);
				}
				ui.page = page;

				self.enableHandler(true);
			};

			/**
			 * Refreshes the scrollhander bounds and dimensions
			 * @method _refresh
			 * @protected
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype._refresh = function () {
				var self = this,
					element = self.element,
					offsets = self._offsets,
					ui = self.ui,
					handle = ui.handle,
					handleStyle = handle.style,
					trackRect = ui.track.getBoundingClientRect(),
					clipHeight = trackRect.height,
					clipWidth = trackRect.width,
					view = element.querySelector("." + Scrollview.classes.view),
					viewRect = view.getBoundingClientRect(),
					viewHeight = viewRect.height,
					viewWidth = viewRect.width;


				if (self.options.direction === "y") {
					handleStyle.height = floor(clipHeight / viewHeight * clipHeight) + "px";
				} else {
					handleStyle.width = floor(clipWidth / viewWidth * clipWidth) + "px";
				}

				offsets.maxX = floor(max(0, clipWidth - CSSUtils.getElementWidth(handle, "inner", true)));
				offsets.maxY = floor(max(0, clipHeight - CSSUtils.getElementHeight(handle, "inner", true)));

				self._availableOffsetX = max(0, viewWidth - clipWidth);
				self._availableOffsetY = max(0, viewHeight - clipHeight);
			};

			/**
			 * Binds the scrollhander and scrollview events
			 * @param {HTMLElement} element
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype._bindEvents = function (element) {
				var self = this,
					callbacks = self._callbacks,
					ui = self.ui;

				ScrollviewBindEvents.call(self, element);

				callbacks.scrollstart = handleScrollstart.bind(null, self);
				callbacks.scrollupdate = handleScrollupdate.bind(null, self);
				callbacks.scrollstop = handleScrollstop.bind(null, self);
				callbacks.touchstart = handleTouchstart.bind(null, self);
				callbacks.touchmove = handleTouchmove.bind(null, self);
				callbacks.touchend = handleTouchend.bind(null, self);
				callbacks.resize = self._refresh.bind(self);

				element.addEventListener("scrollstart", callbacks.scrollstart, false);
				element.addEventListener("scrollupdate", callbacks.scrollupdate, false);
				element.addEventListener("scrollstop", callbacks.scrollstop, false);
				ui.handle.addEventListener("vmousedown", callbacks.touchstart, false);
				ui.page.addEventListener("pageshow", callbacks.resize, false);
				document.addEventListener("vmousemove", callbacks.touchmove, false);
				document.addEventListener("vmouseup", callbacks.touchend, false);
				window.addEventListener("throttledresize", callbacks.resize, false);
				document.addEventListener("touchcancel", callbacks.touchend, true);

			};

			/**
			 * Enables/disables handler
			 *
			 * #### TAU API
			 *
			 *		@example
			 *		<div data-role="page" id="myPage">
			 *			<div data-role="content">
			 *				page content
			 *			<div>
			 *		</div>
			 *		<script>
			 *			var handlerElement = document.getElementById("myPage")
			 *						.querySelector("[data-role=content]"),
			 *				scrollhandler = tau.widget.ScrollHandler(handlerElement);
			 *			scrollhandler.enableHandler(true);
			 *		</script>
			 *
			 * #### jQuery API
			 *
			 *		@example
			 *		<div data-role="page" id="myPage">
			 *			<div data-role="content">
			 *				page content
			 *			<div>
			 *		</div>
			 *		<script>
			 *			#("#myPage > div[data-role=content]).scrollhandler("enableHandler", true);
			 *		</script>
			 *
			 * @param {boolean} enable
			 * @return {boolean}
			 * @method enableHandler
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype.enableHandler = function (enable) {
				var self = this,
					scrollBarDisabledClass = classes.scrollbarDisabled,
					disabledClass = classes.disabled,
					element = self.element,
					parentClassList = element.parentNode.classList,
					elementClassList = element.classList;

				if (enable !== undefined) {
					self.options.handler = enable;
					if (enable) {
						parentClassList.add(scrollBarDisabledClass);
						elementClassList.remove(disabledClass);
						self._refresh();
					} else {
						parentClassList.remove(scrollBarDisabledClass);
						elementClassList.add(disabledClass);
					}
				}

				return self.options.handler;
			};

			/**
			 * Sets the handlers theme
			 * @param {string} theme
			 * @method _setHandlerTheme
			 * @protected
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype._setHandlerTheme = function (theme) {
				var elementClassList = this.element.classList,
					themePrefix = classes.themePrefix,
					themeClass = themePrefix + theme;

				if (elementClassList.contains(themeClass) === false) {
					elementClassList.remove(themePrefix + this.options.handlerTheme);
					elementClassList.add(themeClass);
				}
			};

			/**
			 * Destroys the scrollhander and scrollview DOM
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.ScrollHandler
			 */
			prototype._destroy = function () {
				var self = this,
					ui = self.ui,
					callbacks = self._callbacks,
					element = self.element;

				// Restore native scrollbar
				element.classList.remove(classes.hideNativeScrollbar);
				element.removeEventListener("scrollstart", callbacks.scrollstart, false);
				element.removeEventListener("scroll", callbacks.scrollupdate, false);
				element.removeEventListener("scrollstop", callbacks.scrollstop, false);
				ui.handle.removeEventListener("vmousedown", callbacks.touchstart, false);
				ui.page.removeEventListener("pageshow", callbacks.touchstart, false);
				document.removeEventListener("vmousemove", callbacks.touchmove, false);
				document.removeEventListener("vmouseup", callbacks.touchend, false);
				document.removeEventListener("touchcancel", callbacks.touchend, true);
				window.removeEventListener("throttledresize", callbacks.resize, false);

				ScrollviewDestroy.call(self);
			};

			ScrollHandler.prototype = prototype;

			ns.widget.mobile.ScrollHandler = ScrollHandler;
			engine.defineWidget(
				"ScrollHandler",
				"[data-role='content'][data-handler='true']:not([data-scroll='none']):not(.ui-scrollview-clip):not(.ui-scrolllistview),[data-handler='true'], .ui-scrollhandler",
				[
					"enableHandler",
					"scrollTo",
					"ensureElementIsVisible",
					"centerToElement",
					"getScrollPosition",
					"skipDragging",
					"translateTo"
				],
				ScrollHandler,
				"tizen"
			);
			}(window, window.document, ns));


/*global window, ns, define */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 *
 * #Tabs
 * Tabs component shows an unordered list of buttons on the screen wrapped together in a single group.
 *
 * The Tabs component is a controller component for operate closely with tabbar and sectionChanger.
 * So this component should be used with tabbar and sectionChanger.
 *
 * ##Default selectors
 * By default, all elements with the class="ui-tabs" or data-role="tabs" attribute are displayed as a tabs components.
 *
 * ##HTML Examples
 *
 *      @example
 *      <div id="tabs" class="ui-tabs">
 *          <div class="ui-tabbar">
 *              <ul>
 *                  <li><a href="#" class="ui-btn-active">Tab1</a></li>
 *                  <li><a href="#">Tab2</a></li>
 *                  <li><a href="#">Tab3</a></li>
 *              </ul>
 *          </div>
 *          <div class="ui-section-changer">
 *              <div>
 *                  <section class="ui-section-active">
 *                      <ul class="ui-listview">
 *                          <li class="ui-li-static">
 *                              Section 1
 *                          </li>
 *                      </ul>
 *                  </section>
 *                  <section class="ui-section-active">
 *                      <ul class="ui-listview">
 *                          <li class="ui-li-static">
 *                              Section 2
 *                          </li>
 *                      </ul>
 *                  </section>
 *                  <section class="ui-section-active">
 *                      <ul class="ui-listview">
 *                          <li class="ui-li-static">
 *                              Section 3
 *                          </li>
 *                      </ul>
 *                  </section>
 *              </div>
 *          </div>
 *      </div>
 *
 * ##Manual constructor
 * For manual creation of tabs widget you can use constructor of widget
 *
 *      @example
 *      <script>
 *          var tabsElement = document.getElementById("tabs"),
 *                tabs;
 *          tabs = tau.widget.Tabs(tabsElement);
 *      </script>
 *
 * @since 2.4
 * @class ns.widget.core.Tabs
 * @extends ns.widget.core.BaseWidget
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				selectors = ns.util.selectors,
				Page = ns.widget.core.Page,
				events = ns.event,
				Tabs = function () {
					var self = this;

					self._ui = {};
					self._component = {};
					self.options = {
						changeDuration: 200
					};
				},
				classes = {
					TABS: "ui-tabs",
					WITH_TITLE: "ui-tabs-with-title",
					TITLE: "ui-title",
					PAGE: Page.classes.uiPage
				},
				prototype = new BaseWidget();

			Tabs.prototype = prototype;
			Tabs.classes = classes;

			/**
			 * bind Tabs component necessary events
			 * @method bindTabsEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Tabs
			 * @private
			 * @static
			 */
			function bindTabsEvents(element) {
				var self = this;

				events.on(element, "tabchange sectionchange", self, false);
				window.addEventListener("resize", self, false);
			}

			/**
			 * unbind Tabs component necessary events
			 * @method unBindTabsEvents
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Tabs
			 * @private
			 * @static
			 */
			function unBindTabsEvents(element) {
				var self = this;

				events.off(element, "tabchange sectionchange", self, false);
				window.removeEventListener("resize", self, false);
			}

			/**
			 * Handle events
			 * @method handleEvent
			 * @param {Event} event
			 * @member ns.widget.core.Tabs
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "tabchange":
						self._onTabChange(event);
						break;
					case "sectionchange":
						self._onSectionChange(event);
						break;
					case "resize":
						self._refresh();
						break;
				}
			};

			/**
			 * Build the Tabs component
			 * @method _build
			 * @protected
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Tabs
			 */
			prototype._build = function (element) {

				element.classList.add(classes.TABS);
				if (element.getElementsByClassName(classes.TITLE).length) {
					element.classList.add(classes.WITH_TITLE);
				}
				return element;
			};

			/**
			 * Init the Tabs component
			 * @method _init
			 * @protected
			 * @param {HTMLElement} element
			 * @member ns.widget.core.Tabs
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui;

				ui.page = selectors.getClosestByClass(element, classes.PAGE);
				ui.tabbar = element.querySelector("[data-role='tabbar'], .ui-tabbar");
				ui.title = element.getElementsByClassName(classes.TITLE)[0];
				ui.sectionChanger = element.querySelector("[data-role='section-changer'], .ui-section-changer");
				self._component.tabbar = ns.widget.TabBar(ui.tabbar);
				self._changed = false;
				self._lastIndex = 0;
				self._initSectionChanger();
				return element;
			};

			/**
			 * Pageshow event handler
			 * @method _onPageBeforeShow
			 * @protected
			 * @member ns.widget.core.Tabs
			 */
			prototype._initSectionChanger = function () {
				var self = this,
					ui = self._ui,
					sectionChanger = ui.sectionChanger,
					sectionChangerStyle,
					tabbarOffsetHeight = ui.tabbar.offsetHeight,
					title = ui.title;

				if (sectionChanger) {
					sectionChangerStyle = sectionChanger.style;
					sectionChangerStyle.width = window.innerWidth + "px";
					sectionChangerStyle.height = (self.element.offsetHeight - tabbarOffsetHeight -
						(title ? title.offsetHeight : 0)) + "px";
					self._component.sectionChanger = engine.instanceWidget(sectionChanger, "SectionChanger");
				}

			};

			/**
			 * Tabchange event handler
			 * @method _onTabChange
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Tabs
			 */
			prototype._onTabChange = function (event) {
				var self = this,
					index = event.detail.active,
					sectionChanger = self._component.sectionChanger;

				if (self._changed) {
					self._changed = false;
				} else if (self._lastIndex !== index) {
					self._changed = true;
					sectionChanger.setActiveSection(index, self.options.changeDuration);
				}
				self._lastIndex = index;
			};

			/**
			 * Sectionchange event handler
			 * @method _onSectionChange
			 * @protected
			 * @param {Event} event
			 * @member ns.widget.core.Tabs
			 */
			prototype._onSectionChange = function (event) {
				var self = this,
					index = event.detail.active,
					tabbar = self._component.tabbar;

				if (self._changed) {
					self._changed = false;
				} else if (self._lastIndex !== index) {
					self._changed = true;
					tabbar.setActive(index);
				}
				self._lastIndex = index;
			};

			/**
			 * bind event to the Tabs component
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.core.Tabs
			 */
			prototype._bindEvents = function () {
				var self = this;

				bindTabsEvents.call(self, self.element);
			};

			/**
			 * destroy the Tabs component
			 * @method _destroy
			 * @protected
			 * @member ns.widget.core.Tabs
			 */
			prototype._destroy = function () {
				var self = this;

				unBindTabsEvents.call(self, self.element);
				self._ui = null;
				self._component = null;
			};

			/**
			 * Refresh Tabs component
			 * @method _refresh
			 * @protected
			 * @member ns.widget.core.Tabs
			 */
			prototype._refresh = function () {
				this._initSectionChanger();
			};
			/**
			 * Set the active tab
			 * @method _setIndex
			 * @protected
			 * @param {number} index
			 * @member ns.widget.core.Tabs
			 */
			prototype._setIndex = function (index) {
				var self = this,
					length = self._ui.sectionChanger.getElementsByTagName("section").length;

				if (index < length && !(index < 0)) {
					self._component.tabbar.setActive(index);
				} else {
					ns.warn("You inserted the wrong index value");
				}

			};

			/**
			 * Set the active tab
			 * @method setIndex
			 * @public
			 * @param {number} index
			 * @member ns.widget.core.Tabs
			 */
			prototype.setIndex = function (index) {
				this._setIndex(index);
			};

			/**
			 * Get the active tab
			 * @method _getIndex
			 * @protected
			 * @return {number} index
			 * @member ns.widget.core.Tabs
			 */
			prototype._getIndex = function () {
				return this._lastIndex;
			};

			/**
			 * Get the active tab
			 * @method getIndex
			 * @public
			 * @return {number} index
			 * @member ns.widget.core.Tabs
			 */
			prototype.getIndex = function () {
				return this._getIndex();
			};
			ns.widget.core.Tabs = Tabs;
			engine.defineWidget(
				"Tabs",
				"[data-role='tabs'], .ui-tabs",
				[
					"setIndex", "getIndex"
				],
				Tabs,
				"core"
			);

			}(window.document, ns));

/*global window, define, ns */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*jslint nomen: true */
/**
 * # Grid View
 * Grid View components provides a list of grid-type and presents contents that are easily identified as images.
 *
 * ##Default Selectors
 * By default, all ul elements with the class="ui-gridview" or data-role="gridview" attribute are displayed as grid view components.
 *
 * ##Manual constructor
 *
 *      @example
 *      <ul id="gridview" class="ui-gridview">
 *          <li class="ui-gridview-item">
 *              <img class="ui-gridview-image" src="images/1.jpg">
 *              <div class="ui-gridview-handler"></div>
 *          </li>
 *          <li class="ui-gridview-item">
 *              <img class="ui-gridview-image" src="images/2.jpg">
 *              <div class="ui-gridview-handler"></div>
 *          </li>
 *          <li class="ui-gridview-item">
 *              <img class="ui-gridview-image" src="images/3.jpg">
 *              <div class="ui-gridview-handler"></div>
 *          </li>
 *          <li class="ui-gridview-item">
 *              <img class="ui-gridview-image" src="images/4.jpg">
 *              <div class="ui-gridview-handler"></div>
 *          </li>
 *      </ul>
 *      <script>
 *          var elGridView = document.getElementById("gridview"),
 *               gridView = tau.widget.GridView(elGridView);
 *      </script>
 *
 * @since 2.4
 * @class ns.widget.mobile.GridView
 * @extends ns.widget.BaseWidget
 */
(function (document, ns) {
	"use strict";
				var BaseWidget = ns.widget.BaseWidget,
				engine = ns.engine,
				utilsEvents = ns.event,
				utilsSelectors = ns.util.selectors,
				utilsDom = ns.util.DOM,
				pageEvents = ns.widget.core.Page.events,
				Popup = ns.widget.mobile.Popup,
				PopupConstructor = ns.widget.Popup,
				popupSelector = Popup.selector,
				popupEvents = Popup.events,
				STYLE_PATTERN = ".ui-gridview li:nth-child({index})",
				MATRIX_REGEXP = /matrix\((.*), (.*), (.*), (.*), (.*), (.*)\)/,
				DATA_ROLE = "data-role",
				direction = {
					PREV: 0,
					NEXT: 1
				},
				labels = {
					IN: "in",
					OUT: "out",
					NONE: "none"
				},
				classes = {
					GRIDLIST: "ui-gridview",
					ITEM: "ui-gridview-item",
					ITEM_ACTIVE: "ui-gridview-item-active",
					HELPER: "ui-gridview-helper",
					HOLDER: "ui-gridview-holder",
					LABEL: "ui-gridview-label",
					LABEL_IN: "ui-gridview-label-in",
					LABEL_OUT: "ui-gridview-label-out",
					HANDLER: "ui-gridview-handler"
				},
				GridView = function () {
					var self = this;

					self.options = {};
					self._direction = 0;
					self._styleElement = null;
					self._inPopup = null;
					self._ui = {
						listElements: [],
						listItems: [],
						helper: {},
						holder: {},
						scrollableParent: null,
						content: null
					};
					self._refreshSizesCallback = refreshSizes.bind(null, this);
				},
				prototype = new BaseWidget();

			GridView.prototype = prototype;
			GridView.classes = classes;

			function getScrollableParent(element) {
				var overflow;

				while (element !== document.body) {
					overflow = utilsDom.getCSSProperty(element, "overflow-y");
					if (overflow === "scroll" || (overflow === "auto" && element.scrollHeight > element.clientHeight)) {
						return element;
					}
					element = element.parentNode;
				}

				return null;
			}

			function refreshSizes(gridViewInstance) {
				gridViewInstance._setItemWidth();
				gridViewInstance._setGridStyle();
				gridViewInstance._refreshItemsInfo();
				gridViewInstance._calculateListHeight();
				gridViewInstance._inPopup.refresh();
			}

			/**
			 * Configure options for GridView
			 * @method _configure
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._configure = function () {
				/**
				 * Options for widget.
				 * @property {Object} options
				 * @property {number} [options.cols=4] the number of columns to be displayed (for landscape 7)
				 * @property {boolean} [options.reorder=false] represents whether grid view is reorder mode
				 * @property {string} [options.label="none"] type of label to be attached to grid item("none", "in", "out")
				 * @property {number} [options.minWidth=null] minimum width px of grid item(number or "auto")
				 * @property {number} [options.minCols=1] the minimum number of columns
				 * @property {number} [options.maxCols=5] the maximum number of columns (for landscape 7)
				 * @member ns.widget.mobile.GridView
				 */
				this.options = {
					cols: 4,
					reorder: false,
					label: labels.NONE,
					minWidth: null,
					minCols: 1,
					maxCols: 5
				};
				// for landscape 7
				if (window.innerWidth > window.innerHeight) {
					this.options.cols = 7;
					this.options.maxCols = 7;
				}
				this._direction = direction.NEXT;
			};

			/**
			 * Build GridView
			 * @method _build
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._build = function (element) {
				return element;
			};

			/**
			 * Initialize GridView
			 * @method _init
			 * @param {HTMLElement} element
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._init = function (element) {
				var self = this,
					ui = self._ui,
					popup;

				ui.listElements = [].slice.call(self.element.getElementsByTagName("li"));
				self._setItemWidth();
				self._setLabel(element);
				self._setReorder(element, self.options.reorder);
				self._calculateListHeight();
				self._ui.content = utilsSelectors.getClosestByClass(element, "ui-content") || window;
				self._ui.scrollableParent = getScrollableParent(element) || self._ui.content;
				popup = utilsSelectors.getClosestBySelector(element, popupSelector);
				if (popup) {
					self._inPopup = PopupConstructor(popup);
				}
			};

			function animationEndCallback(event) {
				var classList = event.target.classList;

				if (classList.contains(classes.ITEM)) {
					classList.add(classes.ITEM_ACTIVE);
					event.target.style.animation = "";
				}
			}

			function onSetGridStyle(self) {
				self._setGridStyle();
			}

			/**
			 * Bind events for GridView
			 * @method _bindEvents
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._bindEvents = function () {
				var self = this,
					page = self._getParentPage(self.element),
					popup = this._inPopup;

				self._onSetGridStyle = onSetGridStyle.bind(null, self);
				if (popup) {
					utilsEvents.on(popup.element, popupEvents.before_show, self._refreshSizesCallback);
				}

				self.on("animationend webkitAnimationEnd", animationEndCallback);

				utilsEvents.on(page, pageEvents.SHOW, self._onSetGridStyle);
			};

			/**
			 * Unbind events for GridView
			 * @method _unbindEvents
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._unbindEvents = function () {
				var self = this,
					element = self.element,
					page = self._getParentPage(element),
					popup = self._inPopup;

				utilsEvents.disableGesture(element);

				utilsEvents.off(element, "drag dragstart dragend dragcancel dragprepare", self);
				utilsEvents.off(element, "pinchin pinchout", self);
				if (popup) {
					utilsEvents.off(popup.element, popupEvents.before_show, this._refreshSizesCallback);
				}
				self.off("animationend webkitAnimationEnd", animationEndCallback);
				utilsEvents.off(page, pageEvents.SHOW, self._onSetGridStyle);
			};

			/**
			 * Refresh GridView
			 * @method _refresh
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._refresh = function () {
				var self = this,
					ui = self._ui,
					element = self.element;

				self._removeGridStyle();
				ui.listElements = [].slice.call(element.getElementsByTagName("li"));
				self._setItemWidth();
				self._setGridStyle();
				self._setLabel(element);
				self._setReorder(element, self.options.reorder);
				self._calculateListHeight();
				self._ui.content = utilsSelectors.getClosestByClass(element, "ui-content") || window;
				self._ui.scrollableParent = getScrollableParent(element) || self._ui.content;
			};

			/**
			 * Destroy GridView
			 * @method _destroy
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._destroy = function () {
				this._unbindEvents();
				this._removeGridStyle();
				this._inPopup = null;
			};

			/**
			 * Handle events
			 * @method handleEvent
			 * @public
			 * @param {Event} event Event
			 * @member ns.widget.mobile.GridView
			 */
			prototype.handleEvent = function (event) {
				var self = this;

				switch (event.type) {
					case "dragprepare":
						if (event.detail.srcEvent.srcElement.classList.contains(classes.HANDLER)) {
							break;
						}
						event.preventDefault();
						break;
					case "dragstart":
						if (event.detail.srcEvent.srcElement.classList.contains(classes.HANDLER)) {
							self._start(event);
							break;
						}
						event.preventDefault();
						break;
					case "drag":
						self._move(event);
						break;
					case "dragend":
						self._end(event);
						break;
					case "pinchin":
						self._in(event);
						break;
					case "pinchout":
						self._out(event);
						break;
				}
			};

			/**
			 * Method for dragstart event
			 * @method _start
			 * @protected
			 * @param {Event} event Event
			 * @member ns.widget.mobile.GridView
			 */
			prototype._start = function (event) {
				var self = this,
					element = self.element,
					helper = self._ui.helper,
					helperElement = event.detail.srcEvent.srcElement.parentElement,
					helperElementComputed = window.getComputedStyle(helperElement, null),
					helperStyle = helperElement.style,
					transformProperty = helperElementComputed.getPropertyValue("webkit-transform") ||
						helperElementComputed.getPropertyValue("transform") || "",
					translated = transformProperty.match(MATRIX_REGEXP),
					holder,
					top = 0,
					left = 0;

				self._refreshItemsInfo();
				if (translated.length > 0) {
					top = parseInt(translated[6], 10);
					left = parseInt(translated[5], 10);
				}
				helperElement.classList.add(classes.HELPER);
				holder = self._createHolder();
				element.insertBefore(holder, helperElement);
				element.appendChild(helperElement);
				helperStyle.top = top + "px";
				helperStyle.left = left + "px";

				helper.element = helperElement;
				helper.style = helperStyle;
				helper.position = {
					startTop: top,
					startLeft: left,
					moveTop: top,
					moveLeft: left
				};

				helper.startX = event.detail.estimatedX;
				helper.startY = event.detail.estimatedY;
				helper.width = parseFloat(helperElementComputed.getPropertyValue("width")) || 0;
				helper.height = parseFloat(helperElementComputed.getPropertyValue("height")) || 0;

				self._ui.holder = holder;
				helper.element = helperElement;
				self._ui.helper = helper;
			};

			/**
			 * Method for drag event
			 * @method _move
			 * @protected
			 * @param {Event} event Event
			 * @member ns.widget.mobile.GridView
			 */
			prototype._move = function (event) {
				var self = this,
					ui = self._ui,
					element = self.element,
					listItems = ui.listItems,
					length = listItems.length,
					helper = self._ui.helper,
					style = helper.style,
					position = helper.position,
					helperElement = helper.element,
					startX = helper.startX,
					startY = helper.startY,
					moveX,
					moveY,
					i,
					scrollableParent = self._ui.scrollableParent,
					autoScrollDown,
					autoScrollUp,
					scrollUnit;

				moveY = position.startTop + event.detail.estimatedY - startY;
				moveX = position.startLeft + event.detail.estimatedX - startX;
				autoScrollDown = (element.offsetTop + moveY + helperElement.offsetHeight) - (scrollableParent.offsetHeight + scrollableParent.scrollTop);
				autoScrollUp = scrollableParent.scrollTop - (element.offsetTop + moveY);
				scrollUnit = helperElement.offsetHeight / 5;
				if (autoScrollDown > 0 && ((helperElement.offsetTop + helperElement.offsetHeight) < element.offsetHeight)) {
					scrollableParent.scrollTop += scrollUnit;
					moveY += scrollUnit;
					position.startTop += scrollUnit;
				}
				if (autoScrollUp > 0 && helperElement.offsetTop > 0) {
					scrollableParent.scrollTop -= scrollUnit;
					moveY -= scrollUnit;
					position.startTop -= scrollUnit;
				}
				style.top = moveY + "px";
				style.left = moveX + "px";
				position.moveTop = moveY;
				position.moveLeft = moveX;

				for (i = 0; i < length; i++) {
					if (self._compareOverlapItem(listItems[i])) {
						self._direction ? element.insertBefore(ui.holder, listItems[i].element.nextSibling) : element.insertBefore(ui.holder, listItems[i].element);
						self._refreshItemsInfo();
					}
				}
			};

			/**
			 * Method for dragend event
			 * @method _end
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._end = function () {
				var self = this,
					element = self.element,
					helper = self._ui.helper,
					helperElement = helper.element,
					holder = self._ui.holder;

				helperElement.classList.remove(classes.HELPER);
				helper.style.top = 0;
				helper.style.left = 0;
				element.insertBefore(helperElement, holder);
				element.removeChild(holder);
				self._ui.helper = {};
			};

			/**
			 * Method for pinchout event
			 * @method _out
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._out = function () {
				var self = this,
					options = self.options,
					cols = options.cols,
					minCols = options.minCols;

				if (cols > minCols) {
					options.minWidth = null;
					options.cols = cols - 1;
					self._refresh();
				}
			};

			/**
			 * Method for pinchin event
			 * @method _in
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._in = function () {
				var self = this,
					options = self.options,
					cols = options.cols,
					maxCols = options.maxCols;

				if (maxCols === null || cols < maxCols) {
					options.cols = cols + 1;
					options.minWidth = null;
					self._refresh();
				}
			};

			/**
			 * Check whether a selected item is overlapped with adjacent items
			 * @method _compareOverlapItem
			 * @protected
			 * @param {HTMLElement} item
			 * @member ns.widget.mobile.GridView
			 */
			prototype._compareOverlapItem = function (item) {
				var self = this,
					helper = self._ui.helper,
					position = helper.position,
					overlapWidth,
					overlapHeight;

				if (helper.element === item.element) {
					return false;
				}

				if (position.moveTop > item.top || (position.moveTop === item.top && position.moveLeft > item.left)) {
					self._direction = direction.PREV;
				} else {
					self._direction = direction.NEXT;
				}

				overlapWidth = position.moveTop > item.top ? item.top + item.height - position.moveTop : position.moveTop + helper.height - item.top;
				overlapHeight = position.moveLeft > item.left ? item.left + item.width - position.moveLeft : position.moveLeft + helper.width - item.left;

				if (overlapWidth <= 0 || overlapHeight <= 0) {
					return false;
				} else if (overlapWidth * overlapHeight > item.height * item.width / 2) {
					return true;
				}
				return false;
			};

			/**
			 * Calculate and set the height of grid view depending on the number of columns
			 * @method _calculateListHeight
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._calculateListHeight = function () {
				var self = this,
					listElements = self._ui.listElements,
					firstLiComputed = listElements.length && window.getComputedStyle(listElements[0], null),
					itemHeight,
					rows;

				rows = Math.ceil(listElements.length / self.options.cols);
				itemHeight = parseFloat(firstLiComputed.getPropertyValue("height")) || 0;

				if (self.element.getAttribute("data-label") === "out") {
					self.element.style.height = (itemHeight * rows) + "px";
				} else {
					self.element.style.height = (itemHeight + 1) * rows + 1 + "px";
				}
			};

			/**
			 * Update information of each list item
			 * @method _refreshItemsInfo
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._refreshItemsInfo = function () {
				var self = this,
					listElements = self._ui.listElements,
					length = listElements.length,
					listItems = [],
					translated,
					li,
					i,
					top = 0,
					liComputed = null,
					left = 0,
					transformProperty = "";

				for (i = 0; i < length; i++) {
					li = listElements[i];
					liComputed = window.getComputedStyle(li, null);
					transformProperty = liComputed.getPropertyValue("webkit-transform") ||
						liComputed.getPropertyValue("transform") || "";
					translated = transformProperty.match(MATRIX_REGEXP);
					if (translated && translated.length > 0) {
						top = parseInt(translated[6], 10);
						left = parseInt(translated[5], 10);
					}
					listItems.push({
						top: top,
						left: left,
						height: parseFloat(liComputed.getPropertyValue("width")) || 0,
						width: parseFloat(liComputed.getPropertyValue("width")) || 0,
						element: li
					});
				}

				self._ui.listItems = listItems;

			};

			/**
			 * Create holder element to help reordering
			 * @method _createHolder
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._createHolder = function () {
				var holder = document.createElement("li"),
					classList = holder.classList;

				classList.add(classes.ITEM);
				classList.add(classes.HOLDER);

				return holder;
			};

			/**
			 * Set the width of each item
			 * @method _setItemWidth
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._setItemWidth = function () {
				var self = this,
					options = self.options,
					parentComputedStyle = window.getComputedStyle(self.element, null),
					parentWidth = parseFloat(parentComputedStyle.getPropertyValue("width")) || 0,
					parentHeight = self.element.firstElementChild.offsetHeight || 0,
					minWidth = options.minWidth,
					listElements = self._ui.listElements,
					length = listElements.length,
					firstLiComputed = listElements.length && window.getComputedStyle(listElements[0], null),
					cols,
					i,
					width,
					borderSize = 1,
					elementStyle = null;

				if (minWidth === "auto") {
					minWidth = parseFloat(firstLiComputed.getPropertyValue("width")) || 0;
					options.minWidth = minWidth;
				} else {
					minWidth = parseInt(minWidth, 10);
				}

				cols = minWidth ? Math.floor(parentWidth / minWidth) : options.cols;

				self._itemSize = (parentWidth - (cols - 1) * borderSize) / cols;
				self._itemHeight = parentHeight;
				self._borderSize = 1;

				width = self._itemSize + "px";

				for (i = 0; i < length; i++) {
					elementStyle = listElements[i].style;
					// all without last in raw should have right border
					if (i % cols < cols - 1) {
						elementStyle.borderRightWidth = borderSize + "px";
					}
					// all should have top border
					elementStyle.borderTopWidth = borderSize + "px";
					elementStyle.width = width;
				}

				options.cols = cols;
			};

			/**
			 * Get parent page element
			 * @method _getParentPage
			 * @protected
			 * @param {HTMLElement} element
			 * @return {HTMLElement}
			 * @member ns.widget.mobile.GridView
			 */
			prototype._getParentPage = function (element) {
				while (element && element !== document.body) {
					if (element.getAttribute(DATA_ROLE) === "page" || element.classList.contains("ui-page") === true) {
						return element;
					}
					element = element.parentNode;
				}
				return document.body;
			};

			/**
			 * Toggle grid view reordering mode
			 * @method _setReorder
			 * @protected
			 * @param {HTMLElement} element
			 * @param {boolean} reorder
			 * @member ns.widget.mobile.GridView
			 */
			prototype._setReorder = function (element, reorder) {
				var self = this,
					options = self.options;

				utilsEvents.disableGesture(element);

				if (reorder) {
					utilsEvents.enableGesture(
						element,
						new utilsEvents.gesture.Drag({
							blockVertical: false
						})
					);
					utilsEvents.on(element, "drag dragstart dragend dragcancel dragprepare", self, true);
					utilsEvents.off(element, "pinchin pinchout", self);
					element.classList.add("ui-gridview-reorder");
					// create handlers if not exists
					self._ui.listElements.forEach(function (liItem) {
						var handler = null;

						if (!liItem.querySelector("." + classes.HANDLER)) {
							handler = document.createElement("div");
							handler.classList.add(classes.HANDLER);
							liItem.appendChild(handler);
						}
					});
				} else {
					utilsEvents.enableGesture(
						element,
						new utilsEvents.gesture.Pinch()
					);
					utilsEvents.off(element, "drag dragstart dragend dragcancel dragprepare", self, true);
					utilsEvents.on(element, "pinchin pinchout", self);
					element.classList.remove("ui-gridview-reorder");
				}

				options.reorder = reorder;
			};

			/**
			 * Set style for grid view
			 * @method _setGridStyle
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._setGridStyle = function () {
				var self = this,
					listElements = self._ui.listElements,
					length = listElements.length,
					options = self.options,
					cols = options.cols,
					rows,
					styleElement,
					styles = [],
					index = 0,
					row,
					col;

				styleElement = document.createElement("style");
				styleElement.type = "text/css";

				rows = Math.ceil(length / cols);

				for (row = 0; row < rows; row++) {
					for (col = 0; col < cols && index < length; col++) {
						listElements[index].style.animation = "grid_show_item cubic-bezier(0.25, 0.46, 0.45, 1.00) 350ms " + (17 * index) + "ms";
						styles.push(self._getTransformStyle(col, row, ++index));
					}
				}
				styleElement.textContent = styles.join("\n");
				styleElement.id = "GridView";
				document.head.appendChild(styleElement);
				self._styleElement = styleElement;
			};

			/**
			 * Define transform style for positioning of grid items
			 * @method _getTransformStyle
			 * @protected
			 * @param {number} col
			 * @param {number} row
			 * @param {number} index
			 * @member ns.widget.mobile.GridView
			 */
			prototype._getTransformStyle = function (col, row, index) {
				var size = this._itemSize + this._borderSize,
					x = col * size + "px",
					y = row * (this._itemHeight + this._borderSize) + "px",
					transform,
					style;

				transform = "{ -webkit-transform: translate3d(" + x + ", " + y + ", 0); transform: translate3d(" + x + ", " + y + ", 0) }";
				style = STYLE_PATTERN.replace("{index}", index) + transform;

				return style;
			};

			/**
			 * Remove style node
			 * @method _removeGridStyle
			 * @protected
			 * @member ns.widget.mobile.GridView
			 */
			prototype._removeGridStyle = function () {
				var styleElement = this._styleElement;

				if (styleElement) {
					styleElement.parentNode.removeChild(styleElement);
					this._styleElement = null;
				}
			};

			/**
			 * Add an item to grid view
			 * @method addItem
			 * @public
			 * @param {HTMLElement} item
			 * @member ns.widget.mobile.GridView
			 */
			prototype.addItem = function (item) {
				var self = this,
					listElements = self._ui.listElements,
					styleElement = self._styleElement,
					styles = styleElement.textContent,
					element = self.element,
					cols = self.options.cols,
					col,
					row,
					length,
					firstLiComputed = listElements.length && window.getComputedStyle(listElements[0], null);

				// append item
				item.classList.add(classes.ITEM);
				item.style.width = (parseFloat(firstLiComputed.getPropertyValue("width")) || 0) + "px";
				element.appendChild(item);
				listElements.push(item);

				// calculate item position
				length = listElements.length;
				row = Math.floor((length - 1) / cols);
				col = (length - 1) % cols;

				// add transform style for item added
				styleElement.textContent = styles.concat("\n" + self._getTransformStyle(col, row, length));
			};

			/**
			 * Remove an item from grid view
			 * @method removeItem
			 * @public
			 * @param {HTMLElement} item
			 * @member ns.widget.mobile.GridView
			 */
			prototype.removeItem = function (item) {
				var self = this,
					element = self.element,
					listElements = self._ui.listElements,
					styleElement = self._styleElement,
					styles = styleElement.textContent.split("\n"),
					index;

				index = listElements.indexOf(item);

				if (index > -1) {
					listElements.splice(index, 1);
					element.removeChild(item);
					styles.pop();
					styleElement.textContent = styles.join("\n");
				}
			};

			/**
			 * Set label type for grid view
			 * @method _setLabel
			 * @protected
			 * @param {HTMLElement} element
			 * @param {string} label
			 * @member ns.widget.mobile.GridView
			 */
			prototype._setLabel = function (element, label) {
				var self = this,
					options = self.options,
					labelCheck;

				labelCheck = label || options.label;

				element.classList.remove(classes.LABEL_IN);
				element.classList.remove(classes.LABEL_OUT);

				if (labelCheck === labels.IN) {
					element.classList.add(classes.LABEL_IN);
				} else if (labelCheck === labels.OUT) {
					element.classList.add(classes.LABEL_OUT);
				}

				options.label = labelCheck;
			};

			ns.widget.mobile.GridView = GridView;

			engine.defineWidget(
				"GridView",
				"ul.ui-gridview, ul[data-role='gridview']",
				[],
				GridView,
				"mobile"
			);
			}(window.document, ns));

/*global window, define, ns */
/*jslint plusplus: true, nomen: true */
(function (document, ns) {
	"use strict";
	
			var object = ns.util.object;

			/**
			 * Look to ns.router.Page#open
			 * @method changePage
			 * @inheritdoc ns.router.Page#open
			 * @member ns
			 */
			ns.changePage = function (toPage, options) {
				var router = ns.router.Router.getInstance();

				if (router) {
					router.open(toPage, options);
				}
			};

			/**
			 * Back in history.
			 * @method back
			 * @static
			 * @member ns
			 */
			ns.back = function () {
				window.history.back();
			};

			/**
			 * Look to ns.router.Page#open
			 * @method openPopup
			 * @inheritdoc ns.router.Page#open
			 * @member ns
			 */
			ns.openPopup = function (to, options) {
				var router = ns.router.Router.getInstance();

				if (router) {
					router.open(to, object.merge({}, options, {
						rel: "popup"
					}));
				}
			};

			/**
			 * Close active popup
			 * @method closePopup
			 * @static
			 * @member ns
			 */
			ns.closePopup = function () {
				var activePopup = ns.activePopup;

				if (activePopup) {
					activePopup.close();
				}
			};

			/**
			 * Returns active page element
			 * @inheritdoc ns.router.Router#getActivePageElement
			 * @method getActivePage
			 * @member tau
			 */
			ns.getActivePage = function () {
				var router = ns.router.Router.getInstance();

				if (router) {
					return router.getActivePageElement();
				}
				return null;
			};

			}(document, ns));

/*global window, ns, define, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Route Page
 * Support class for router to control changing pages.
 * @class ns.router.route.page
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (document) {
	"use strict";
				var util = ns.util,
				path = util.path,
				DOM = util.DOM,
				object = util.object,
				utilSelector = util.selectors,
				history = ns.history,
				engine = ns.engine,
				baseElement,
				routePage = {},
				head;

			/**
			 * Tries to find a page element matching id and filter (selector).
			 * Adds data url attribute to found page, sets page = null when nothing found
			 * @method findPageAndSetDataUrl
			 * @param {string} dataUrl DataUrl of searching element
			 * @param {string} filter Query selector for searching page
			 * @return {?HTMLElement}
			 * @private
			 * @static
			 * @member ns.router.route.page
			 */
			function findPageAndSetDataUrl(dataUrl, filter) {
				var id = path.stripQueryParams(dataUrl).replace("#", ""),
					page = document.getElementById(id);

				if (page && utilSelector.matchesSelector(page, filter)) {
					if (dataUrl === id) {
						DOM.setNSData(page, "url", "#" + id);
					} else {
						DOM.setNSData(page, "url", dataUrl);
					}

				} else {
					// if we matched any element, but it doesn't match our filter
					// reset page to null
					page = null;
				}
				return page;
			}

			routePage.orderNumber = 1;
			/**
			 * Property containing default properties
			 * @property {Object} defaults
			 * @property {string} defaults.transition="none"
			 * @static
			 * @member ns.router.route.page
			 */
			routePage.defaults = {
				transition: "none"
			};

			/**
			 * Property defining selector without spaces for filtering only page elements.
			 * @property {string} filter
			 * @member ns.router.route.page
			 * @static
			 */
			routePage.filter = engine.getWidgetDefinition("Page").selector.replace(/(\s*)/g, "");

			/**
			 * Property contains first page element
			 * @property {?HTMLElement} firstPage
			 * @member ns.router.route.page
			 * @static
			 */
			routePage.firstPage = null;

			/**
			 * Returns default route options used inside Router.
			 * @method option
			 * @static
			 * @member ns.router.route.page
			 * @return {Object} default route options
			 */
			routePage.option = function () {
				var defaults = object.merge({}, routePage.defaults);

				defaults.transition = ns.getConfig("pageTransition", defaults.transition);
				return defaults;
			};

			routePage.init = function () {
				var pages = [].slice.call(document.querySelectorAll(this.filter));

				pages.forEach(function (page) {
					if (!DOM.getNSData(page, "url")) {
						DOM.setNSData(page, "url",
							(page.id && "#" + page.id) || location.pathname + location.search);
					}
				});
			};

			/**
			 * This method changes page. It sets history and opens page passed as a parameter.
			 * @method open
			 * @param {HTMLElement|string} toPage The page which will be opened.
			 * @param {Object} [options]
			 * @param {boolean} [options.fromHashChange] Sets if call was made on hash change.
			 * @param {string} [options.dataUrl] Sets if page has url attribute.
			 * @member ns.router.route.page
			 */
			routePage.open = function (toPage, options) {
				var pageTitle = document.title,
					url,
					state;

				if (toPage === this.getFirstElement() && !options.dataUrl) {
					url = path.documentUrl.hrefNoHash;
				} else {
					url = DOM.getNSData(toPage, "url");
				}

				// if no url is set, apply the address of chosen page to data-url attribute
				// and use it as url, as this is needed for history state
				if (!url && options.href) {
					url = options.href;
					DOM.setNSData(toPage, "url", url);
				}

				pageTitle = DOM.getNSData(toPage, "title") ||
					utilSelector.getChildrenBySelector(toPage, ".ui-header > .ui-title").textContent ||
					pageTitle;
				if (!DOM.getNSData(toPage, "title")) {
					DOM.setNSData(toPage, "title", pageTitle);
				}

				if (url && !options.fromHashChange) {
					if (!path.isPath(url) && url.indexOf("#") < 0) {
						url = path.makeUrlAbsolute("#" + url, path.documentUrl.hrefNoHash);
					}

					state = object.merge(
						{},
						options,
						{
							url: url
						}
					);

					history.replace(state, pageTitle, url);
				}

				// write base element
				this._setBase(url);

				//set page title
				document.title = pageTitle;
				this.active = true;
				this.getContainer().change(toPage, options);

			};

			/**
			 * This method determines target page to open.
			 * @method find
			 * @param {string} absUrl Absolute path to opened page
			 * @member ns.router.route.page
			 * @return {?HTMLElement} Element of page to open.
			 */
			routePage.find = function (absUrl) {
				var self = this,
					router = ns.router.Router.getInstance(),
					dataUrl = self._createDataUrl(absUrl),
					initialContent = self.getFirstElement(),
					pageContainer = router.getContainer(),
					page,
					selector = "[data-url='" + dataUrl + "']",
					filterRegexp = /,/gm;

				if (/#/.test(absUrl) && path.isPath(dataUrl)) {
					return null;
				}

				// Check to see if the page already exists in the DOM.
				// NOTE do _not_ use the :jqmData pseudo selector because parenthesis
				//      are a valid url char and it breaks on the first occurrence
				// prepare selector for new page
				selector += self.filter.replace(filterRegexp, ",[data-url='" + dataUrl + "']");
				page = pageContainer.element.querySelector(selector);

				// If we failed to find the page, check to see if the url is a
				// reference to an embedded page. If so, it may have been dynamically
				// injected by a developer, in which case it would be lacking a
				// data-url attribute and in need of enhancement.
				if (!page && dataUrl && !path.isPath(dataUrl)) {
					//Remove search data
					page = findPageAndSetDataUrl(dataUrl, self.filter);
				}

				// If we failed to find a page in the DOM, check the URL to see if it
				// refers to the first page in the application. Also check to make sure
				// our cached-first-page is actually in the DOM. Some user deployed
				// apps are pruning the first page from the DOM for various reasons.
				// We check for this case here because we don't want a first-page with
				// an id falling through to the non-existent embedded page error case.
				if (!page &&
					path.isFirstPageUrl(dataUrl, self.getFirstElement()) &&
					initialContent) {
					page = initialContent;
				}

				return page;
			};

			/**
			 * This method parses HTML and runs scripts from parsed code.
			 * Fetched external scripts if required.
			 * Sets document base to parsed document absolute path.
			 * @method parse
			 * @param {string} html HTML code to parse
			 * @param {string} absUrl Absolute url for parsed page
			 * @member ns.router.route.page
			 * @return {?HTMLElement} Element of page in parsed document.
			 */
			routePage.parse = function (html, absUrl) {
				var self = this,
					page,
					dataUrl = self._createDataUrl(absUrl);

				// write base element
				self._setBase(absUrl);

				// Finding matching page inside created element
				page = html.querySelector(self.filter);

				// If a page exists...
				if (page) {
					DOM.setNSData(page, "url", dataUrl);
					DOM.setNSData(page, "external", true);
				}
				return page;
			};

			/**
			 * This method handles hash change, **currently does nothing**.
			 * @method onHashChange
			 * @static
			 * @member ns.router.route.page
			 * @return {null}
			 */
			routePage.onHashChange = function (/* url, options */) {
				return null;
			};

			/**
			 * This method creates data url from absolute url given as argument.
			 * @method _createDataUrl
			 * @param {string} absoluteUrl
			 * @protected
			 * @static
			 * @member ns.router.route.page
			 * @return {string}
			 */
			routePage._createDataUrl = function (absoluteUrl) {
				return path.convertUrlToDataUrl(absoluteUrl, true);
			};

			/**
			 * On open fail, currently never used
			 * @method onOpenFailed
			 * @member ns.router.route.page
			 */
			routePage.onOpenFailed = function (/* options */) {
				this._setBase(path.parseLocation().hrefNoSearch);
			};

			/**
			 * This method returns base element from document head.
			 * If no base element is found, one is created based on current location.
			 * @method _getBaseElement
			 * @protected
			 * @static
			 * @member ns.router.route.page
			 * @return {HTMLElement}
			 */
			routePage._getBaseElement = function () {
				// Fetch document head if never cached before
				if (!head) {
					head = document.querySelector("head");
				}
				// Find base element
				if (!baseElement) {
					baseElement = document.querySelector("base");
					if (!baseElement) {
						baseElement = document.createElement("base");
						baseElement.href = path.documentBase.hrefNoHash;
						head.appendChild(baseElement);
					}
				}
				return baseElement;
			};

			/**
			 * Sets document base to url given as argument
			 * @method _setBase
			 * @param {string} url
			 * @protected
			 * @member ns.router.route.page
			 */
			routePage._setBase = function (url) {
				var base = this._getBaseElement(),
					baseHref = base.href;

				if (path.isPath(url)) {
					url = path.makeUrlAbsolute(url, path.documentBase);
					if (path.parseUrl(baseHref).hrefNoSearch !== path.parseUrl(url).hrefNoSearch) {
						base.href = url;
						path.documentBase = path.parseUrl(path.makeUrlAbsolute(url, path.documentUrl.href));
					}
				}
			};

			/**
			 * Returns container of pages
			 * @method getContainer
			 * @return {?ns.widget.core.Page}
			 * @member ns.router.route.page
			 * @static
			 */
			routePage.getContainer = function () {
				return ns.router.Router.getInstance().getContainer();
			};

			/**
			 * Returns active page.
			 * @method getActive
			 * @return {?ns.widget.core.Page}
			 * @member ns.router.route.page
			 * @static
			 */
			routePage.getActive = function () {
				return this.getContainer().getActivePage();
			};

			/**
			 * Returns element of active page.
			 * @method getActiveElement
			 * @return {HTMLElement}
			 * @member ns.router.route.page
			 * @static
			 */
			routePage.getActiveElement = function () {
				return this.getActive().element;
			};

			/**
			 * Method returns ths first page.
			 * @method getFirstElement
			 * @return {HTMLElement} the first page
			 * @member ns.router.route.page
			 */
			routePage.getFirstElement = function () {
				return this.firstPage;
			};

			/**
			 * Method sets ths first page.
			 * @method setFirstElement
			 * @param {HTMLElement} firstPage the first page
			 * @member ns.router.route.page
			 */
			routePage.setFirstElement = function (firstPage) {
				this.firstPage = firstPage;
			};

			ns.router.route.page = routePage;

			}(window.document));

/*global window, define, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Route Popup
 * Support class for router to control changing popups.
 * @class ns.router.route.popup
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Damian Osipiuk <d.osipiuk@samsung.com>
 */
(function (window, document, ns) {
	"use strict";
				var
				/**
				 * @property {Object} Popup Alias for {@link ns.widget.Popup}
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				Popup = ns.widget.core.Popup,
				util = ns.util,
				routePopup = {
					/**
					 * Object with default options
					 * @property {Object} defaults
					 * @property {string} [defaults.transition='none'] Sets the animation used during change
					 * of popup.
					 * @property {?HTMLElement} [defaults.container=null] Sets container of element.
					 * @property {boolean} [defaults.volatileRecord=true] Sets if the current history entry
					 * will be modified or a new one will be created.
					 * @member ns.router.route.popup
					 * @static
					 */
					defaults: {
						transition: "none",
						container: null,
						volatileRecord: true
					},
					/**
					 * Popup Element Selector
					 * @property {string} filter
					 * @member ns.router.route.popup
					 * @static
					 */
					filter: "." + Popup.classes.popup,
					/**
					 * Storage variable for active popup
					 * @property {?HTMLElement} activePopup
					 * @member ns.router.route.popup
					 * @static
					 */
					activePopup: null,
					/**
					 * Dictionary for popup related event types
					 * @property {Object} events
					 * @property {string} [events.POPUP_HIDE='popuphide']
					 * @member ns.router.route.popup
					 * @static
					 */
					events: {
						POPUP_HIDE: "popuphide"
					},

					/**
					 * Alias for {@link ns.util.path}
					 * @property {Object} path
					 * @member ns.router.route.popup
					 * @protected
					 * @static
					 */
					_path: ns.util.path,
					/**
					 * Alias for {@link ns.router.history}
					 * @property {Object} history
					 * @member ns.router.route.popup
					 * @protected
					 * @static
					 */
					_history: ns.history
				},
				/**
				 * Alias for {@link ns.engine}
				 * @property {Object} engine
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				engine = ns.engine,
				/**
				 * Alias for {@link ns.util.selectors}
				 * @property {Object} utilSelector
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				utilSelector = ns.util.selectors,
				/**
				 * Alias for {@link ns.util.DOM}
				 * @property {Object} DOM
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				DOM = ns.util.DOM,
				/**
				 * Alias for Object utils
				 * @method slice
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				object = ns.util.object,
				/**
				 * Popup's hash added to url
				 * @property {string} popupHashKey
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				popupHashKey = "popup=true",
				/**
				 * Regexp for popup's hash
				 * @property {RegExp} popupHashKeyReg
				 * @member ns.router.route.popup
				 * @private
				 * @static
				 */
				popupHashKeyReg = /([&|\?]popup=true)/;

			/**
			 * Tries to find a popup element matching id and filter (selector).
			 * Adds data url attribute to found page, sets page = null when nothing found.
			 * @method findPopupAndSetDataUrl
			 * @param {string} id
			 * @param {string} filter
			 * @return {HTMLElement}
			 * @member ns.router.route.popup
			 * @private
			 * @static
			 */
			function findPopupAndSetDataUrl(id, filter) {
				var popup,
					hashReg = /^#/;

				id = id.replace(hashReg, "");
				popup = document.getElementById(id);

				if (popup && utilSelector.matchesSelector(popup, filter)) {
					DOM.setNSData(popup, "url", "#" + id);
				} else {
					// if we matched any element, but it doesn't match our filter
					// reset page to null
					popup = null;
				}
				// probably there is a need for running onHashChange while going back to a history entry
				// without state, eg. manually entered #fragment. This may not be a problem on target device
				return popup;
			}

			routePopup.orderNumber = 100;
			/**
			 * This method returns default options for popup router.
			 * @method option
			 * @return {Object}
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.option = function () {
				var defaults = object.merge({}, routePopup.defaults);

				defaults.transition = ns.getConfig("popupTransition", defaults.transition);
				return defaults;
			};

			/**
			 * This method sets active popup and manages history.
			 * @method setActive
			 * @param {?ns.widget.core.popup} activePopup
			 * @param {Object} options
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.setActive = function (activePopup, options) {
				var url,
					pathLocation = routePopup._path.getLocation(),
					documentUrl = pathLocation.replace(popupHashKeyReg, "");

				this.activePopup = activePopup;

				if (activePopup) {
					// If popup is being opened, the new state is added to history.
					if (options && !options.fromHashChange && options.history) {
						url = routePopup._path.addHashSearchParams(documentUrl, popupHashKey);
						routePopup._history.replace(options, "", url);
						this.active = true;
					}
				} else if (pathLocation !== documentUrl) {
					// If popup is being closed, the history.back() is called
					// but only if url has special hash.
					// Url is changed after opening animation and in some cases,
					// the popup is closed before this animation and then the history.back
					// could cause undesirable change of page.
					this.active = false;
					routePopup._history.back();
				}
			};

			/**
			 * This method opens popup if no other popup is opened.
			 * It also changes history to show that popup is opened.
			 * If there is already active popup, it will be closed.
			 * @method open
			 * @param {HTMLElement|string} toPopup
			 * @param {Object} options
			 * @param {"page"|"popup"|"external"} [options.rel = 'popup'] Represents kind of link as
			 * 'page' or 'popup' or 'external' for linking to another domain.
			 * @param {string} [options.transition = 'none'] Sets the animation used during change of
			 * popup.
			 * @param {boolean} [options.reverse = false] Sets the direction of change.
			 * @param {boolean} [options.fromHashChange = false] Sets if will be changed after hashchange.
			 * @param {boolean} [options.showLoadMsg = true] Sets if message will be shown during loading.
			 * @param {number} [options.loadMsgDelay = 0] Sets delay time for the show message during
			 * loading.
			 * @param {boolean} [options.dataUrl] Sets if page has url attribute.
			 * @param {string} [options.container = null] Selector for container.
			 * @param {boolean} [options.volatileRecord=true] Sets if the current history entry will be
			 * modified or a new one will be created.
			 * @param {Event} event
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.open = function (toPopup, options, event) {
				var self = this,
					popup,
					router = ns.router.Router.getInstance(),
					events = self.events,
					removePopup = function () {
						document.removeEventListener(events.POPUP_HIDE, removePopup, false);
						toPopup.parentNode.removeChild(toPopup);
						self.activePopup = null;
					},
					openPopup = function () {
						var positionTo = options["position-to"],
							touch;
						// add such option only if it exists

						if (positionTo) {
							options.positionTo = positionTo;
						}
						if (event) {
							touch = event.touches ? event.touches[0] : event;
							options.x = touch.clientX;
							options.y = touch.clientY;
						}

						document.removeEventListener(events.POPUP_HIDE, openPopup, false);
						popup = engine.instanceWidget(toPopup, "Popup", options);
						popup.open(options);
						self.activePopup = popup;
						self.active = popup.options.history;
					},
					activePage = router.container.getActivePage(),
					container;

				if (DOM.getNSData(toPopup, "external") === true) {
					container = options.container ?
						activePage.element.querySelector(options.container) : activePage.element;
					if (toPopup.parentNode !== container) {
						toPopup = util.importEvaluateAndAppendElement(toPopup, container);
					}
					document.addEventListener(routePopup.events.POPUP_HIDE, removePopup, false);
				}

				if (self.hasActive()) {
					document.addEventListener(events.POPUP_HIDE, openPopup, false);
					if (!self.close()) {
						openPopup();
					}
				} else {
					openPopup();
				}
			};

			/**
			 * This method closes active popup.
			 * @method close
			 * @param {ns.widget.core.Popup} [activePopup]
			 * @param {Object} options
			 * @param {string} [options.transition]
			 * @param {string} [options.ext= in ui-pre-in] options.ext
			 * @member ns.router.route.popup
			 * @protected
			 * @static
			 */
			routePopup.close = function (activePopup, options) {
				var popupOptions,
					pathLocation = routePopup._path.getLocation(),
					documentUrl = pathLocation.replace(popupHashKeyReg, "");

				options = options || {};

				if (activePopup && !(activePopup instanceof Popup)) {
					activePopup = engine.instanceWidget(activePopup, "Popup", options);
				}
				activePopup = activePopup || this.activePopup;

				// if popup is active
				if (activePopup) {
					popupOptions = activePopup.options;
					// we check if it changed the history
					if (popupOptions.history && pathLocation !== documentUrl) {
						// and then set new options for popup
						popupOptions.transition = options.transition || popupOptions.transition;
						popupOptions.ext = options.ext || popupOptions.ext;
						// unlock the router if it was locked
						if (!popupOptions.dismissible) {
							ns.router.Router.getInstance().unlock();
						}
						// and call history.back()
						routePopup._history.back();
					} else {
						// if popup did not change the history, we close it normally
						activePopup.close(options);
					}
					return true;
				}
				return false;
			};

			/**
			 * This method handles hash change.
			 * It closes opened popup.
			 * @method onHashChange
			 * @param {string} url
			 * @param {Object} options
			 * @return {boolean}
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.onHashChange = function (url, options) {
				var activePopup = this.activePopup;

				if (activePopup) {
					activePopup.close(options);
					// Default routing setting cause to rewrite further window history
					// even if popup has been closed
					// To prevent this onHashChange after closing popup we need to change
					// disable volatile mode to allow pushing new history elements
					if (this.active) {
						this.active = false;
						return true;
					}
				}
				return false;
			};

			/**
			 * On open fail, currently never used
			 * @method onOpenFailed
			 * @member ns.router.route.popup
			 * @return {null}
			 * @static
			 */
			routePopup.onOpenFailed = function (/* options */) {
				return null;
			};

			/**
			 * This method finds popup by data-url.
			 * @method find
			 * @param {string} absUrl Absolute path to opened popup
			 * @return {HTMLElement} Element of popup
			 * @member ns.router.route.popup
			 */
			routePopup.find = function (absUrl) {
				var self = this,
					dataUrl = self._createDataUrl(absUrl),
					activePage = ns.router.Router.getInstance().getContainer().getActivePage(),
					popup;

				popup = activePage.element.querySelector("[data-url='" + dataUrl + "']" + self.filter);

				if (!popup && dataUrl && !routePopup._path.isPath(dataUrl)) {
					popup = findPopupAndSetDataUrl(dataUrl, self.filter);
				}

				return popup;
			};

			/**
			 * This method parses HTML and runs scripts from parsed code.
			 * Fetched external scripts if required.
			 * @method parse
			 * @param {string} html HTML code to parse
			 * @param {string} absUrl Absolute url for parsed popup
			 * @return {HTMLElement}
			 * @member ns.router.route.popup
			 */
			routePopup.parse = function (html, absUrl) {
				var self = this,
					popup,
					dataUrl = self._createDataUrl(absUrl);

				popup = html.querySelector(self.filter);

				if (popup) {
					// TODO tagging a popup with external to make sure that embedded popups aren't
					// removed by the various popup handling code is bad. Having popup handling code
					// in many places is bad. Solutions post 1.0
					DOM.setNSData(popup, "url", dataUrl);
					DOM.setNSData(popup, "external", true);
				}

				return popup;
			};

			/**
			 * Convert url to data-url
			 * @method _createDataUrl
			 * @param {string} absoluteUrl
			 * @return {string}
			 * @member ns.router.route.popup
			 * @protected
			 * @static
			 */
			routePopup._createDataUrl = function (absoluteUrl) {
				return routePopup._path.convertUrlToDataUrl(absoluteUrl);
			};

			/**
			 * Return true if active popup exists.
			 * @method hasActive
			 * @return {boolean}
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.hasActive = function () {
				return this.active;
			};

			/**
			 * Returns active popup.
			 * @method getActive
			 * @return {?ns.widget.core.Popup}
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.getActive = function () {
				return this.activePopup;
			};

			/**
			 * Returns element of active popup.
			 * @method getActiveElement
			 * @return {HTMLElement}
			 * @member ns.router.route.popup
			 * @static
			 */
			routePopup.getActiveElement = function () {
				var active = this.getActive();

				return active && active.element;
			};

			ns.router.route.popup = routePopup;

			}(window, window.document, ns));

/*global window, ns, define */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Route Drawer
 * Support class for router to control drawer widget in profile Wearable.
 * @class ns.router.route.drawer
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function () {
	"use strict";
				var CoreDrawer = ns.widget.core.Drawer,
				Router = ns.router.Router,
				path = ns.util.path,
				history = ns.history,
				engine = ns.engine,
				routeDrawer = {},
				drawerHashKey = "drawer=true",
				drawerHashKeyReg = /([&|\?]drawer=true)/;

			routeDrawer.orderNumber = 1000;
			/**
			 * Property containing default properties
			 * @property {Object} defaults
			 * @property {string} defaults.transition="none"
			 * @static
			 * @member ns.router.route.drawer
			 */
			routeDrawer.defaults = {
				transition: "none"
			};

			/**
			 * Property defining selector for filtering only drawer elements
			 * @property {string} filter
			 * @member ns.router.route.drawer
			 * @static
			 */
			routeDrawer.filter = "." + CoreDrawer.classes.drawer;


			/**
			 * Returns default route options used inside Router.
			 * But, drawer router has not options.
			 * @method option
			 * @static
			 * @member ns.router.route.drawer
			 * @return {null}
			 */
			routeDrawer.option = function () {
				return null;
			};

			/**
			 * This method opens the drawer.
			 * @method open
			 * @param {HTMLElement} drawerElement
			 * @member ns.router.route.drawer
			 */
			routeDrawer.open = function (drawerElement) {
				var drawer = engine.instanceWidget(drawerElement, "Drawer");

				drawer.open();
			};

			/**
			 * This method determines target drawer to open.
			 * @method find
			 * @param {string} absUrl Absolute path to opened drawer widget
			 * @member ns.router.route.drawer
			 * @return {?HTMLElement} drawerElement
			 */
			routeDrawer.find = function (absUrl) {
				var dataUrl = path.convertUrlToDataUrl(absUrl),
					activePage = Router.getInstance().getContainer().getActivePage(),
					drawer;

				drawer = activePage.element.querySelector("#" + dataUrl);

				return drawer;
			};

			/**
			 * This method parses HTML and runs scripts from parsed code.
			 * But, drawer router doesn't need to that.
			 * @method parse
			 * @member ns.router.route.drawer
			 */
			routeDrawer.parse = function () {
				return null;
			};

			/**
			 * This method sets active drawer and manages history.
			 * @method setActive
			 * @param {Object} activeDrawer
			 * @member ns.router.route.drawer
			 * @static
			 */
			routeDrawer.setActive = function (activeDrawer) {
				var url,
					pathLocation = path.getLocation(),
					documentUrl = pathLocation.replace(drawerHashKeyReg, "");

				this._activeDrawer = activeDrawer;

				if (activeDrawer) {
					url = path.addHashSearchParams(documentUrl, drawerHashKey);
					history.replace({}, "", url);
					this.active = true;
				} else if (pathLocation !== documentUrl) {
					history.back();
				}
			};

			/**
			 * This method handles hash change.
			 * @method onHashChange
			 * @param {string} url
			 * @param {Object} options
			 * @param {string} prev Previous url string
			 * @static
			 * @member ns.router.route.drawer
			 * @return {null}
			 */
			routeDrawer.onHashChange = function (url, options, prev) {
				var self = this,
					activeDrawer = self._activeDrawer,
					stateUrl = prev.stateUrl;

				if (activeDrawer && stateUrl.search(drawerHashKey) > 0 && url.search(drawerHashKey) < 0) {
					activeDrawer.close(options);
					this.active = false;
					return true;
				}
				return false;
			};

			ns.router.route.drawer = routeDrawer;

			}());

/*global window, ns, define, localStorage, ns */
/*jslint nomen: true */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * #Route panel
 * Support class for router to control panel widget in profile Wearable.
 * @class ns.router.route.panel
 * @author Hyeoncheol Choi <hc7.choi@samsung.com>
 */
(function (document, ns) {
	"use strict";
				var panelChanger = ns.widget.core.PanelChanger,
				selectors = ns.util.selectors,
				history = ns.history,
				engine = ns.engine,
				classes = {
					PANEL_CHANGER: panelChanger.classes.PANEL_CHANGER
				},
				CONST = {
					REVERSE: "slide-reverse"
				},
				routePanel = {};

			routePanel.orderNumber = 10;

			/**
			 * Returns default route options used inside Router.
			 * But, panel router has not options.
			 * @method option
			 * @static
			 * @member ns.router.route.panel
			 * @return {null}
			 */
			routePanel.option = function () {
				return null;
			};

			/**
			 * This method sets active panel and manages history.
			 * @method setActive
			 * @param {HTMLElement} element
			 * @member ns.router.route.panel
			 * @static
			 */
			routePanel.setActive = function (element) {
				var self = this,
					panelChangerElement = selectors.getClosestByClass(element, classes.PANEL_CHANGER),
					panelChangerComponent = engine.instanceWidget(panelChangerElement, "PanelChanger");

				self.active = true;
				self._panelChangerElement = panelChangerElement;
				self._panelChangerComponent = panelChangerComponent;
			};

			/**
			 * This method handles hash change.
			 * @method onHashChange
			 * @param {string} url
			 * @param {Object} options
			 * @param {string} prev
			 * @static
			 * @member ns.router.route.panel
			 * @return {boolean}
			 */
			routePanel.onHashChange = function (url, options, prev) {
				var self = this,
					storageName = panelChanger.default.STORAGE_NAME,
					panelHistory = JSON.parse(localStorage[storageName] || "[]"),
					panelChangerComponent = self._panelChangerComponent,
					activePanel = panelHistory[panelHistory.length - 1];

				if (!self.active || !panelChangerComponent) {
					return false;
				} if (self._panelChangerElement.querySelector("#" + activePanel).classList.contains(panelChanger.classes.PRE_IN) ||
						panelHistory.length === 0) {
					history.replace(prev, prev.stateTitle, prev.stateUrl);
					return true;
				}
				panelHistory.pop();
				if (panelChangerComponent.options.manageHistory && panelHistory.length > 0) {
					history.replace(prev, prev.stateTitle, prev.stateUrl);
					localStorage[storageName] = JSON.stringify(panelHistory);
					panelChangerComponent.changePanel("#" + panelHistory.pop(), CONST.REVERSE, "back");
					return true;
				}
				self.active = false;
				return false;
			};

			ns.router.route.panel = routePanel;

			}(window.document, ns));

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global window, ns, define, ns */
/**
 * @class tau.expose
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function (document) {
	"use strict";
	
			document.addEventListener("beforerouterinit", function () {
				if (ns.autoInitializePage !== undefined) {
					ns.setConfig("autoInitializePage", ns.autoInitializePage);
				}
			}, false);

			document.addEventListener("routerinit", function (evt) {
				var router = evt.detail,
					utilObject = ns.util.object,
					routePage = router.getRoute("page"),
					routePopup = router.getRoute("popup"),
					history = ns.history,
					back = history.back.bind(router),
					classes = ns.widget.core.Page.classes,
					pageActiveClass = classes.uiPageActive;
				/**
				 * @method changePage
				 * @inheritdoc ns.router.Router#open
				 * @member tau
				 */

				ns.changePage = router.open.bind(router);
				document.addEventListener("pageshow", function () {
					/**
					 * Current active page
					 * @property {HTMLElement} activePage
					 * @member tau
					 */
					ns.activePage = document.querySelector("." + pageActiveClass);
				});
				/**
				 * First page element
				 * @inheritdoc ns.router.Router#firstPage
				 * @property {HTMLElement} firstPage
				 * @member tau
				 */
				ns.firstPage = routePage.getFirstElement();
				/**
				 * Returns active page element
				 * @inheritdoc ns.router.Router#getActivePageElement
				 * @method getActivePage
				 * @member tau
				 */
				ns.getActivePage = routePage.getActiveElement.bind(routePage);
				/**
				 * @inheritdoc ns.router.history#back
				 * @method back
				 * @member tau
				 */
				ns.back = back;
				/**
				 * @inheritdoc ns.router.Router#init
				 * @method initializePage
				 * @member tau
				 */
				ns.initializePage = router.init.bind(router);
				/**
				 * Page Container widget
				 * @property {HTMLElement} pageContainer
				 * @inheritdoc ns.router.Router#container
				 * @member tau
				 */
				ns.pageContainer = router.container;
				/**
				 * @method openPopup
				 * @inheritdoc ns.router.Router#openPopup
				 * @member tau
				 */
				ns.openPopup = function (to, options) {
					var htmlElementTo;

					if (to && to.length !== undefined && typeof to === "object") {
						htmlElementTo = to[0];
					} else {
						htmlElementTo = to;
					}
					options = utilObject.merge({}, options, {rel: "popup"});
					router.open(htmlElementTo, options);
				};
				/**
				 * @method closePopup
				 * @inheritdoc ns.router.Router#closePopup
				 * @member tau
				 */
				ns.closePopup = routePopup.close.bind(routePopup, null);

			}, false);

			}(window.document));

/*global define, XMLHttpRequest, ns*/
/**
 * #HTML template engine
 *
 * Parser for HTML files.
 *
 * This class hasn't public interface. This class is registered as template engine
 * in template manager.
 *
 * This engine give support of load HTML files for give URL.
 *
 * @class ns.template.html
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 */
(function () {
	"use strict";
				var template = ns.template,
				util = ns.util,
				utilPath = util.path;

			/**
			 * Callback for event load and error on  XMLHttpRequest
			 * @param {Function} callback Function called after parse response
			 * @param {Object} data Data passed to render function
			 * @param {Event} event event object
			 */
			function callbackFunction(callback, data, event) {
				var request = event.target,
					status = {},
					element;

				if (request.readyState === 4) {
					status.success = (request.status === 200 || (request.status === 0 && request.responseXML));
					element = request.responseXML;
					// if option fullDocument is set then return document element
					// Router require full document
					if (!data.fullDocument) {
						// otherwise return first child of body
						// controller require only element
						element = element.body.firstChild;
					}
					callback(status, element);
				}
			}

			/**
			 * Function process given path, get file by XMLHttpRequest and return
			 * HTML element.
			 * @param {Object} globalOptions
			 * @param {string} path
			 * @param {Object} data
			 * @param {Function} callback
			 */
			function htmlTemplate(globalOptions, path, data, callback) {
				var absUrl = path,
					request,
					eventCallback = callbackFunction.bind(null, callback, data);

				// If the caller provided data append the data to the URL.
				if (data) {
					absUrl = utilPath.addSearchParams(path, data);
				}

				// Load the new content.
				request = new XMLHttpRequest();
				request.responseType = "document";
				request.overrideMimeType("text/html");
				request.open("GET", absUrl);
				request.addEventListener("error", eventCallback);
				request.addEventListener("load", eventCallback);
				request.send();
			}

			template.register("html", htmlTemplate);
			}());

/*global define, ns, window */
/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * @author Maciej Urbanski <m.urbanski@samsung.com>
 * @author Krzysztof Antoszek <k.antoszek@samsung.com>
 */
(function (ns) {
	"use strict";
				if (ns.getConfig("autorun", true) === true) {
				ns.engine.run();
			}
			}(ns));

/*global define, ns */
/**
 * #Tizen Advanced UI Framework
 *
 * Tizen Advanced UI Framework(TAU) is new name of Tizen Web UI framework. It provides tools, such as widgets, events, effects, and animations, for Web application development. You can leverage these tools by just selecting the required screen elements and creating applications.
 *
 * TAU service is based on a template and works on a Web browser, which runs on the WebKit engine. You can code Web applications using the TAU, standard HTML5, and Tizen device APIs. You can also use different widgets with CSS animations and rendering optimized for Tizen Web browsers.
 *
 * For more information about the basic structure of a page in the Web application using the TAU, see [Application Page Structure](page/app_page_layout.htm).
 *
 * ##Framework Services
 *
 * The Web UI framework consists of the following services:
 *
 *  - Page navigation
 *
 *    Navigation JavaScript library is provided to allow smooth navigation between TAU based application [pages](page/layout.htm).
 *  - Web widgets and themes
 *
 *    We support APIs and CSS themes for Tizen web [widgets](widget/widget_reference.htm)
 *  - Element Events
 *
 *    Some special [events](event/event_reference.htm) are available with TAU that optimized for the Web applications.
 *  - Useful utility
 *
 *    Some special [utility](util/util_reference.htm) are available with TAU that supporting easy DOM methods for the Web applications.
 *
 * !!!The framework runs only on browsers supporting the HTML5/CSS standards. The draft version of the W3C specification is not fully supported.!!!
 * @class ns.mobile
 * @title Tizen Advanced UI Framework
 */
			ns.info.profile = "mobile";
			
}(window, window.document));
