<commit_before>/* ==========================================================
 * Dropdown.js v1.1.0
 * ==========================================================
 * Copyright 2012 xsokev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

define([
    './Support',
    "dojo/_base/declare",
    "dojo/query",
    "dojo/_base/lang",
    'dojo/_base/window',
    'dojo/on',
    'dojo/dom-class',
    "dojo/dom-attr",
    "dojo/NodeList-dom",
    'dojo/NodeList-traverse',
    "dojo/domReady!"
], function (support, declare, query, lang, win, on, domClass, domAttr) {
    "use strict";

    var toggleSelector = '[data-toggle=dropdown]';
    var Dropdown = declare([], {
        defaultOptions:{},
        constructor:function (element, options) {
            this.options = lang.mixin(lang.clone(this.defaultOptions), (options || {}));
            var el = query(element).closest(toggleSelector);
            if (!el[0]) {
                el = query(element);
            }
            if (el) {
                domAttr.set(el[0], "data-toggle", "dropdown");
            }
        },
        toggle: function(e){
            if (domClass.contains(this, "disabled") || domAttr.get(this, "disabled")) {
                return false;
            }
            var targetNode = _getParent(this)[0];
            if (targetNode) {
                var isActive = domClass.contains(targetNode, 'open');
                clearMenus();
                if (!isActive) {
                    domClass.toggle(targetNode, 'open');
                    this.focus();
                }
            }

            e.preventDefault();
            e.stopPropagation();
            return false;
        },
        keydown: function(e) {
            if (!/(38|40|27)/.test(e.keyCode)) { return; }

            e.preventDefault();
            e.stopPropagation();

            if (domClass.contains(this, "disabled") || domAttr.get(this, "disabled")) {
                return false;
            }
            var targetNode = _getParent(this)[0];
            if (targetNode) {
                var isActive = domClass.contains(targetNode, 'open');
                if (!isActive || (isActive && e.keyCode === 27)) {
                    return on.emit(targetNode, 'click', { bubbles:true, cancelable:true });
                }
            }
            var items = query('[role=menu] li:not(.divider) a', targetNode);
            if (!items.length) { return; }
            var index = items.indexOf(document.activeElement);

            if (e.keyCode === 38 && index > 0) { index--; }
            if (e.keyCode === 40 && index < items.length - 1) { index++; }
            if (index < 0) { index = 0; }

            if (items[index]) {
                items[index].focus();
            }
        }
    });

    function clearMenus() {
        _getParent(query(toggleSelector)).removeClass('open');
    }

    function _getParent(node){
        var selector = domAttr.get(node, 'data-target');
        if (!selector) {
            selector = domAttr.get(node, "href");
            selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
        }
        var parentNode = query(node).parent();
        if (selector && selector !== '#' && selector !== '') {
            parentNode = query(selector).parent();
        }
        return parentNode;
    }

    lang.extend(query.NodeList, {
        dropdown:function (option) {
            var options = (lang.isObject(option)) ? option : {};
            return this.forEach(function (node) {
                var data = support.getData(node, 'dropdown');
                if (!data) {
                    support.setData(node, 'dropdown', (data = new Dropdown(node, options)));
                }
                if (lang.isString(option) && lang.exists(data[option])) {
                    data[option].call(data);
                }
            });
        }
    });
    query('html').on('click, touchstart', clearMenus);
    on(win.body(), on.selector(toggleSelector, 'click, touchstart'), Dropdown.prototype.toggle);
    //TODO: this is causing the dropdown still visible when the user clicks it. This behaviour doesn't seem ideal
    on(win.body(), on.selector('.dropdown', 'click, touchstart'), function (e) {
        e.stopPropagation();
    });
    on(win.body(), on.selector(toggleSelector+', [role=menu]', 'keydown, touchstart'), Dropdown.prototype.keydown);

    return Dropdown;
});<commit_msg>Fixes issue #23. Added form to selector for onclick event<commit_after>/* ==========================================================
 * Dropdown.js v1.1.0
 * ==========================================================
 * Copyright 2012 xsokev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

define([
    './Support',
    "dojo/_base/declare",
    "dojo/query",
    "dojo/_base/lang",
    'dojo/_base/window',
    'dojo/on',
    'dojo/dom-class',
    "dojo/dom-attr",
    "dojo/NodeList-dom",
    'dojo/NodeList-traverse',
    "dojo/domReady!"
], function (support, declare, query, lang, win, on, domClass, domAttr) {
    "use strict";

    var toggleSelector = '[data-toggle=dropdown]';
    var Dropdown = declare([], {
        defaultOptions:{},
        constructor:function (element, options) {
            this.options = lang.mixin(lang.clone(this.defaultOptions), (options || {}));
            var el = query(element).closest(toggleSelector);
            if (!el[0]) {
                el = query(element);
            }
            if (el) {
                domAttr.set(el[0], "data-toggle", "dropdown");
            }
        },
        toggle: function(e){
            if (domClass.contains(this, "disabled") || domAttr.get(this, "disabled")) {
                return false;
            }
            var targetNode = _getParent(this)[0];
            if (targetNode) {
                var isActive = domClass.contains(targetNode, 'open');
                clearMenus();
                if (!isActive) {
                    domClass.toggle(targetNode, 'open');
                    this.focus();
                }
            }

            e.preventDefault();
            e.stopPropagation();
            return false;
        },
        keydown: function(e) {
            if (!/(38|40|27)/.test(e.keyCode)) { return; }

            e.preventDefault();
            e.stopPropagation();

            if (domClass.contains(this, "disabled") || domAttr.get(this, "disabled")) {
                return false;
            }
            var targetNode = _getParent(this)[0];
            if (targetNode) {
                var isActive = domClass.contains(targetNode, 'open');
                if (!isActive || (isActive && e.keyCode === 27)) {
                    return on.emit(targetNode, 'click', { bubbles:true, cancelable:true });
                }
            }
            var items = query('[role=menu] li:not(.divider) a', targetNode);
            if (!items.length) { return; }
            var index = items.indexOf(document.activeElement);

            if (e.keyCode === 38 && index > 0) { index--; }
            if (e.keyCode === 40 && index < items.length - 1) { index++; }
            if (index < 0) { index = 0; }

            if (items[index]) {
                items[index].focus();
            }
        }
    });

    function clearMenus() {
        _getParent(query(toggleSelector)).removeClass('open');
    }

    function _getParent(node){
        var selector = domAttr.get(node, 'data-target');
        if (!selector) {
            selector = domAttr.get(node, "href");
            selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
        }
        var parentNode = query(node).parent();
        if (selector && selector !== '#' && selector !== '') {
            parentNode = query(selector).parent();
        }
        return parentNode;
    }

    lang.extend(query.NodeList, {
        dropdown:function (option) {
            var options = (lang.isObject(option)) ? option : {};
            return this.forEach(function (node) {
                var data = support.getData(node, 'dropdown');
                if (!data) {
                    support.setData(node, 'dropdown', (data = new Dropdown(node, options)));
                }
                if (lang.isString(option) && lang.exists(data[option])) {
                    data[option].call(data);
                }
            });
        }
    });
    query('html').on('click, touchstart', clearMenus);
    on(win.body(), on.selector(toggleSelector, 'click, touchstart'), Dropdown.prototype.toggle);
    on(win.body(), on.selector('.dropdown form', 'click, touchstart'), function (e) {
        e.stopPropagation();
    });
    on(win.body(), on.selector(toggleSelector+', [role=menu]', 'keydown, touchstart'), Dropdown.prototype.keydown);

    return Dropdown;
});<|endoftext|>


<commit_before>/**
* TabNavigator UI-Component for static and dinamic content.
* @name TabNavigator
* @class TabNavigator
* @augments ch.Controllers
* @memberOf ch
* @param {object} conf Object with configuration properties
* @returns itself
*/

ch.tabNavigator = function(conf){

	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.TabNavigator#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.controllers.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/

	/**
	* The actual location hash, is used to know if there's a specific tab selected.
	* @private
	* @name ch.TabNavigator#hash
	* @type string
	*/
	var hash = window.location.hash.replace("#!", "");

	/**
	* A boolean property to know if the some tag should be selected.
	* @private
	* @name ch.TabNavigator#hashed
	* @type boolean
	* @default false
	*/
	var hashed = false;

	/**
	* Get wich tab is selected.
	* @private
	* @name ch.TabNavigator#selected
	* @type number
	*/
	var selected = conf.selected - 1 || conf.value - 1 || 0;

	/**
	* Create controller's children.
	* @private
	* @name ch.TabNavigator#createTabs
	* @function
	*/

	var createTabs = function(){

		// Children
		that.$triggers.find("a").each(function(i, e){

			// Tab context
			var tab = {};
				tab.uid = that.uid + "#" + i;
				tab.type = "tab";
				tab.element = e;
				tab.$element = $(e);
				tab.controller = that["public"];

				tab.$element.attr("role","tab");

			// Tab configuration
			var config = {};
				config.open = (selected == i);
				config.onShow = function(){
					selected = i;
				};

			if(ch.utils.hasOwn(that.conf, "cache")) {
				config.cache = that.conf.cache;
			};

		/**
		* Callback function
		* @name ch.TabNavigator#onContentLoad
		* @event
		* @public
		*/
			if (ch.utils.hasOwn(that.conf, "onContentLoad")) config.onContentLoad = that.conf.onContentLoad;
		/**
		* Callback function
		* @name ch.TabNavigator#onContentError
		* @event
		* @public
		*/
			if (ch.utils.hasOwn(that.conf, "onContentError")) config.onContentError = that.conf.onContentError;

			// Create Tabs
			that.children.push(
				ch.tab.call(tab, config)
			);

			// Bind new click to have control
			$(e).unbind("click").bind("click", function(event){
				that.prevent(event);
				select(i + 1);
			});

		});

		return;

	};

	/**
	* Select a child to show its content.
	* @name ch.TabNavigator#select
	* @private
	* @function
	*/
	var select = function(tab){

		tab = that.children[tab - 1];

		if(tab === that.children[selected]) return; // Don't click me if I'm open

		// Hide my open bro
		$.each(that.children, function(i, e){
			if(tab !== e) e.hide();
		});

		tab.show();

		//Change location hash
		window.location.hash = "#!" + tab.$content.attr("id");

	/**
	* Callback function
	* @name ch.TabNavigator#onSelect
	* @event
	* @public
	*/
		that.callbacks("onSelect");
		// new callback
		that.trigger("select");

	return that;
	};

/**
*	Protected Members
*/

	/**
	* The component's triggers container.
	* @private
	* @name ch.TabNavigator#$triggers
	* @type jQuery
	*/
	that.$triggers = that.$element.children(":first").addClass("ch-tabNavigator-triggers").attr("role","tablist");

	/**
	* The component's content.
	* @private
	* @name ch.TabNavigator#$content
	* @type jQuery
	*/
	that.$content = that.$triggers.next().addClass("ch-tabNavigator-content box").attr("role","presentation");

/**
*	Public Members
*/

	/**
	* The component's instance unique identifier.
	* @public
	* @name ch.TabNavigator#uid
	* @type number
	*/

	/**
	* The element reference.
	* @public
	* @name ch.TabNavigator#element
	* @type HTMLElement
	*/

	/**
	* The component's type.
	* @public
	* @name ch.TabNavigator#type
	* @type string
	*/

	/**
	* Children instances associated to this controller.
	* @public
	* @name ch.TabNavigator#children
	* @type collection
	*/
	that["public"].children = that.children;

	/**
	* Select a specific child.
	* @public
	* @function
	* @name ch.TabNavigator#select
	* @param {number} tab Tab's index.
	*/
	that["public"].select = function(tab){
		select(tab);

		return that["public"];
	};

	/**
	* Returns the selected child's index.
	* @public
	* @function
	* @name ch.TabNavigator#getSelected
	* @returns {number} selected Tab's index.
	*/
	that["public"].getSelected = function(){ return (selected + 1); };

/**
*	Default event delegation
*/

	that.$element.addClass("ch-tabNavigator");

	createTabs();

	//Default: Load hash tab or Open first tab
	for(var i = that.children.length; i-=1; ){
		if ( that.children[i].$content.attr("id") === hash ) {
			select(i + 1);

			hashed = true;

			break;
		};
	};

	return that;

};

ch.factory("tabNavigator");

/**
* Simple unit of content for TabNavigators.
* @abstract
* @name Tab
* @class Tab
* @augments ch.Navs
* @memberOf ch
* @param {object} conf Object with configuration properties
* @returns itself
*/

ch.tab = function(conf){
	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.Tab#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);
	conf.icon = false;

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.navs.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/
	/**
	* Creates the basic structure for the tab's content.
	* @private
	* @name ch.Tab#createContent
	* @function
	*/
	var createContent = function(){
		var href = that.element.href.split("#");
		var controller = that.$element.parents(".ch-tabNavigator");
		var content = controller.find("#" + href[1]);

		// If there are a tabContent...
		if (content.length > 0) {

			return content;

		// If tabContent doesn't exists
		} else {
			/**
			* Content configuration property.
			* @public
			* @name ch.Tab#source
			* @type string
			*/
			that.source = that.element.href;

			var id = (href.length == 2) ? href[1] : "ch-tab" + that.uid.replace("#","-");

			// Create tabContent
			return $("
").appendTo( controller.children().eq(1) );
		}

	};

/**
*	Protected Members
*/
	/**
	* Reference to the trigger element.
	* @private
	* @name ch.Tab#$trigger
	* @type jQuery
	*/
	that.$trigger = that.$element;

	/**
	* The component's content.
	* @private
	* @name ch.Tab#$content
	* @type jQuery
	*/
	that.$content = createContent();

	/**
	* Process the show event.
	* @private
	* @function
	* @name ch.Tab#show
	* @returns jQuery
	*/
	that.show = function(event){
		that.prevent(event);

		// Load my content if I'need an ajax request
		if (ch.utils.hasOwn(that, "source")) {
			that.content();
		}

		// Show me
		that.parent.show(event);

		// Set me as hidden false
		that.$content.attr("aria-hidden","false");

		// It removes the class ch-js-hide because the content be visible on click
		//that.$content.hasClass('ch-js-hide')?that.$content.removeClass('ch-js-hide'):null;

		// When click or enter to the tab, then it will be focused
		that.$trigger.focus();

		return that;
	};

	/**
	* Process the hide event.
	* @private
	* @function
	* @name ch.Tab#hide
	* @returns jQuery
	*/
	that.hide = function(event){
		that.prevent(event);

		// Hide me
		that.parent.hide(event);

		// Set all inactive tabs as hidden
		that.$content.attr("aria-hidden","true");

		return that;
	};

	/**
	* This callback is triggered when async data is loaded into component's content, when ajax content comes back.
	* @protected
	* @name ch.Tab#contentCallback
	*/
	that["public"].on("contentLoad", function(event, context){

		that.$content.html(that.staticContent);

		if (ch.utils.hasOwn(conf, "onContentLoad")) {
			conf.onContentLoad.call(context, that.staticContent);
		}

	});

	/**
	* This callback is triggered when async request fails.
	* @public
	* @name contentCallback
	* @returns {Chico-UI Object}
	* @memberOf ch.TabNavigator
	*/
	that["public"].on("contentError", function(event, data){

		that.$content.html(that.staticContent);

		// Get the original that.source
		var originalSource = that.source;

		if (ch.utils.hasOwn(conf, "onContentError")) {
			conf.onContentError.call(data.context, data.jqXHR, data.textStatus, data.errorThrown);
		}

		// Reset content configuration
		that.source = originalSource;
		that.staticContent = undefined;

	});

/**
*	Public Members
*/

/**
*	Default event delegation
*/

	that.configBehavior();
	// Add the attr for WAI-ARIA to the tabs and tabpanel
	var hidden = that.$content.hasClass("ch-hide")?true:false;
		that.$content.attr("role","tabpanel").attr("aria-hidden",hidden);
		that.$trigger.attr("role","tab");
		that.$trigger.attr("arial-controls",that.$content.attr("id"));
	return that;
}<commit_msg>GH-346 Tab navigator had a focus event triggered on show<commit_after>/**
* TabNavigator UI-Component for static and dinamic content.
* @name TabNavigator
* @class TabNavigator
* @augments ch.Controllers
* @memberOf ch
* @param {object} conf Object with configuration properties
* @returns itself
*/

ch.tabNavigator = function (conf) {

	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.TabNavigator#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.controllers.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/

	/**
	* The actual location hash, is used to know if there's a specific tab selected.
	* @private
	* @name ch.TabNavigator#hash
	* @type string
	*/
	var hash = window.location.hash.replace("#!", ""),

	/**
	* A boolean property to know if the some tag should be selected.
	* @private
	* @name ch.TabNavigator#hashed
	* @type boolean
	* @default false
	*/
		hashed = false,

	/**
	* Get wich tab is selected.
	* @private
	* @name ch.TabNavigator#selected
	* @type number
	*/
		selected = conf.selected - 1 || conf.value - 1 || 0,

	/**
	* Create controller's children.
	* @private
	* @name ch.TabNavigator#createTabs
	* @function
	*/
		createTabs = function () {

			// Children
			that.$triggers.find("a").each(function (i, e) {

				// Tab context
				var tab = {};
					tab.uid = that.uid + "#" + i;
					tab.type = "tab";
					tab.element = e;
					tab.$element = $(e);
					tab.controller = that["public"];

				// Tab configuration
				var config = {};
					config.open = (selected == i);
					config.onShow = function () { selected = i; };

				if (ch.utils.hasOwn(that.conf, "cache")) { config.cache = that.conf.cache; }

				/**
				* Callback function
				* @name ch.TabNavigator#onContentLoad
				* @event
				* @public
				*/
				if (ch.utils.hasOwn(that.conf, "onContentLoad")) { config.onContentLoad = that.conf.onContentLoad; }

				/**
				* Callback function
				* @name ch.TabNavigator#onContentError
				* @event
				* @public
				*/
				if (ch.utils.hasOwn(that.conf, "onContentError")) { config.onContentError = that.conf.onContentError; }

				// Create Tabs
				that.children.push(ch.tab.call(tab, config));

				// Bind new click to have control
				$(e).unbind("click").bind("click", function (event) {
					that.prevent(event);
					select(i + 1);
				});

			});

			return;

		},

	/**
	* Select a child to show its content.
	* @name ch.TabNavigator#select
	* @private
	* @function
	*/
		select = function (tab) {

			tab = that.children[tab - 1];

			// Don't click me if I'm open
			if (tab === that.children[selected]) { return; }

			// Hide my open bro
			$.each(that.children, function (i, e) {
				if (tab !== e) { e.hide(); }
			});

			tab.show();

			//Change location hash
			window.location.hash = "#!" + tab.$content.attr("id");

			/**
			* Callback function
			* @name ch.TabNavigator#onSelect
			* @event
			* @public
			*/
			that.callbacks("onSelect");
			// new callback
			that.trigger("select");

			return that;

		};

/**
*	Protected Members
*/

	/**
	* The component's triggers container.
	* @private
	* @name ch.TabNavigator#$triggers
	* @type jQuery
	*/
	that.$triggers = that.$element.children(":first").addClass("ch-tabNavigator-triggers").attr("role", "tablist");

	/**
	* The component's content.
	* @private
	* @name ch.TabNavigator#$content
	* @type jQuery
	*/
	that.$content = that.$triggers.next().addClass("ch-tabNavigator-content box").attr("role", "presentation");

/**
*	Public Members
*/

	/**
	* The component's instance unique identifier.
	* @public
	* @name ch.TabNavigator#uid
	* @type number
	*/

	/**
	* The element reference.
	* @public
	* @name ch.TabNavigator#element
	* @type HTMLElement
	*/

	/**
	* The component's type.
	* @public
	* @name ch.TabNavigator#type
	* @type string
	*/

	/**
	* Children instances associated to this controller.
	* @public
	* @name ch.TabNavigator#children
	* @type collection
	*/
	that["public"].children = that.children;

	/**
	* Select a specific child.
	* @public
	* @function
	* @name ch.TabNavigator#select
	* @param {number} tab Tab's index.
	*/
	that["public"].select = function (tab) {
		select(tab);

		return that["public"];
	};

	/**
	* Returns the selected child's index.
	* @public
	* @function
	* @name ch.TabNavigator#getSelected
	* @returns {number} selected Tab's index.
	*/
	that["public"].getSelected = function () { return (selected + 1); };

/**
*	Default event delegation
*/

	that.$element.addClass("ch-tabNavigator");

	createTabs();

	// Default: Load hash tab or Open first tab
	for(var i = that.children.length; i -= 1;) {
		if (that.children[i].$content.attr("id") === hash) {
			select(i + 1);

			hashed = true;

			break;
		};
	};

	return that;

};

ch.factory("tabNavigator");

/**
* Simple unit of content for TabNavigators.
* @abstract
* @name Tab
* @class Tab
* @augments ch.Navs
* @memberOf ch
* @param {object} conf Object with configuration properties
* @returns itself
*/

ch.tab = function (conf) {
	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.Tab#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);
	conf.icon = false;

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.navs.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/
	/**
	* Creates the basic structure for the tab's content.
	* @private
	* @name ch.Tab#createContent
	* @function
	*/
	var createContent = function () {

		var href = that.element.href.split("#"),
			controller = that.$element.parents(".ch-tabNavigator"),
			content = controller.find("#" + href[1]);

		// If there are a tabContent...
		if (content.length > 0) {

			return content;

		// If tabContent doesn't exists
		} else {
			/**
			* Content configuration property.
			* @public
			* @name ch.Tab#source
			* @type string
			*/
			that.source = that.element.href;

			var id = (href.length === 2) ? href[1] : "ch-tab" + that.uid.replace("#", "-");

			// Create tabContent
			return $("
").appendTo(controller.children().eq(1));
		}

	};

/**
*	Protected Members
*/
	/**
	* Reference to the trigger element.
	* @private
	* @name ch.Tab#$trigger
	* @type jQuery
	*/
	that.$trigger = that.$element;

	/**
	* The component's content.
	* @private
	* @name ch.Tab#$content
	* @type jQuery
	*/
	that.$content = createContent();

	/**
	* Process the show event.
	* @private
	* @function
	* @name ch.Tab#show
	* @returns jQuery
	*/
	that.show = function (event) {
		that.prevent(event);

		// Load my content if I'need an ajax request
		if (ch.utils.hasOwn(that, "source")) { that.content(); }

		// Show me
		that.parent.show(event);

		// Set me as hidden false
		that.$content.attr("aria-hidden", "false");

		// It removes the class ch-js-hide because the content be visible on click
		//that.$content.hasClass('ch-js-hide')?that.$content.removeClass('ch-js-hide'):null;

		// When click or enter to the tab, then it will be focused
		// Deprecated: Issue GH-346
		//that.$trigger.focus();

		return that;
	};

	/**
	* Process the hide event.
	* @private
	* @function
	* @name ch.Tab#hide
	* @returns jQuery
	*/
	that.hide = function (event) {
		that.prevent(event);

		// Hide me
		that.parent.hide(event);

		// Set all inactive tabs as hidden
		that.$content.attr("aria-hidden", "true");

		return that;
	};

	/**
	* This callback is triggered when async data is loaded into component's content, when ajax content comes back.
	* @protected
	* @name ch.Tab#contentCallback
	*/
	that["public"].on("contentLoad", function (event, context) {

		that.$content.html(that.staticContent);

		if (ch.utils.hasOwn(conf, "onContentLoad")) {
			conf.onContentLoad.call(context, that.staticContent);
		}

	});

	/**
	* This callback is triggered when async request fails.
	* @public
	* @name contentCallback
	* @returns {Chico-UI Object}
	* @memberOf ch.TabNavigator
	*/
	that["public"].on("contentError", function (event, data) {

		that.$content.html(that.staticContent);

		// Get the original that.source
		var originalSource = that.source;

		if (ch.utils.hasOwn(conf, "onContentError")) {
			conf.onContentError.call(data.context, data.jqXHR, data.textStatus, data.errorThrown);
		}

		// Reset content configuration
		that.source = originalSource;

		that.staticContent = undefined;

	});

/**
*	Public Members
*/

/**
*	Default event delegation
*/

	that.configBehavior();

	// Add the attributes for WAI-ARIA to the tabs and tabpanel
	that.$content.attr({
		"role": "tabpanel",
		"aria-hidden": that.$content.hasClass("ch-hide")
	});

	that.$trigger.attr({
		"role": "tab",
		"arial-controls": that.$content.attr("id")
	});

	return that;
}<|endoftext|>


<commit_before>(function() {
    "use strict";

    if (!SirTrevor)
        return console.error("SirTrevor.Blocks.ImageEdit could not load because SirTrevor wasn't found");

    SirTrevor.Blocks.ImageEdit = SirTrevor.Blocks.Image.extend({

        type: "image_edit",
        title: function() { return i18n.t('blocks:image:title') },
        cropTimeout: 1000,

        controllable: true,
        controls: {
            save: function() {
                var data = {
                    name: this.filename + '.png',
                    folder: 'img',
                    base64content: this.$cropper('getCroppedCanvas').toDataURL()
                };

                this.resetErrors();
                this.$editor.html($('<img>', { src: data.base64content })).show();

                var handleError = function(err) {
                    if (err && err.cause !== undefined)
                        this.setError(this.$inputs, err.cause);
                    else
                        this.setError(this.$inputs, err);
                }.bind(this);

                $.ajax({
                    method: 'POST',
                    url: SirTrevor.config.defaults.uploadUrl,
                    data: data,
                    beforeSend: function() {
                        this.$control_ui.hide();
                        this.loading();
                    }.bind(this),
                    complete: function() {
                        this.ready();
                        this.performValidations();
                    }.bind(this)

                })
                .then(function (res) {
                    if (res.result == 'success') {
                        this.setAndLoadData({ file: { url: res.url } });
                        // event
                        var eventType = 'blocks:image_edit:uploaded';
                        var ev = jQuery.Event(ev);
                        ev.target = this.$editor.find('>img')[0];
                        this.mediator.trigger(eventType, ev);
                    } else
                        handleError(res);
                }.bind(this))
                .fail(handleError);
            }
        },

        validations: [ '_checkCropFinished' ],

        _isImageUploaded: function() {
            var data = this._getData();
            if (data && data.file && data.file.url.length > 0)
                return !/^data:image\/png;base64/.test(data.file.url);
            else
                return false;
        },

        _checkCropFinished: function() {
            if (!this.isEmpty() && !this._isImageUploaded())
                this.setError(this.$inputs, i18n.t('blocks:image_edit:finish'));
        },

        loadData: function(data){
            // Create our image tag
            this.$editor.html($('<img>', { src: data.file.url })).show();
            this.$control_ui.hide();
        },

        remove: function() {
            if (this.ajaxable) {
                this.resolveAllInQueue();
            }

            var data = this._getData();
            if (this._isImageUploaded()) {
                var ev = 'block:image_edit:remove';
                if (this.mediator._events[ev] && this.mediator._events[ev].length > 0) {
                    this.mediator.trigger(ev, data.file.url);
                } else {
                    $.ajax({
                        method: 'POST',
                        url: SirTrevor.config.defaults.imageDeleteUrl,
                        data: {
                            url: data.file.url
                        }
                    });
                }
            }

            this.$el.remove();
        },

        onBlockRender: function() {
            // Make the whole element a dropable zone
            this.$el.dropArea()
                .bind('drop', this._handleDrop.bind(this));
            /* Setup the upload button */
            this.$inputs.find('button').bind('click', function(ev){ ev.preventDefault(); });
            this.$inputs.find('input').on('change', (function(ev) {
                this.onDrop(ev.currentTarget);
            }).bind(this));
            this.$control_ui.hide();
        },

        onDrop: function(transferData) {
            var file = transferData.files[0],
                urlAPI = (typeof URL !== "undefined") ? URL : (typeof webkitURL !== "undefined") ? webkitURL : null;

            if (/image/.test(file.type)) {
                this.filename = file.name;
                this.$control_ui.show();
                this.$inputs.hide();
                this.$el.noDropArea();
                this.$editor.html($('<img>', { src: urlAPI.createObjectURL(file) })).show();
                this.loading();
                var timerCache = null;
                var $img = this.$editor.find('>img');
                $img.cropper({
                    built: function () {
                        // Expose the $cropper at block level
                        this.$cropper = function() {
                            return $img.cropper.apply($img, arguments);
                        };
                        this.ready();
                    }.bind(this),
                    crop: function(e) {
                        // If we have a timer cache means user is cropping, cancel the previous timer
                        if (timerCache)
                            clearTimeout(timerCache);
                        else {
                            var e2 = jQuery.Event('crop:start');
                            e2.originalEvent = e;
                            this.$el.triggerHandler(e2, [this.$inputs.first()]);
                        }

                        timerCache = setTimeout(function () {
                            timerCache = null;
                            var e2 = jQuery.Event('crop:finish');
                            e2.originalEvent = e;
                            this.$el.triggerHandler(e2, [this.$inputs.first()]);
                        }.bind(this), this.cropTimeout);
                    }.bind(this)
                });
            }
        },

        isEmpty: function() {
            return this.$editor.find('img').length <= 0;
        },

        _serializeData: function() {
            var url = this.$editor.find('img').attr('src');

            if (this.$cropper && !/^http/.test(url))
                url = this.$cropper('getCroppedCanvas').toDataURL();

            if (url && url.length > 0)
                return  { file: { url: url } };
            else
                return null;
        },
    });
})();<commit_msg>ImageEdit: Fix drop image on FireFox<commit_after>(function() {
    "use strict";

    if (!SirTrevor)
        return console.error("SirTrevor.Blocks.ImageEdit could not load because SirTrevor wasn't found");

    SirTrevor.Blocks.ImageEdit = SirTrevor.Blocks.Image.extend({

        type: "image_edit",
        title: function() { return i18n.t('blocks:image:title') },
        cropTimeout: 1000,

        controllable: true,
        controls: {
            save: function() {
                var data = {
                    name: this.filename + '.png',
                    folder: 'img',
                    base64content: this.$cropper('getCroppedCanvas').toDataURL()
                };

                this.resetErrors();
                this.$editor.html($('<img>', { src: data.base64content })).show();

                var handleError = function(err) {
                    if (err && err.cause !== undefined)
                        this.setError(this.$inputs, err.cause);
                    else
                        this.setError(this.$inputs, err);
                }.bind(this);

                $.ajax({
                    method: 'POST',
                    url: SirTrevor.config.defaults.uploadUrl,
                    data: data,
                    beforeSend: function() {
                        this.$control_ui.hide();
                        this.loading();
                    }.bind(this),
                    complete: function() {
                        this.ready();
                        this.performValidations();
                    }.bind(this)

                })
                .then(function (res) {
                    if (res.result == 'success') {
                        this.setAndLoadData({ file: { url: res.url } });
                        // event
                        var eventType = 'blocks:image_edit:uploaded';
                        var ev = jQuery.Event(ev);
                        ev.target = this.$editor.find('>img')[0];
                        this.mediator.trigger(eventType, ev);
                    } else
                        handleError(res);
                }.bind(this))
                .fail(handleError);
            }
        },

        validations: [ '_checkCropFinished' ],

        _isImageUploaded: function() {
            var data = this._getData();
            if (data && data.file && data.file.url.length > 0)
                return !/^data:image\/png;base64/.test(data.file.url);
            else
                return false;
        },

        _checkCropFinished: function() {
            if (!this.isEmpty() && !this._isImageUploaded())
                this.setError(this.$inputs, i18n.t('blocks:image_edit:finish'));
        },

        loadData: function(data){
            // Create our image tag
            this.$editor.html($('<img>', { src: data.file.url })).show();
            this.$control_ui.hide();
        },

        remove: function() {
            if (this.ajaxable) {
                this.resolveAllInQueue();
            }

            var data = this._getData();
            if (this._isImageUploaded()) {
                var ev = 'block:image_edit:remove';
                if (this.mediator._events[ev] && this.mediator._events[ev].length > 0) {
                    this.mediator.trigger(ev, data.file.url);
                } else {
                    $.ajax({
                        method: 'POST',
                        url: SirTrevor.config.defaults.imageDeleteUrl,
                        data: {
                            url: data.file.url
                        }
                    });
                }
            }

            this.$el.remove();
        },

        onBlockRender: function() {
            // Remove the default dropzone (from the dropable mixin)
            this.$dropzone
                .noDropArea()
                .unbind('drop');

            // Make the whole element a dropable zone
            this.$inner
                .dropArea()
                .bind('drop', this.__handleDrop.bind(this));

            /* Setup the upload button */
            this.$inputs.find('button')
                .bind('click', function (ev){ ev.preventDefault(); });
            this.$inputs.find('input')
                .on('change', (function (ev) { this.onDrop(ev.currentTarget); }).bind(this));

            // By default, hide the confirm crop button
            this.$control_ui.hide();
        },

        onDrop: function(transferData) {
            var file = transferData.files[0],
                urlAPI = (typeof URL !== "undefined") ? URL : (typeof webkitURL !== "undefined") ? webkitURL : null;

            if (/image/.test(file.type)) {
                this.filename = file.name;
                this.$control_ui.show();
                this.$inputs.hide();
                this.$el.noDropArea();
                this.$editor.html($('<img>', { src: urlAPI.createObjectURL(file) })).show();
                this.loading();
                var timerCache = null;
                var $img = this.$editor.find('>img');
                $img.cropper({
                    built: function () {
                        // Expose the $cropper at block level
                        this.$cropper = function() {
                            return $img.cropper.apply($img, arguments);
                        };
                        this.ready();
                    }.bind(this),
                    crop: function(e) {
                        // If we have a timer cache means user is cropping, cancel the previous timer
                        if (timerCache)
                            clearTimeout(timerCache);
                        else {
                            var e2 = jQuery.Event('crop:start');
                            e2.originalEvent = e;
                            this.$el.triggerHandler(e2, [this.$inputs.first()]);
                        }

                        timerCache = setTimeout(function () {
                            timerCache = null;
                            var e2 = jQuery.Event('crop:finish');
                            e2.originalEvent = e;
                            this.$el.triggerHandler(e2, [this.$inputs.first()]);
                        }.bind(this), this.cropTimeout);
                    }.bind(this)
                });
            }
        },

        isEmpty: function() {
            return this.$editor.find('img').length <= 0 || _.isEmpty(this._getData());
        },

        _serializeData: function() {
            var url = this.$editor.find('img').attr('src');

            if (this.$cropper && !/^http/.test(url))
                url = this.$cropper('getCroppedCanvas').toDataURL();

            if (url && url.length > 0)
                return  { file: { url: url } };
            else
                return null;
        },

        /*
            Fix for FFox upload.
            These two methods should be removed when updated to a new version of ST (current 0.5.0)
            ST fixed it on : d612cd6ce4b267677c40fd1ceea88820b382f5e4
         */
        // https://github.com/madebymany/sir-trevor-js/commit/d612cd6ce4b267677c40fd1ceea88820b382f5e4
        __handleDrop: function(e) {
            e.preventDefault();
            e = e.originalEvent;

            var el = $(e.target),
            types = this._toArray(e.dataTransfer.types);
            el.removeClass('st-dropzone--dragover');

            /*
            Check the type we just received,
            delegate it away to our blockTypes to process
            */
            if (types && types.some(function(type) { return this.valid_drop_file_types.includes(type) }, this))
                this.onDrop(e.dataTransfer);

            SirTrevor.EventBus.trigger('block:content:dropped', this.blockID);
        },

        // https://github.com/madebymany/sir-trevor-js/commit/d612cd6ce4b267677c40fd1ceea88820b382f5e4
        _toArray: function(obj) {
            if (Array.isArray(obj))
                return obj;

            var array = [];

            // iterate backwards ensuring that length is an UInt32
            for (var i = obj.length >>> 0; i--;)
                array[i] = obj[i];

            return array;
        }
    });
})();<|endoftext|>


<commit_before>var utils = require('../utils');
var xhr = require('../xhr');
var settings = require('../settings');
var iScroll = require('iscroll');
var session = require('../session');
var i18n = require('../i18n');
var gamesMenu = {};

var isOpen = false;
var newGameCardSwapped = false;

gamesMenu.open = function() {
  isOpen = true;
};

gamesMenu.close = function() {
  isOpen = false;
};

gamesMenu.joinGame = function(id) {
  m.route('/play/' + id);
};

gamesMenu.startAIGame = function() {
  xhr.newAiGame().then(function(data) {
    m.route('/play' + data.url.round);
  }, function(error) {
    utils.handleXhrError(error);
  });
};

gamesMenu.seekHumanGame = function() {
  xhr.seekGame().then(function(data) {
    m.route('/seek/' + data.hook.id);
  }, function(error) {
    utils.handleXhrError(error);
  });
};

function swapCard() {
  newGameCardSwapped = !newGameCardSwapped;
}

function renderRadio(label, name, value, settingsProp) {
  var isOn = settingsProp() === value;
  var id = name + value;
  return [
    m('input.radio[type=radio]', {
      name: name,
      id: id,
      'class': value,
      value: value,
      checked: isOn,
      onchange: function(e) {
        settingsProp(e.target.value);
      }
    }),
    m('label', {
      'for': id
    }, label)
  ];
}

function renderOption(label, value, storedValue) {
  return m('option', {
    value: value,
    selected: storedValue === value
  }, label);
}

function renderSelect(label, name, options, settingsProp, isDisabled) {
  var storedValue = settingsProp();
  return [
    m('label', {
      'for': name
    }, label),
    m('select', {
      name: name,
      disabled: isDisabled,
      onchange: function(e) {
        settingsProp(e.target.value);
      }
    }, options.map(function(e) {
      return renderOption(e[0], e[1], storedValue);
    }))
  ];
}

function tupleOf(x) {
  return [x, x];
}

var humanVariants = [
  ['Standard', '1'],
  ['Chess960', '2'],
  ['King of the hill', '4'],
  ['Antichess', '6']
];
var aiVariants = [
  ['Standard', '1'],
  ['Chess960', '2'],
  ['King of the hill', '4']
];

function renderForm(action, settingsObj, variants) {
  var timeMode = settingsObj.timeMode();
  var hasClock = timeMode === '1';
  // var hasCorresp = timeMode === '2';

  var generalFieldset = [
    m('div.select_input', [
      renderSelect(i18n('color'), 'color', [
        [i18n('white'), 'white'],
        [i18n('black'), 'black'],
        [i18n('randomColor'), 'random']
      ], settingsObj.color),
    ]),
    m('div.select_input', [
      renderSelect(i18n('variant'), 'variant', variants, settingsObj.variant),
    ])
  ];
  if (settingsObj.level) {
    generalFieldset.push(m('div.select_input', [
      renderSelect(i18n('level'), 'ailevel', [
        '1', '2', '3', '4', '5', '6', '7', '8'
      ].map(tupleOf), settingsObj.level)
    ]));
  }
  if (settingsObj.mode) {
    generalFieldset.push(m('div.select_input', [
      renderSelect(i18n('mode'), 'mode', [
        [i18n('casual'), '0'],
        [i18n('rated'), '1']
      ], settingsObj.mode)
    ]));
  }

  var timeFieldset = [
    m('div.select_input', [
      renderSelect(i18n('timeControl'), 'timeMode', [
        [i18n('unlimited'), '0'],
        [i18n('realTime'), '1']
      ], settingsObj.timeMode)
    ])
  ];
  if (settingsObj.time && settingsObj.increment && hasClock) {
    timeFieldset.push(
      m('div.select_input.inline', [
        renderSelect(i18n('time'), 'time', [
          '2', '5', '10', '30', '60'
        ].map(tupleOf), settingsObj.time, false)
      ]),
      m('div.select_input.inline', [
        renderSelect(i18n('increment'), 'increment', [
          '0', '1', '2', '3', '5', '10'
        ].map(tupleOf), settingsObj.increment, false)
      ])
    );
  }
  if (settingsObj.timePreset && hasClock) {
    timeFieldset.push(m('div.select_input', [
      renderSelect(i18n('clock'), 'timepreset', [
        '3+0', '3+2', '5+0', '5+3', '10+0', '30+0'
      ].map(tupleOf), settingsObj.timePreset, false)
    ]));
  }
  // if (hasCorresp) {
  //   timeFieldset.push(m('div.select_input', [
  //     renderSelect('Days per turn:', 'days', [
  //       '1', '2', '3', '5', '7', '10', '14'
  //       ].map(tupleOf), settingsObj.days, false)
  //   ]));
  // }

  return m('form#new_game_form.form', {
    onsubmit: function(e) {
      e.preventDefault();
      gamesMenu.close();
      swapCard();
      action();
    }
  }, [
    m('fieldset', [
      m('div.nice-radio', renderRadio(i18n('human'), 'selected', 'human', settings.newGame.selected)),
      m('div.nice-radio', renderRadio(i18n('computer'), 'selected', 'computer', settings.newGame.selected))
    ]),
    m('fieldset', generalFieldset),
    m('fieldset#clock', timeFieldset),
    m('button', i18n('createAGame'))
  ]);
}

function renderAllGames() {

  function cardDims() {
    var vp = utils.getViewportDims();
    var width = vp.vw * 85 / 100;
    var padding = vp.vw * 2.5 / 100;
    return {
      w: width + padding * 2,
      h: width + 144,
      innerW: width,
      padding: padding
    };
  }

  function renderViewOnlyBoard(fen, lastMove, color) {
    return m('div', {
      style: {
        height: cDim.innerW + 'px'
      }
    }, [
      utils.viewOnlyBoard(fen, lastMove, color)
    ]);
  }

  var nowPlaying = session.isConnected() ? session.get().nowPlaying : [];
  if (nowPlaying === undefined) nowPlaying = [];
  var cDim = cardDims();
  var cardStyle = {
    width: cDim.w + 'px',
    height: cDim.h + 'px',
    paddingLeft: cDim.padding + 'px',
    paddingRight: cDim.padding + 'px'
  };
  var nbCards = nowPlaying.length + 1;
  // scroller wrapper width
  // calcul is:
  // ((cardWidth + visible part of adjacent card) * nb of cards) +
  //   wrapper's marginLeft
  var wrapperWidth = ((cDim.w + cDim.padding * 2) * nbCards) +
    (cDim.padding * 2);

  var allGames = nowPlaying.map(function(g) {
    return m('div.card.standard', {
      style: cardStyle
    }, [
      renderViewOnlyBoard(g.fen, g.lastMove.match(/.{2}/g), g.color),
      m('div.infos', [
        m('div.icon-game.' + g.variant),
        m('div.description', [
          m('h2.title', g.variant),
          m('p', g.opponent.username),
          m('button', {
            config: utils.ontouchendScroll(function() {
              gamesMenu.joinGame(g.id);
              gamesMenu.close();
            })
          }, i18n('joinTheGame'))
        ])
      ])
    ]);
  });

  var newGame = m('div.card.new-game', {
    class: newGameCardSwapped ? 'back_visible' : '',
    style: cardStyle,
  }, [
    m('div.container_flip', [
      m('div.front', [
        renderViewOnlyBoard(),
        m('div.infos', [
          m('div.description', [
            m('h2.title', i18n('createAGame')),
            m('p', i18n('newOpponent')),
            m('button', {
              config: utils.ontouchendScroll(swapCard)
            }, '+ ' + i18n('createAGame'))
          ])
        ])
      ]),
      m('div.back', [
        settings.newGame.selected() === 'human' ?
        renderForm(gamesMenu.seekHumanGame, settings.newGame.human, humanVariants) :
        renderForm(gamesMenu.startAIGame, settings.newGame.ai, aiVariants)
      ])
    ])
  ]);

  allGames.unshift(newGame);

  return m('div#all_games', {
    style: {
      width: wrapperWidth + 'px',
      marginLeft: (cDim.padding * 2) + 'px'
    }
  }, allGames);
}

gamesMenu.view = function() {
  var children = [
    m('div.overlay-close', {
        config: utils.ontouchend(gamesMenu.close)
      },
      '+'),
    m('div#wrapper_games', {
      config: function(el, isUpdate, context) {
        if (!isUpdate) {
          var scroller = new iScroll(el, {
            scrollX: true,
            scrollY: false,
            momentum: false,
            snap: '.card',
            snapSpeed: 400,
            preventDefaultException: {
              tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|LABEL)$/
            }
          });

          context.onunload = function() {
            if (scroller) {
              scroller.destroy();
              scroller = null;
            }
          };
        }
      }
    }, renderAllGames())
  ];

  return m('div#game_menu.overlay.overlay-effect', {
    class: isOpen ? 'open' : '',
  }, children);
};

module.exports = gamesMenu;
<commit_msg>Fix scroller<commit_after>var utils = require('../utils');
var xhr = require('../xhr');
var settings = require('../settings');
var iScroll = require('iscroll');
var session = require('../session');
var i18n = require('../i18n');
var gamesMenu = {};

var isOpen = false;
var newGameCardSwapped = false;

gamesMenu.open = function() {
  isOpen = true;
};

gamesMenu.close = function() {
  isOpen = false;
};

gamesMenu.joinGame = function(id) {
  m.route('/play/' + id);
};

gamesMenu.startAIGame = function() {
  xhr.newAiGame().then(function(data) {
    m.route('/play' + data.url.round);
  }, function(error) {
    utils.handleXhrError(error);
  });
};

gamesMenu.seekHumanGame = function() {
  xhr.seekGame().then(function(data) {
    m.route('/seek/' + data.hook.id);
  }, function(error) {
    utils.handleXhrError(error);
  });
};

function swapCard() {
  newGameCardSwapped = !newGameCardSwapped;
}

function renderRadio(label, name, value, settingsProp) {
  var isOn = settingsProp() === value;
  var id = name + value;
  return [
    m('input.radio[type=radio]', {
      name: name,
      id: id,
      'class': value,
      value: value,
      checked: isOn,
      onchange: function(e) {
        settingsProp(e.target.value);
      }
    }),
    m('label', {
      'for': id
    }, label)
  ];
}

function renderOption(label, value, storedValue) {
  return m('option', {
    value: value,
    selected: storedValue === value
  }, label);
}

function renderSelect(label, name, options, settingsProp, isDisabled) {
  var storedValue = settingsProp();
  return [
    m('label', {
      'for': name
    }, label),
    m('select', {
      name: name,
      disabled: isDisabled,
      onchange: function(e) {
        settingsProp(e.target.value);
      }
    }, options.map(function(e) {
      return renderOption(e[0], e[1], storedValue);
    }))
  ];
}

function tupleOf(x) {
  return [x, x];
}

var humanVariants = [
  ['Standard', '1'],
  ['Chess960', '2'],
  ['King of the hill', '4'],
  ['Antichess', '6']
];
var aiVariants = [
  ['Standard', '1'],
  ['Chess960', '2'],
  ['King of the hill', '4']
];

function renderForm(action, settingsObj, variants) {
  var timeMode = settingsObj.timeMode();
  var hasClock = timeMode === '1';
  // var hasCorresp = timeMode === '2';

  var generalFieldset = [
    m('div.select_input', [
      renderSelect(i18n('color'), 'color', [
        [i18n('white'), 'white'],
        [i18n('black'), 'black'],
        [i18n('randomColor'), 'random']
      ], settingsObj.color),
    ]),
    m('div.select_input', [
      renderSelect(i18n('variant'), 'variant', variants, settingsObj.variant),
    ])
  ];
  if (settingsObj.level) {
    generalFieldset.push(m('div.select_input', [
      renderSelect(i18n('level'), 'ailevel', [
        '1', '2', '3', '4', '5', '6', '7', '8'
      ].map(tupleOf), settingsObj.level)
    ]));
  }
  if (settingsObj.mode) {
    generalFieldset.push(m('div.select_input', [
      renderSelect(i18n('mode'), 'mode', [
        [i18n('casual'), '0'],
        [i18n('rated'), '1']
      ], settingsObj.mode)
    ]));
  }

  var timeFieldset = [
    m('div.select_input', [
      renderSelect(i18n('timeControl'), 'timeMode', [
        [i18n('unlimited'), '0'],
        [i18n('realTime'), '1']
      ], settingsObj.timeMode)
    ])
  ];
  if (settingsObj.time && settingsObj.increment && hasClock) {
    timeFieldset.push(
      m('div.select_input.inline', [
        renderSelect(i18n('time'), 'time', [
          '2', '5', '10', '30', '60'
        ].map(tupleOf), settingsObj.time, false)
      ]),
      m('div.select_input.inline', [
        renderSelect(i18n('increment'), 'increment', [
          '0', '1', '2', '3', '5', '10'
        ].map(tupleOf), settingsObj.increment, false)
      ])
    );
  }
  if (settingsObj.timePreset && hasClock) {
    timeFieldset.push(m('div.select_input', [
      renderSelect(i18n('clock'), 'timepreset', [
        '3+0', '3+2', '5+0', '5+3', '10+0', '30+0'
      ].map(tupleOf), settingsObj.timePreset, false)
    ]));
  }
  // if (hasCorresp) {
  //   timeFieldset.push(m('div.select_input', [
  //     renderSelect('Days per turn:', 'days', [
  //       '1', '2', '3', '5', '7', '10', '14'
  //       ].map(tupleOf), settingsObj.days, false)
  //   ]));
  // }

  return m('form#new_game_form.form', {
    onsubmit: function(e) {
      e.preventDefault();
      gamesMenu.close();
      swapCard();
      action();
    }
  }, [
    m('fieldset', [
      m('div.nice-radio', renderRadio(i18n('human'), 'selected', 'human', settings.newGame.selected)),
      m('div.nice-radio', renderRadio(i18n('computer'), 'selected', 'computer', settings.newGame.selected))
    ]),
    m('fieldset', generalFieldset),
    m('fieldset#clock', timeFieldset),
    m('button', i18n('createAGame'))
  ]);
}

function renderAllGames() {

  function cardDims() {
    var vp = utils.getViewportDims();
    var width = vp.vw * 85 / 100;
    var padding = vp.vw * 2.5 / 100;
    return {
      w: width + padding * 2,
      h: width + 144,
      innerW: width,
      padding: padding
    };
  }

  function renderViewOnlyBoard(fen, lastMove, color) {
    return m('div', {
      style: {
        height: cDim.innerW + 'px'
      }
    }, [
      utils.viewOnlyBoard(fen, lastMove, color)
    ]);
  }

  var nowPlaying = session.isConnected() ? session.get().nowPlaying : [];
  if (nowPlaying === undefined) nowPlaying = [];
  var cDim = cardDims();
  var cardStyle = {
    width: cDim.w + 'px',
    height: cDim.h + 'px',
    paddingLeft: cDim.padding + 'px',
    paddingRight: cDim.padding + 'px'
  };
  var nbCards = nowPlaying.length + 1;
  // scroller wrapper width
  // calcul is:
  // ((cardWidth + visible part of adjacent card) * nb of cards) +
  //   wrapper's marginLeft
  var wrapperWidth = ((cDim.w + cDim.padding * 2) * nbCards) +
    (cDim.padding * 2);

  var allGames = nowPlaying.map(function(g) {
    return m('div.card.standard', {
      style: cardStyle
    }, [
      renderViewOnlyBoard(g.fen, g.lastMove.match(/.{2}/g), g.color),
      m('div.infos', [
        m('div.icon-game.' + g.variant),
        m('div.description', [
          m('h2.title', g.variant),
          m('p', g.opponent.username),
          m('button', {
            config: utils.ontouchendScroll(function() {
              gamesMenu.joinGame(g.id);
              gamesMenu.close();
            })
          }, i18n('joinTheGame'))
        ])
      ])
    ]);
  });

  var newGame = m('div.card.new-game', {
    class: newGameCardSwapped ? 'back_visible' : '',
    style: cardStyle,
  }, [
    m('div.container_flip', [
      m('div.front', [
        renderViewOnlyBoard(),
        m('div.infos', [
          m('div.description', [
            m('h2.title', i18n('createAGame')),
            m('p', i18n('newOpponent')),
            m('button', {
              config: utils.ontouchendScroll(swapCard)
            }, '+ ' + i18n('createAGame'))
          ])
        ])
      ]),
      m('div.back', [
        settings.newGame.selected() === 'human' ?
        renderForm(gamesMenu.seekHumanGame, settings.newGame.human, humanVariants) :
        renderForm(gamesMenu.startAIGame, settings.newGame.ai, aiVariants)
      ])
    ])
  ]);

  allGames.unshift(newGame);

  return m('div#all_games', {
    style: {
      width: wrapperWidth + 'px',
      marginLeft: (cDim.padding * 2) + 'px'
    }
  }, allGames);
}

gamesMenu.view = function() {
  var children = [
    m('div.overlay-close', {
        config: utils.ontouchend(gamesMenu.close)
      },
      '+'),
    m('div#wrapper_games', {
      config: function(el, isUpdate, context) {
        if (!isUpdate) {
          context.scroller = new iScroll(el, {
            scrollX: true,
            scrollY: false,
            momentum: false,
            snap: '.card',
            snapSpeed: 400,
            preventDefaultException: {
              tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|LABEL)$/
            }
          });

          context.onunload = function() {
            if (context.scroller) {
              context.scroller.destroy();
              context.scroller = null;
            }
          };
        }
        // see https://github.com/cubiq/iscroll/issues/412
        context.scroller.options.snap = el.querySelectorAll('.card');
        context.scroller.refresh();
      }
    }, renderAllGames())
  ];

  return m('div#game_menu.overlay.overlay-effect', {
    class: isOpen ? 'open' : '',
  }, children);
};

module.exports = gamesMenu;
<|endoftext|>


<commit_before>// (C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import KeyboardAccelerators from '../utils/KeyboardAccelerators';
import Drop from '../utils/Drop';
import Responsive from '../utils/Responsive';
import Button from './Button';
import SearchIcon from './icons/base/Search';

const CLASS_ROOT = "search";

export default class Search extends Component {

  constructor(props) {
    super(props);

    this._onAddDrop = this._onAddDrop.bind(this);
    this._onRemoveDrop = this._onRemoveDrop.bind(this);
    this._onFocusControl = this._onFocusControl.bind(this);
    this._onBlurControl = this._onBlurControl.bind(this);
    this._onFocusInput = this._onFocusInput.bind(this);
    this._onBlurInput = this._onBlurInput.bind(this);
    this._onChangeInput = this._onChangeInput.bind(this);
    this._onNextSuggestion = this._onNextSuggestion.bind(this);
    this._onPreviousSuggestion = this._onPreviousSuggestion.bind(this);
    this._onEnter = this._onEnter.bind(this);
    this._onClickSuggestion = this._onClickSuggestion.bind(this);
    this._onSink = this._onSink.bind(this);
    this._onResponsive = this._onResponsive.bind(this);

    this.state = {
      align: 'left',
      controlFocused: false,
      inline: props.inline,
      dropActive: false,
      activeSuggestionIndex: -1
    };
  }

  componentDidMount () {
    if (this.props.inline && this.props.responsive) {
      this._responsive = Responsive.start(this._onResponsive);
    }
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.suggestions && nextProps.suggestions.length > 0 &&
      ! this.state.dropActive && this.refs.input === document.activeElement) {
      this.setState({dropActive: true});
    } else if ((! nextProps.suggestions || nextProps.suggestions.length === 0) &&
      this.state.inline) {
      this.setState({dropActive: false});
    }
  }

  componentDidUpdate (prevProps, prevState) {
    // Set up keyboard listeners appropriate to the current state.

    var activeKeyboardHandlers = {
      esc: this._onRemoveDrop,
      tab: this._onRemoveDrop,
      up: this._onPreviousSuggestion,
      down: this._onNextSuggestion,
      enter: this._onEnter
    };
    var focusedKeyboardHandlers = {
      space: this._onAddDrop
    };

    // the order here is important, need to turn off keys before turning on

    if (! this.state.controlFocused && prevState.controlFocused) {
      KeyboardAccelerators.stopListeningToKeyboard(this, focusedKeyboardHandlers);
    }

    if (! this.state.dropActive && prevState.dropActive) {
      document.removeEventListener('click', this._onRemoveDrop);
      KeyboardAccelerators.stopListeningToKeyboard(this, activeKeyboardHandlers);
      if (this._drop) {
        this._drop.remove();
        this._drop = null;
      }
    }

    if (this.state.controlFocused && ! prevState.controlFocused) {
      KeyboardAccelerators.startListeningToKeyboard(this, focusedKeyboardHandlers);
    }

    if (this.state.dropActive && ! prevState.dropActive) {
      // Slow down adding the click handler,
      // otherwise the drop will close when the mouse is released.
      // Not observable in Safari, 1ms is sufficient for Chrome, Firefox needs 100ms though. :(
      // TODO: re-evaluate how to solve this without a timeout.
      document.addEventListener('click', this._onRemoveDrop);
      KeyboardAccelerators.startListeningToKeyboard(this, activeKeyboardHandlers);

      var baseElement;
      if (this.refs.control) {
        baseElement = this.refs.control.firstChild;
      } else {
        baseElement = this.refs.input;
      }
      var dropAlign = this.props.dropAlign || {
        top: (this.state.inline ? 'bottom' : 'top'),
        left: 'left'
      };
      this._drop = Drop.add(baseElement, this._renderDrop(), dropAlign);

      if (! this.state.inline) {
        document.getElementById('search-drop-input').focus();
      }
    } else if (this._drop) {
      this._drop.render(this._renderDrop());
    }
  }

  componentWillUnmount () {
    document.removeEventListener('click', this._onRemoveDrop);
    KeyboardAccelerators.stopListeningToKeyboard(this);
    if (this._responsive) {
      this._responsive.stop();
    }
    if (this._drop) {
      this._drop.remove();
    }
  }

  _onAddDrop (event) {
    event.preventDefault();
    this.setState({dropActive: true, activeSuggestionIndex: -1});
  }

  _onRemoveDrop () {
    this.setState({dropActive: false});
  }

  _onFocusControl () {
    this.setState({
      controlFocused: true,
      dropActive: true,
      activeSuggestionIndex: -1
    });
  }

  _onBlurControl () {
    this.setState({controlFocused: false});
  }

  _onFocusInput () {
    this.refs.input.select();
    this.setState({
      dropActive: (! this.state.inline ||
        (this.props.suggestions && this.props.suggestions.length > 0)),
      activeSuggestionIndex: -1
    });
  }

  _onBlurInput () {
    //this.setState({drop: false});
  }

  _fireDOMChange () {
    var event = new Event('change', {
      'bubbles': true,
      'cancelable': true
    });
    var controlInput = document.getElementById('search-drop-input');
    var target = this.refs.input || controlInput;
    target.dispatchEvent(event);
    this.props.onDOMChange(event);
  }

  _onChangeInput (event) {
    this.setState({activeSuggestionIndex: -1});
    if (this.props.onChange) {
      this.props.onChange(event.target.value);
    }
    if (this.props.onDOMChange) {
      this._fireDOMChange();
    }
  }

  _onNextSuggestion () {
    var index = this.state.activeSuggestionIndex;
    index = Math.min(index + 1, this.props.suggestions.length - 1);
    this.setState({activeSuggestionIndex: index});
  }

  _onPreviousSuggestion () {
    var index = this.state.activeSuggestionIndex;
    index = Math.max(index - 1, 0);
    this.setState({activeSuggestionIndex: index});
  }

  _onEnter (event) {
    event.preventDefault(); // prevent submitting forms
    this._onRemoveDrop();
    var suggestion;
    if (this.state.activeSuggestionIndex >= 0) {
      suggestion = this.props.suggestions[this.state.activeSuggestionIndex];
      this.setState({value: suggestion});
      if (this.props.onChange) {
        this.props.onChange(suggestion);
      }
      if (this.props.onSelect) {
        this.props.onSelect({
          target: this.refs.input || this.refs.control,
          suggestion: suggestion
        }, true);
      }
    } else {
      if (this.props.onSelect) {
        this.props.onSelect({
          target: this.refs.input || this.refs.control,
          suggestion: suggestion
        }, false);
      }
    }
  }

  _onClickSuggestion (suggestion) {
    this._onRemoveDrop();

    if (this.props.onChange) {
      this.props.onChange(suggestion);
    }
    if (this.props.onSelect) {
      this.props.onSelect({
        target: this.refs.input || this.refs.control,
        suggestion: suggestion
      }, true);
    }
  }

  _onSink (event) {
    event.stopPropagation();
    event.nativeEvent.stopImmediatePropagation();
  }

  _onResponsive (small) {
    if (small) {
      this.setState({inline: false});
    } else {
      this.setState({inline: this.props.inline});
    }
  }

  focus () {
    var ref = this.refs.input || this.refs.control;
    if (ref) {
      ref.focus();
    }
  }

  _classes (prefix) {
    var classes = [prefix];

    if (this.state.inline) {
      classes.push(prefix + "--inline");
    } else {
      classes.push(prefix + "--controlled");
    }

    return classes;
  }

  _renderLabel (suggestion) {
    if (typeof suggestion === 'object') {
      return suggestion.label || suggestion.value;
    } else {
      return suggestion;
    }
  }

  _renderDrop () {
    var classes = this._classes(CLASS_ROOT + "__drop");
    if (this.props.dropColorIndex) {
      classes.push("background-color-index-" + this.props.dropColorIndex);
    }
    if (this.props.large) {
      classes.push(CLASS_ROOT + "__drop--large");
    }

    var input;
    if (! this.state.inline) {
      input = (
        <input key="input" id="search-drop-input" type="search"
          defaultValue={this.props.defaultValue}
          value={this.props.value}
          className={CLASS_ROOT + "__input"}
          onChange={this._onChangeInput} />
      );
    }

    var suggestions;
    if (this.props.suggestions) {
      suggestions = this.props.suggestions.map(function (suggestion, index) {
        var classes = [CLASS_ROOT + "__suggestion"];
        if (index === this.state.activeSuggestionIndex) {
          classes.push(CLASS_ROOT + "__suggestion--active");
        }
        return (

            {this._renderLabel(suggestion)}

        );
      }, this);
      suggestions = (

          {suggestions}

      );
    }

    var contents = [input, suggestions];

    if (! this.state.inline) {
      contents = [
        <Button key="icon" icon="Search"
          className={CLASS_ROOT + "__drop-control"}
          onClick={this._onRemoveDrop} />,

          {contents}

      ];
      if (this.props.dropAlign && ! this.props.dropAlign.left) {
        contents.reverse();
      }
    }

    return (

        {contents}

    );
  }

  render () {

    var classes = this._classes(CLASS_ROOT);
    if (this.props.size) {
      classes.push(CLASS_ROOT + "--" + this.props.size);
    } else if (this.props.large && ! this.props.size) {
      classes.push(CLASS_ROOT + "--large");
    }
    if (this.props.className) {
      classes.push(this.props.className);
    }

    if (this.state.inline) {

      return (

          <input ref="input" type="search"
            id={this.props.id}
            placeholder={this.props.placeHolder}
            defaultValue={this._renderLabel(this.props.defaultValue)}
            value={this._renderLabel(this.props.value)}
            className={CLASS_ROOT + "__input"}
            onFocus={this._onFocusInput}
            onBlur={this._onBlurInput}
            onChange={this._onChangeInput} />
          <SearchIcon />

      );

    } else {

      return (

          <Button id={this.props.id}
            className={classes.join(' ')}
            icon="Search"
            tabIndex="0"
            onClick={this._onAddDrop}
            onFocus={this._onFocusControl}
            onBlur={this._onBlurControl} />

      );
    }
  }

}

Search.propTypes = {
  defaultValue: PropTypes.string,
  dropAlign: Drop.alignPropType,
  dropColorIndex: PropTypes.string,
  id: React.PropTypes.string,
  inline: PropTypes.bool,
  large: PropTypes.bool,
  onChange: PropTypes.func,
  onDOMChange: PropTypes.func,
  onSelect: PropTypes.func,
  placeHolder: PropTypes.string,
  responsive: PropTypes.bool,
  size: React.PropTypes.oneOf(['small', 'medium', 'large']),
  suggestions: PropTypes.arrayOf(PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.shape({
      label: PropTypes.string.isRequired
    })
  ])),
  value: PropTypes.string
};

Search.defaultProps = {
  align: 'left',
  inline: false,
  responsive: true
};
<commit_msg>Fixed Search suggestions property type.<commit_after>// (C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import KeyboardAccelerators from '../utils/KeyboardAccelerators';
import Drop from '../utils/Drop';
import Responsive from '../utils/Responsive';
import Button from './Button';
import SearchIcon from './icons/base/Search';

const CLASS_ROOT = "search";

export default class Search extends Component {

  constructor(props) {
    super(props);

    this._onAddDrop = this._onAddDrop.bind(this);
    this._onRemoveDrop = this._onRemoveDrop.bind(this);
    this._onFocusControl = this._onFocusControl.bind(this);
    this._onBlurControl = this._onBlurControl.bind(this);
    this._onFocusInput = this._onFocusInput.bind(this);
    this._onBlurInput = this._onBlurInput.bind(this);
    this._onChangeInput = this._onChangeInput.bind(this);
    this._onNextSuggestion = this._onNextSuggestion.bind(this);
    this._onPreviousSuggestion = this._onPreviousSuggestion.bind(this);
    this._onEnter = this._onEnter.bind(this);
    this._onClickSuggestion = this._onClickSuggestion.bind(this);
    this._onSink = this._onSink.bind(this);
    this._onResponsive = this._onResponsive.bind(this);

    this.state = {
      align: 'left',
      controlFocused: false,
      inline: props.inline,
      dropActive: false,
      activeSuggestionIndex: -1
    };
  }

  componentDidMount () {
    if (this.props.inline && this.props.responsive) {
      this._responsive = Responsive.start(this._onResponsive);
    }
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.suggestions && nextProps.suggestions.length > 0 &&
      ! this.state.dropActive && this.refs.input === document.activeElement) {
      this.setState({dropActive: true});
    } else if ((! nextProps.suggestions || nextProps.suggestions.length === 0) &&
      this.state.inline) {
      this.setState({dropActive: false});
    }
  }

  componentDidUpdate (prevProps, prevState) {
    // Set up keyboard listeners appropriate to the current state.

    var activeKeyboardHandlers = {
      esc: this._onRemoveDrop,
      tab: this._onRemoveDrop,
      up: this._onPreviousSuggestion,
      down: this._onNextSuggestion,
      enter: this._onEnter
    };
    var focusedKeyboardHandlers = {
      space: this._onAddDrop
    };

    // the order here is important, need to turn off keys before turning on

    if (! this.state.controlFocused && prevState.controlFocused) {
      KeyboardAccelerators.stopListeningToKeyboard(this, focusedKeyboardHandlers);
    }

    if (! this.state.dropActive && prevState.dropActive) {
      document.removeEventListener('click', this._onRemoveDrop);
      KeyboardAccelerators.stopListeningToKeyboard(this, activeKeyboardHandlers);
      if (this._drop) {
        this._drop.remove();
        this._drop = null;
      }
    }

    if (this.state.controlFocused && ! prevState.controlFocused) {
      KeyboardAccelerators.startListeningToKeyboard(this, focusedKeyboardHandlers);
    }

    if (this.state.dropActive && ! prevState.dropActive) {
      // Slow down adding the click handler,
      // otherwise the drop will close when the mouse is released.
      // Not observable in Safari, 1ms is sufficient for Chrome, Firefox needs 100ms though. :(
      // TODO: re-evaluate how to solve this without a timeout.
      document.addEventListener('click', this._onRemoveDrop);
      KeyboardAccelerators.startListeningToKeyboard(this, activeKeyboardHandlers);

      var baseElement;
      if (this.refs.control) {
        baseElement = this.refs.control.firstChild;
      } else {
        baseElement = this.refs.input;
      }
      var dropAlign = this.props.dropAlign || {
        top: (this.state.inline ? 'bottom' : 'top'),
        left: 'left'
      };
      this._drop = Drop.add(baseElement, this._renderDrop(), dropAlign);

      if (! this.state.inline) {
        document.getElementById('search-drop-input').focus();
      }
    } else if (this._drop) {
      this._drop.render(this._renderDrop());
    }
  }

  componentWillUnmount () {
    document.removeEventListener('click', this._onRemoveDrop);
    KeyboardAccelerators.stopListeningToKeyboard(this);
    if (this._responsive) {
      this._responsive.stop();
    }
    if (this._drop) {
      this._drop.remove();
    }
  }

  _onAddDrop (event) {
    event.preventDefault();
    this.setState({dropActive: true, activeSuggestionIndex: -1});
  }

  _onRemoveDrop () {
    this.setState({dropActive: false});
  }

  _onFocusControl () {
    this.setState({
      controlFocused: true,
      dropActive: true,
      activeSuggestionIndex: -1
    });
  }

  _onBlurControl () {
    this.setState({controlFocused: false});
  }

  _onFocusInput () {
    this.refs.input.select();
    this.setState({
      dropActive: (! this.state.inline ||
        (this.props.suggestions && this.props.suggestions.length > 0)),
      activeSuggestionIndex: -1
    });
  }

  _onBlurInput () {
    //this.setState({drop: false});
  }

  _fireDOMChange () {
    var event = new Event('change', {
      'bubbles': true,
      'cancelable': true
    });
    var controlInput = document.getElementById('search-drop-input');
    var target = this.refs.input || controlInput;
    target.dispatchEvent(event);
    this.props.onDOMChange(event);
  }

  _onChangeInput (event) {
    this.setState({activeSuggestionIndex: -1});
    if (this.props.onChange) {
      this.props.onChange(event.target.value);
    }
    if (this.props.onDOMChange) {
      this._fireDOMChange();
    }
  }

  _onNextSuggestion () {
    var index = this.state.activeSuggestionIndex;
    index = Math.min(index + 1, this.props.suggestions.length - 1);
    this.setState({activeSuggestionIndex: index});
  }

  _onPreviousSuggestion () {
    var index = this.state.activeSuggestionIndex;
    index = Math.max(index - 1, 0);
    this.setState({activeSuggestionIndex: index});
  }

  _onEnter (event) {
    event.preventDefault(); // prevent submitting forms
    this._onRemoveDrop();
    var suggestion;
    if (this.state.activeSuggestionIndex >= 0) {
      suggestion = this.props.suggestions[this.state.activeSuggestionIndex];
      this.setState({value: suggestion});
      if (this.props.onChange) {
        this.props.onChange(suggestion);
      }
      if (this.props.onSelect) {
        this.props.onSelect({
          target: this.refs.input || this.refs.control,
          suggestion: suggestion
        }, true);
      }
    } else {
      if (this.props.onSelect) {
        this.props.onSelect({
          target: this.refs.input || this.refs.control,
          suggestion: suggestion
        }, false);
      }
    }
  }

  _onClickSuggestion (suggestion) {
    this._onRemoveDrop();

    if (this.props.onChange) {
      this.props.onChange(suggestion);
    }
    if (this.props.onSelect) {
      this.props.onSelect({
        target: this.refs.input || this.refs.control,
        suggestion: suggestion
      }, true);
    }
  }

  _onSink (event) {
    event.stopPropagation();
    event.nativeEvent.stopImmediatePropagation();
  }

  _onResponsive (small) {
    if (small) {
      this.setState({inline: false});
    } else {
      this.setState({inline: this.props.inline});
    }
  }

  focus () {
    var ref = this.refs.input || this.refs.control;
    if (ref) {
      ref.focus();
    }
  }

  _classes (prefix) {
    var classes = [prefix];

    if (this.state.inline) {
      classes.push(prefix + "--inline");
    } else {
      classes.push(prefix + "--controlled");
    }

    return classes;
  }

  _renderLabel (suggestion) {
    if (typeof suggestion === 'object') {
      return suggestion.label || suggestion.value;
    } else {
      return suggestion;
    }
  }

  _renderDrop () {
    var classes = this._classes(CLASS_ROOT + "__drop");
    if (this.props.dropColorIndex) {
      classes.push("background-color-index-" + this.props.dropColorIndex);
    }
    if (this.props.large) {
      classes.push(CLASS_ROOT + "__drop--large");
    }

    var input;
    if (! this.state.inline) {
      input = (
        <input key="input" id="search-drop-input" type="search"
          defaultValue={this.props.defaultValue}
          value={this.props.value}
          className={CLASS_ROOT + "__input"}
          onChange={this._onChangeInput} />
      );
    }

    var suggestions;
    if (this.props.suggestions) {
      suggestions = this.props.suggestions.map(function (suggestion, index) {
        var classes = [CLASS_ROOT + "__suggestion"];
        if (index === this.state.activeSuggestionIndex) {
          classes.push(CLASS_ROOT + "__suggestion--active");
        }
        return (

            {this._renderLabel(suggestion)}

        );
      }, this);
      suggestions = (

          {suggestions}

      );
    }

    var contents = [input, suggestions];

    if (! this.state.inline) {
      contents = [
        <Button key="icon" icon="Search"
          className={CLASS_ROOT + "__drop-control"}
          onClick={this._onRemoveDrop} />,

          {contents}

      ];
      if (this.props.dropAlign && ! this.props.dropAlign.left) {
        contents.reverse();
      }
    }

    return (

        {contents}

    );
  }

  render () {

    var classes = this._classes(CLASS_ROOT);
    if (this.props.size) {
      classes.push(CLASS_ROOT + "--" + this.props.size);
    } else if (this.props.large && ! this.props.size) {
      classes.push(CLASS_ROOT + "--large");
    }
    if (this.props.className) {
      classes.push(this.props.className);
    }

    if (this.state.inline) {

      return (

          <input ref="input" type="search"
            id={this.props.id}
            placeholder={this.props.placeHolder}
            defaultValue={this._renderLabel(this.props.defaultValue)}
            value={this._renderLabel(this.props.value)}
            className={CLASS_ROOT + "__input"}
            onFocus={this._onFocusInput}
            onBlur={this._onBlurInput}
            onChange={this._onChangeInput} />
          <SearchIcon />

      );

    } else {

      return (

          <Button id={this.props.id}
            className={classes.join(' ')}
            icon="Search"
            tabIndex="0"
            onClick={this._onAddDrop}
            onFocus={this._onFocusControl}
            onBlur={this._onBlurControl} />

      );
    }
  }

}

Search.propTypes = {
  defaultValue: PropTypes.string,
  dropAlign: Drop.alignPropType,
  dropColorIndex: PropTypes.string,
  id: React.PropTypes.string,
  inline: PropTypes.bool,
  large: PropTypes.bool,
  onChange: PropTypes.func,
  onDOMChange: PropTypes.func,
  onSelect: PropTypes.func,
  placeHolder: PropTypes.string,
  responsive: PropTypes.bool,
  size: React.PropTypes.oneOf(['small', 'medium', 'large']),
  suggestions: PropTypes.arrayOf(
    PropTypes.oneOfType([
      PropTypes.shape({
        label: PropTypes.node,
        value: PropTypes.any
      }),
      PropTypes.string
    ])
  ),
  value: PropTypes.string
};

Search.defaultProps = {
  align: 'left',
  inline: false,
  responsive: true
};
<|endoftext|>


<commit_before>/* http://upload.wikimedia.org/wikipedia/commons/0/01/2007-02-20_time_zones.svg */
/*
  In the northern emisphere, january is in winter and july is in summer.
  In the southern emisphere, january is in summer and july is in winter.
  A daylight saving time offset is always -1, so standard time is always
  the greatest of two offsets.
*/
(function() {
  var jsk = {
    /******* PROPERTIES *******/
    breakingMonth : 0,
    testMonth0Offset : null,
    testMonth6Offset : null,
    timeSepator : "",
    /******* PRIVATE *******/
    getDateOffset : function getDate(month) {
      return new Date((new Date()).getFullYear(), month, 0).getTimezoneOffset();
    },
    getMonth0Offset : function() {
      return jsk.testMonth0Offset !== null ?
        jsk.testMonth0Offset :
        jsk.getDateOffset(jsk.breakingMonth);
    },
    getMonth6Offset : function() {
      return jsk.testMonth6Offset !== null ?
        jsk.testMonth6Offset :
        jsk.getDateOffset(jsk.breakingMonth+6);
    },
    offsetToString : function(offset, timeSeparator) {
      timeSeparator = timeSeparator || jsk.timeSeparator || "";

      var parts = [];

      var st = offset/60.0;
      parts.push(st >= 0 ? "+" : "-");

      var hour = Math.floor(Math.abs(st));
      parts.push((hour <= 9 ? "0" : "") + hour);

      parts.push(timeSeparator);

      var min = Math.abs(st%1.0)*60;
      parts.push((min <= 9 ? "0" : "") + min);

      return parts.join("");
    },
    /******* PUBLIC *******/
    // Force some test offsets
    testOffset : function testOffset(month0Offset, month6Offset) {
      jsk.testMonth0Offset = month0Offset;
      jsk.testMonth6Offset = month6Offset;
    },
    // the timezone has daylight saving
    hasDst : function() {
      return jsk.st() != jsk.dst();
    },
    // Returns the standard time offset (inverted)
    invertedSt : function invertedSt() {
      return Math.max(jsk.getMonth0Offset(), jsk.getMonth6Offset());
    },
    // Returns the daylight saving time offset (inverted)
    invertedDst : function invertedDst() {
      return Math.min(jsk.getMonth0Offset(), jsk.getMonth6Offset());
    },
    // Returns the standard time offset
    st : function st() {
      return 0-jsk.invertedSt();
    },
    // Returns standard to string
    stToString : function(timeSeparator) {
      return jsk.offsetToString(jsk.st(), timeSeparator);
    },
    // Returns the standard time offset
    dst : function dst() {
      return 0-jsk.invertedDst();
    },
    // Returns daylight saving to string
    dstToString : function(timeSeparator) {
      return jsk.offsetToString(jsk.dst(), timeSeparator);
    },
    iHateTheLastComma : true // this line exists because I hate the last comma
  };

  // Creates the base namespaces
  if (typeof(window.javascriptKataDotCom) == "undefined") { window.javascriptKataDotCom = {}; }
  if (typeof(window.jsKata) == "undefined") { window.jsKata = window.javascriptKataDotCom; }
  if (typeof(window.jsk) == "undefined") { window.jsk = window.javascriptKataDotCom; }
  if (typeof(window._) == "undefined") { window._ = window.javascriptKataDotCom; }

  window.javascriptKataDotCom.timezone = jsk;
  window.javascriptKataDotCom.tz = jsk;
})()
<commit_msg>no more jslint error<commit_after>/* http://upload.wikimedia.org/wikipedia/commons/0/01/2007-02-20_time_zones.svg */
/*
  In the northern emisphere, january is in winter and july is in summer.
  In the southern emisphere, january is in summer and july is in winter.
  A daylight saving time offset is always -1, so standard time is always
  the greatest of two offsets.
*/
(function() {
  var jsk = {
    /******* PROPERTIES *******/
    breakingMonth : 0,
    testMonth0Offset : null,
    testMonth6Offset : null,
    timeSepator : "",
    /******* PRIVATE *******/
    getDateOffset : function getDate(month) {
      return new Date((new Date()).getFullYear(), month, 0).getTimezoneOffset();
    },
    getMonth0Offset : function() {
      return jsk.testMonth0Offset !== null ?
        jsk.testMonth0Offset :
        jsk.getDateOffset(jsk.breakingMonth);
    },
    getMonth6Offset : function() {
      return jsk.testMonth6Offset !== null ?
        jsk.testMonth6Offset :
        jsk.getDateOffset(jsk.breakingMonth+6);
    },
    offsetToString : function(offset, timeSeparator) {
      timeSeparator = timeSeparator || jsk.timeSeparator || "";

      var parts = [];

      var st = offset/60.0;
      parts.push(st >= 0 ? "+" : "-");

      var hour = Math.floor(Math.abs(st));
      parts.push((hour <= 9 ? "0" : "") + hour);

      parts.push(timeSeparator);

      var min = Math.abs(st%1.0)*60;
      parts.push((min <= 9 ? "0" : "") + min);

      return parts.join("");
    },
    /******* PUBLIC *******/
    // Force some test offsets
    testOffset : function testOffset(month0Offset, month6Offset) {
      jsk.testMonth0Offset = month0Offset;
      jsk.testMonth6Offset = month6Offset;
    },
    // the timezone has daylight saving
    hasDst : function() {
      return jsk.st() != jsk.dst();
    },
    // Returns the standard time offset (inverted)
    invertedSt : function invertedSt() {
      return Math.max(jsk.getMonth0Offset(), jsk.getMonth6Offset());
    },
    // Returns the daylight saving time offset (inverted)
    invertedDst : function invertedDst() {
      return Math.min(jsk.getMonth0Offset(), jsk.getMonth6Offset());
    },
    // Returns the standard time offset
    st : function st() {
      return 0-jsk.invertedSt();
    },
    // Returns standard to string
    stToString : function(timeSeparator) {
      return jsk.offsetToString(jsk.st(), timeSeparator);
    },
    // Returns the standard time offset
    dst : function dst() {
      return 0-jsk.invertedDst();
    },
    // Returns daylight saving to string
    dstToString : function(timeSeparator) {
      return jsk.offsetToString(jsk.dst(), timeSeparator);
    },
    iHateTheLastComma : true // this line exists because I hate the last comma
  };

  // Creates the base namespaces
  if (typeof(window.javascriptKataDotCom) == "undefined") { window.javascriptKataDotCom = {}; }
  if (typeof(window.jsKata) == "undefined") { window.jsKata = window.javascriptKataDotCom; }
  if (typeof(window.jsk) == "undefined") { window.jsk = window.javascriptKataDotCom; }
  if (typeof(window._) == "undefined") { window._ = window.javascriptKataDotCom; }

  window.javascriptKataDotCom.timezone = jsk;
  window.javascriptKataDotCom.tz = jsk;
})();
<|endoftext|>