<commit_before>// mapping for standard US keyboards. Replace the $.keymap.normal and $.keymap.shift to match your keyboard.
// Assumes that control and alt modified keys are nonprinting!
//
// Updated to use the 2014 proposed W3C DOM events:
// http://www.w3.org/TR/DOM-Level-3-Events/
// http://www.w3.org/TR/DOM-Level-3-Events-key/
// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key for how it is currently (2015) implemented
//
// Version: 3.0
// Copyright (c) 2015 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// requires Array.prototype.forEach, so lets me nice to IE8
if ( !Array.prototype.forEach ) {
  Array.prototype.forEach = function(fn, scope) {
    for(var i = 0, len = this.length; i < len; ++i) {
      fn.call(scope, this[i], i, this);
    }
  }
}

// depends on jQuery to normalize the keydown event
(function($){
	// polyfill for the key and code fields of the event, and sets a new field "keys" that incorporates
	// the modifier keys, as in Microsoft's sendkeys function: + for shift, ^ for control, % for alt
	// Shifted keys with a different form do not get the +; thus ^a is control-a; ^A is control-shift-a
	// returns the value of keys
	// NOTE: modifier keys are intentionally ignored and return undefined
	$.keymap = function (evt){
		var key = evt.key, c = evt.which, shift = evt.shiftKey, ctrl = evt.ctrlKey, alt = evt.altKey;
		if (evt.keys !== undefined) return evt.keys; // event has already been handled
		if (key !== undefined){
			// key is implemented; just use what they got
			if (/^(?:shift|control|meta|alt)$/i.test(evt.key)) return;
			// IE (of course!) is different: shifted control characters use the unshifted key code.
			var i = $.keymap.unshiftedchars.indexOf(key);
			if (shift && i !== -1){
				key = $.keymap.shiftedchars[i];
				shift = false;
			}
			if ($.keymap.shiftedchars.indexOf(key) !== -1) shift = false; // already have the key shifted
		}else{
			key = shift ? $.keymap.shift[c] : $.keymap.normal[c];
		}
		if (key === undefined) return;
		if ($.keymap.shiftedchars.indexOf(key) !== -1) shift = false; // already have the key shifted
		var keys = key;
		if (alt) keys = '%' + keys;
		if (ctrl) keys = '^' + keys;
		if (shift) keys = '+' + keys;
		keys = $.keymap.normalize(keys);
		evt.key = key;
		evt.keys = keys;
		return keys;
	}

	// normalize capitalization
	var aliases = $.map(
		'Backspace CapsLock Delete Enter Escape Insert NumLock Tab Spacebar PageUp PageDown End Home ArrowLeft ArrowUp ArrowRight ArrowDown'.split(' '),
		function (key){
			return {alias:  new RegExp('\\b'+key+'\\b', 'i'), key: key};
		}
	);

	// various other key notations that I want to use
	var aliasgenerator = {
		// VIM notation (and jquery.hotkeys numeric keypad notation)
		'^<(.*)>$': '$1', // strip the brackets
		'\\b(?:k|num_)(\\w+)': '$1', // we do not distinguish the numeric keypad
		// the sendkeys aliases
		bksp: 'Backspace',
		bs: 'Backspace',
		del: 'Delete',
		down: 'ArrowDown',
		esc: 'Escape',
		left: 'ArrowLeft',
		right: 'ArrowRight',
		up: 'ArrowUp',
		add: '+',
		subtract: '-',
		multiply: '*',
		divide: '/',
		// hotkeys plugin (https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js), and modifiers from VIM
		's(hift)?[+-]': '+',
		'c(trl)?[+-]': '^',
		'm(eta)?[+-]': '%', // needs to be before a(lt)- so the met[a-] doesn't match!
		'a(lt)?[+-]': '%',
		decimal: '.',
		// VIM notation (http://polarhome.com/vim/manual/v71/intro.html#key-notation)
		minus: '-',
		point: '.',
		lt: '<',
		'#(\\d+)': 'F$1', // this is documented for ex (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html#tag_20_40_13_24) but no reason not to include it
		// Jonathan Tang's keycode.js (https://github.com/nostrademons/keycode.js)
		'page_up': 'pgup',
		'page_down': 'pgdn',
		'num\\+': '+',
		'num-': '-',
		'num\\*': '*',
		'num/': '/',

		'^\\+(\\w)$': function (match, p1) {return p1.toUpperCase()}, // uppercase shifted letters

		// two exceptions to the standard:
		// I use Spacebar for space, not ' ', since that is used as a separator below
		' ': 'Spacebar',
		// and special modifier characters need to be marked
		'(.)([+^%])$': '$1{$2}',

		'[+^%]+': function (match) { // normalize the order of shift-ctrl-alt
			return (/\+/.test(match) ? '+' : '') +
				(/\^/.test(match) ? '^' : '') +
				(/%/.test(match) ? '%' : '')
		},
	};
	for (alias in aliasgenerator){
		// mark whole words
		var key = aliasgenerator[alias];
		if (/^\w/.test(alias)) alias = '\\b'+alias;
		if (/\w$/.test(alias)) alias += '\\b';
		aliases.push({alias: new RegExp(alias, 'i'), key: key});
	}

	$.keymap.normalize = function(c){
		aliases.forEach(function(alias) { c=c.replace(alias.alias, alias.key) });
		return c;
	}

	$.keymap.normalizeList = function(str){
		// normalize a list of space-delimited characters.
		return $.trim($.map(str.split(/\s+/), $.keymap.normalize).join(' '));
	}

	$.keymap.normal = {
		  8	: 'Backspace',
		 20	: 'CapsLock',
		 46	: 'Delete',
		 13	: 'Enter',
		 27	: 'Escape',
		 45	: 'Insert',
		144	: 'NumLock',
		  9	: 'Tab'
	};
	'Spacebar PageUp PageDown End Home ArrowLeft ArrowUp ArrowRight ArrowDown'.
		split(' ').forEach(function(c,i) {$.keymap.normal[i+32] = c});
	"0123456789"
		.split('').forEach(function(c,i) {$.keymap.normal[i+48] = c});
	$.keymap.normal[59] = ';'; // Firefox only!
	"abcdefghijklmnopqrstuvwxyz"
		.split('').forEach(function(c,i) {$.keymap.normal[i+65] = c});
	"0123456789*+,-./". // numeric keypad keys
		split('').forEach(function(c,i) {$.keymap.normal[i+96] = c});
	for(i=1;i<=12;++i)
		$.keymap.normal[i+111] = 'F'+i; // function keys
	';=,-./`'
		.split('').forEach(function(c,i) {$.keymap.normal[i+186] = c});
	"[\\]'"
		.split('').forEach(function(c,i) {$.keymap.normal[i+219] = c});

	$.keymap.shift = {
		192: '~',
		 49: '!',
		 50: '@',
		 51: '#',
		 52: '$',
		 53: '%',
		 54: '^',
		 55: '&',
		 56: '*',
		 57: '(',
		 59: ':', // Firefox only!
		 48: ')',
		189: '_',
		187: '+',
		219: '{',
		220: '|',
		221: '}',
		186: ':',
		222: '"',
		188: '<',
		190: '>',
		191: '?'
	};

	"ABCDEFGHIJKLMNOPQRSTUVXWYZ"
		.split('').forEach(function(c,i) {$.keymap.shift[i+65] = c});

	// characters that represent already-shifted keys, so should not have the + added.
	$.keymap.shiftedchars = '';
	$.keymap.unshiftedchars = '';
	for (c in $.keymap.shift){
		$.keymap.shiftedchars += $.keymap.shift[c];
		$.keymap.unshiftedchars += $.keymap.normal[c];
	}

	// based on John Resig's hotkeys (https://github.com/jeresig/jquery.hotkeys)
	// $.event.special documentation at http://learn.jquery.com/events/event-extensions/
	// use with $(element).on('keydown, {keys: '%1', allowDefault: true}, function(){});

	// first, create an index of namespaces to identify each keystroke list
	var nss = {};
	var nsIndex = 0;
	function hotkeysnamespace(keys){
		return nss[keys] || (nss[keys] = 'hotkeys'+ (++nsIndex));
	}

	["keydown","keyup"].forEach(function(type){
		$.event.fixHooks[type] = { props: "char charCode key keyCode hotkeys".split(" ") };
		$.event.special[type] = $.event.special[type] || {};
		$.event.special[type].add = function(handleObj){
			if (!handleObj.data) return;
			var keys = handleObj.data.keys;
			// Use the keys as a sort of namespace for the event. This is a hack for removing the handler later.
			handleObj.namespace = handleObj.namespace.split('.').
				concat(hotkeysnamespace(keys)).sort().join('.'); // add the new namespace in alphabetical order
			if (typeof keys == 'string'){
				// escape RegExp from https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
				keys = keyregexp(
					$.keymap.normalizeList(keys).replace(/[.*+?^=!:${}()|\[\]\/\\]/g, "\\$&")
				);
			}else if (keys instanceof RegExp){
				keys = keyregexp(keys.source, keys.ignoreCase);
			}else{
				return;
			}
			var origHandler = handleObj.handler;
			var currSequence = '';
			handleObj.handler = function (event){
				var self = $(this);
				var key = $.keymap(event);
				if (!key) return; // avoid problems with control keys
				currSequence += ' ' + key;
				// find the portion of the current sequence that could be a prefix of the sought keys
				while (currSequence){
					var length = currSequence.split(' ').length-1;
					if (keys[length-1].test(currSequence)){
						var actualkeys = currSequence.slice(1); // remove the initial space
						if (length == keys.length){
							// matched the whole thing
							event.hotkeys = actualkeys;
							self.trigger('keymapcomplete', [actualkeys]);
							currSequence = ''; // restart
							return origHandler.apply(this, arguments);
						}else{
							self.trigger('keymapprefix', [actualkeys]);
							return !!handleObj.data.allowDefault;
						}
					}
					currSequence = currSequence.replace(/ \S+/, ''); // strip the first key
				}
			};
		};
	});

	function keyregexp(source, ignorecase){
		// return an array of regexps to match key sequences in source
		var accum = '';
		return $.map(source.split(' '), function(key) {
			accum += ' '+key;
			return new RegExp('^'+accum+'$', ignorecase ? 'i' : ''); // ^...$ to match the entire key
		});
	}

	function addnamespace (types, keys){
		var ns = '.'+hotkeysnamespace($.keymap.normalizeList(keys));
		return types.replace (/(\S)(\s)|$/g, '$1'+ns+'$2');;
	}
	// monkey patch remove so we can do $(elem).off('keydown', {keys: '^A'}) (basically fake the off('keydown', handler) syntax into accepting an object instead
	var oldRemove = $.event.remove;
	$.event.remove = function (elem, types, handler, selector){
		// remove just looks for the guid to match., so we can fake it out.
		// off(types, object) becomes remove (elem, types, undefined, object)
		// off(types, selector, object) becomes  remove (elem, types, object, selector)
		if (handler === undefined && $.isPlainObject(selector) && selector.keys){
			var args = [elem, addnamespace (types, selector.keys)];
		}else if ($.isPlainObject(handler) && handler.keys){
			args = [elem, addnamespace (types, handler.keys), undefined, selector];
		}else{
			args = arguments;
		}
		return oldRemove.apply(this,args);
	}

})(jQuery);<commit_msg>Add keystroke normalization<commit_after>// mapping for standard US keyboards. Replace the $.keymap.normal and $.keymap.shift to match your keyboard.
// Assumes that control and alt modified keys are nonprinting!
//
// Updated to use the 2014 proposed W3C DOM events:
// http://www.w3.org/TR/DOM-Level-3-Events/
// http://www.w3.org/TR/DOM-Level-3-Events-key/
// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key for how it is currently (2015) implemented
//
// Version: 3.0
// Copyright (c) 2015 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// requires Array.prototype.forEach, so lets me nice to IE8
if ( !Array.prototype.forEach ) {
  Array.prototype.forEach = function(fn, scope) {
    for(var i = 0, len = this.length; i < len; ++i) {
      fn.call(scope, this[i], i, this);
    }
  }
}

// depends on jQuery to normalize the keydown event
(function($){
	// polyfill for the key and code fields of the event, and sets a new field "keymap" that incorporates
	// the modifier keys, as in Microsoft's sendkeys function: + for shift, ^ for control, % for alt
	// Shifted keys with a different form do not get the +; thus ^a is control-a; ^A is control-shift-a
	// returns the value of keymap
	// NOTE: modifier keys are intentionally ignored and return undefined
	$.keymap = function (evt){
		var key = evt.key, c = evt.which, shift = evt.shiftKey, ctrl = evt.ctrlKey, alt = evt.altKey;
		if (evt.keymap !== undefined) return evt.keymap; // event has already been handled
		if (key !== undefined){
			// key is implemented; just use what they got
			if (/^(?:shift|control|meta|alt)$/i.test(evt.key)) return;
			// IE (of course!) is different: shifted control characters use the unshifted key code.
			var i = $.keymap.unshiftedchars.indexOf(key);
			if (shift && i !== -1){
				key = $.keymap.shiftedchars[i];
				shift = false;
			}
			if ($.keymap.shiftedchars.indexOf(key) !== -1) shift = false; // already have the key shifted
		}else{
			key = shift ? $.keymap.shift[c] : $.keymap.normal[c];
		}
		if (key === undefined) return;
		if ($.keymap.shiftedchars.indexOf(key) !== -1) shift = false; // already have the key shifted
		var keymap = key;
		if (alt) keymap = '%' + keymap;
		if (ctrl) keymap = '^' + keymap;
		if (shift) keymap = '+' + keymap;
		keymap = $.keymap.normalize(keymap);
		evt.key = key;
		evt.keymap = keymap;
		return keymap;
	}

	// normalize capitalization
	var aliases = $.map(
		'Backspace CapsLock Delete Enter Escape Insert NumLock Tab Spacebar PageUp PageDown End Home ArrowLeft ArrowUp ArrowRight ArrowDown'.split(' '),
		function (key){
			return {alias:  new RegExp('\\b'+key+'\\b', 'i'), key: key};
		}
	);

	// various other key notations that I want to use
	var aliasgenerator = {
		// VIM notation (and jquery.hotkeys numeric keypad notation)
		'^<(.*)>$': '$1', // strip the brackets
		'\\b(?:k|num_)(\\w+)': '$1', // we do not distinguish the numeric keypad
		// the sendkeys aliases
		bksp: 'Backspace',
		bs: 'Backspace',
		del: 'Delete',
		down: 'ArrowDown',
		esc: 'Escape',
		left: 'ArrowLeft',
		right: 'ArrowRight',
		up: 'ArrowUp',
		add: '+',
		subtract: '-',
		multiply: '*',
		divide: '/',
		// hotkeys plugin (https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js), and modifiers from VIM
		's(hift)?[+-]': '+',
		'c(trl)?[+-]': '^',
		'm(eta)?[+-]': '%', // needs to be before a(lt)- so the met[a-] doesn't match!
		'a(lt)?[+-]': '%',
		decimal: '.',
		// VIM notation (http://polarhome.com/vim/manual/v71/intro.html#key-notation)
		minus: '-',
		point: '.',
		lt: '<',
		'#(\\d+)': 'F$1', // this is documented for ex (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html#tag_20_40_13_24) but no reason not to include it
		// Jonathan Tang's keycode.js (https://github.com/nostrademons/keycode.js)
		'page_up': 'pgup',
		'page_down': 'pgdn',
		'num\\+': '+',
		'num-': '-',
		'num\\*': '*',
		'num/': '/',

		'^\\+(\\w)$': function (match, p1) {return p1.toUpperCase()}, // uppercase shifted letters

		// one exceptions to the standard:
		// I use Spacebar for space, not ' ', since that is used as a separator below
		' ': 'Spacebar',

		'([+^%]+)(.)': function (match, p1, p2) { // normalize the order of shift-ctrl-alt
			return (/\+/.test(p1) ? '+' : '') +
				(/\^/.test(p1) ? '^' : '') +
				(/%/.test(p1) ? '%' : '') + p2;
		},
	};
	for (alias in aliasgenerator){
		// mark whole words
		var key = aliasgenerator[alias];
		if (/^\w/.test(alias)) alias = '\\b'+alias;
		if (/\w$/.test(alias)) alias += '\\b';
		aliases.push({alias: new RegExp(alias, 'i'), key: key});
	}

	$.keymap.normalize = function(c){
		aliases.forEach(function(alias) { c=c.replace(alias.alias, alias.key) });
		return c;
	}

	$.keymap.normalizeList = function(str){
		// normalize a list of space-delimited characters.
		return $.trim($.map(str.split(/\s+/), $.keymap.normalize).join(' '));
	}

	$.keymap.normal = {
		 32	: ' ',
		  8	: 'Backspace',
		 20	: 'CapsLock',
		 46	: 'Delete',
		 13	: 'Enter',
		 27	: 'Escape',
		 45	: 'Insert',
		144	: 'NumLock',
		  9	: 'Tab'
	};
	'PageUp PageDown End Home ArrowLeft ArrowUp ArrowRight ArrowDown'.
		split(' ').forEach(function(c,i) {$.keymap.normal[i+33] = c});
	"0123456789"
		.split('').forEach(function(c,i) {$.keymap.normal[i+48] = c});
	$.keymap.normal[59] = ';'; // Firefox only!
	"abcdefghijklmnopqrstuvwxyz"
		.split('').forEach(function(c,i) {$.keymap.normal[i+65] = c});
	"0123456789*+,-./". // numeric keypad keys
		split('').forEach(function(c,i) {$.keymap.normal[i+96] = c});
	for(i=1;i<=12;++i)
		$.keymap.normal[i+111] = 'F'+i; // function keys
	';=,-./`'
		.split('').forEach(function(c,i) {$.keymap.normal[i+186] = c});
	"[\\]'"
		.split('').forEach(function(c,i) {$.keymap.normal[i+219] = c});

	$.keymap.shift = {
		192: '~',
		 49: '!',
		 50: '@',
		 51: '#',
		 52: '$',
		 53: '%',
		 54: '^',
		 55: '&',
		 56: '*',
		 57: '(',
		 59: ':', // Firefox only!
		 48: ')',
		189: '_',
		187: '+',
		219: '{',
		220: '|',
		221: '}',
		186: ':',
		222: '"',
		188: '<',
		190: '>',
		191: '?'
	};

	"ABCDEFGHIJKLMNOPQRSTUVWYZ"
		.split('').forEach(function(c,i) {$.keymap.shift[i+65] = c});

	// characters that represent already-shifted keys, so should not have the + added.
	$.keymap.shiftedchars = '';
	$.keymap.unshiftedchars = '';
	for (c in $.keymap.shift){
		$.keymap.shiftedchars += $.keymap.shift[c];
		$.keymap.unshiftedchars += $.keymap.normal[c];
	}

	// based on John Resig's hotkeys (https://github.com/jeresig/jquery.hotkeys)
	// $.event.special documentation at http://learn.jquery.com/events/event-extensions/
	// use with $(element).on('keydown, {keys: '%1', allowDefault: true}, function(){});

	// first, create an index of namespaces to identify each keystroke list
	var nss = {};
	var nsIndex = 0;
	function hotkeysnamespace(keys){
		return nss[keys] || (nss[keys] = 'hotkeys'+ (++nsIndex));
	}

	// function to set up the "key" field in keydown and keyup events
	function polyfill (evt, original){
		// do the original filtering
		if (evt.which == null && original != null) {
			event.which = original.charCode != null ? original.charCode : original.keyCode;
		}
		// don't take it for granted that the caller got this right
		if (evt.keymap !== undefined) evt.keymap = $.keymap.normalize (evt.keymap);
		var keymap = $.keymap(evt);
		if (keymap == undefined) return evt;
		var key = keymap.replace (/^([+^%]+)(.+)/, function (match, p1, p2){
			if (/\+/.test(p1) || $.keymap.shiftedchars.indexOf(p2) !== -1) evt.shiftKey = true;
			if (/\^/.test(p1)) evt.ctrlKey = true;
			if (/%/.test(p1)) evt.altKey = true;
			return p2;
		});
		if (key == 'Spacebar') key = ' '; // restore the standard
		evt.key = key;
		return evt;
	}

	["keydown","keyup"].forEach(function(type){
		$.event.fixHooks[type] = {
			props: "char charCode code key keyCode keys hotkeys".split(" "),
			filter: polyfill
		};
		$.event.special[type] = $.event.special[type] || {};
		$.event.special[type].trigger = polyfill;
		$.event.special[type].add = function(handleObj){
			if (!handleObj.data) return;
			var keys = handleObj.data.keys;
			// Use the keys as a sort of namespace for the event. This is a hack for removing the handler later.
			handleObj.namespace = handleObj.namespace.split('.').
				concat(hotkeysnamespace(keys)).sort().join('.'); // add the new namespace in alphabetical order
			if (typeof keys == 'string'){
				// escape RegExp from https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
				keys = keyregexp(
					$.keymap.normalizeList(keys).replace(/[.*+?^=!:${}()|\[\]\/\\]/g, "\\$&")
				);
			}else if (keys instanceof RegExp){
				keys = keyregexp(keys.source, keys.ignoreCase);
			}else{
				return;
			}
			var origHandler = handleObj.handler;
			var currSequence = '';
			handleObj.handler = function (event){
				var self = $(this);
				var key = $.keymap(event);
				if (!key) return; // avoid problems with control keys
				currSequence += ' ' + key;
				// find the portion of the current sequence that could be a prefix of the sought keys
				while (currSequence){
					var length = currSequence.split(' ').length-1;
					if (keys[length-1].test(currSequence)){
						var actualkeys = currSequence.slice(1); // remove the initial space
						if (length == keys.length){
							// matched the whole thing
							event.hotkeys = actualkeys;
							self.trigger('keymapcomplete', [actualkeys]);
							currSequence = ''; // restart
							return origHandler.apply(this, arguments);
						}else{
							self.trigger('keymapprefix', [actualkeys]);
							return !!handleObj.data.allowDefault;
						}
					}
					currSequence = currSequence.replace(/ \S+/, ''); // strip the first key
				}
			};
		};
	});

	function keyregexp(source, ignorecase){
		// return an array of regexps to match key sequences in source
		var accum = '';
		return $.map(source.split(' '), function(key) {
			accum += ' '+key;
			return new RegExp('^'+accum+'$', ignorecase ? 'i' : ''); // ^...$ to match the entire key
		});
	}

	function addnamespace (types, keys){
		var ns = '.'+hotkeysnamespace($.keymap.normalizeList(keys));
		return types.replace (/(\S)(\s)|$/g, '$1'+ns+'$2');;
	}
	// monkey patch remove so we can do $(elem).off('keydown', {keys: '^A'}) (basically fake the off('keydown', handler) syntax into accepting an object instead
	var oldRemove = $.event.remove;
	$.event.remove = function (elem, types, handler, selector){
		// remove just looks for the guid to match., so we can fake it out.
		// off(types, object) becomes remove (elem, types, undefined, object)
		// off(types, selector, object) becomes  remove (elem, types, object, selector)
		if (handler === undefined && $.isPlainObject(selector) && selector.keys){
			var args = [elem, addnamespace (types, selector.keys)];
		}else if ($.isPlainObject(handler) && handler.keys){
			args = [elem, addnamespace (types, handler.keys), undefined, selector];
		}else{
			args = arguments;
		}
		return oldRemove.apply(this,args);
	}

})(jQuery);<|endoftext|>


<commit_before>
class BCBProcess extends CommonProcess {
  constructor() {
    super({
      name: `${Project.NAME} Process`
    });

    this.initProcess();
  }

  initProcess() {
    this.initPopover();
  }

  initPopover() {
    {
      new PopoverController({
        name: 'ID Popover',
        selector: '#login-id-help',
        help: 'ID を入力してください。'
      });
      new PopoverController({
        name: 'Password Popover',
        selector: '#login-password-help',
        help: 'パスワード を入力してください。'
      });
      new PopoverController({
        name: 'Login Check Popover',
        selector: '#login-check-help',
        help: '共有デバイスでは設定に注意してください。'
      });
    }
  }
}
<commit_msg>Add Logined ID Popover in BCBProcess<commit_after>
class BCBProcess extends CommonProcess {
  constructor() {
    super({
      name: `${Project.NAME} Process`
    });

    this.initProcess();
  }

  initProcess() {
    this.initPopover();
  }

  initPopover() {
    {
      new PopoverController({
        name: 'ID Popover',
        selector: '#login-id-help',
        help: 'ID を入力してください。'
      });
      new PopoverController({
        name: 'Password Popover',
        selector: '#login-password-help',
        help: 'パスワード を入力してください。'
      });
      new PopoverController({
        name: 'Login Check Popover',
        selector: '#login-check-help',
        help: '共有デバイスでは設定に注意してください。'
      });
      new PopoverController({
        name: 'Logined ID Popover',
        selector: '#logined-id-help',
        help: 'ログインしている ID です。'
      });
    }
  }
}
<|endoftext|>


<commit_before>/*

    plugin name: router
    jquery plugin to handle routes with both hash and push state
    why? why another routing plugin? because i couldnt find one that handles both hash and pushstate
    created by 24hr // camilo.tapia
    author twitter: camilo.tapia

    Copyright 2011  camilo tapia // 24hr (email : camilo.tapia@gmail.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

(function($){

    var hasPushState = (history && history.pushState);
    var hasHashState = !hasPushState && ("onhashchange" in window) && false;
    var router = {};
    var routeList = [];
    var eventAdded = false;
    var currentUsedUrl = location.href; //used for ie to hold the current url
    var firstRoute = true;

    // hold the latest route that was activated
    router.currentId = "";
    router.currentParameters = {};

    router.capabilities = {
        hash: hasHashState,
        pushState: hasPushState,
        timer: !hasHashState && !hasPushState
    };

    // reset all routes
    router.reset = function()
    {
        var router = {};
        var routeList = [];
        router.currentId = "";
        router.currentParameters = {};
    }

    router.add = function(route, id, callback)
    {
        // if we only get a route and a callback, we switch the arguments
        if (typeof id == "function")
        {
            callback = id;
            delete id;
        }

        var isRegExp = typeof route == "object";

        if (!isRegExp)
        {

            // remove the last slash to unifiy all routes
            if (route.lastIndexOf("/") == route.length - 1)
            {
                route = route.substring(0, route.length - 1);
            }

            // if the routes where created with an absolute url ,we have to remove the absolut part anyway, since we cant change that much
            route = route.replace(location.protocol + "//", "").replace(location.hostname, "");
        }

        var routeItem = {
            route: route,
            callback: callback,
            type: isRegExp ? "regexp" : "string",
            id: id
        }

        routeList.push(routeItem);

        // we add the event listener after the first route is added so that we dont need to listen to events in vain
        if (!eventAdded)
        {
            bindStateEvents();
        }
    };

    function bindStateEvents()
    {
        eventAdded = true;

        // default value telling router that we havent replaced the url from a hash. yet.
        router.fromHash = false;

        if (hasPushState)
        {
            // if we get a request with a qualified hash (ie it begins with #!)
            if (location.hash.indexOf("#!/") === 0)
            {
                // replace the state
                var url = location.pathname + location.hash.replace(/^#!\//gi, "");
                history.replaceState({}, "", url);

                // this flag tells router that the url was converted from hash to popstate
                router.fromHash = true;
            }

            $(window).bind("popstate", handleRoutes);
        }
        else if (hasHashState)
        {
            $(window).bind("hashchange.router", handleRoutes);
        }
        else
        {
            // if no events are available we use a timer to check periodically for changes in the url
            setInterval(
                function()
                {
                    if (location.href != currentUsedUrl)
                    {
                        handleRoutes();
                        currentUsedUrl = location.href;
                    }
                }, 500
            );
        }

    }

    bindStateEvents();

    router.go = function(url, title)
    {
        if (hasPushState)
        {
            history.pushState({}, title, url);
            checkRoutes();
        }
        else
        {
            // remove part of url that we dont use
            url = url.replace(location.protocol + "//", "").replace(location.hostname, "");
            var hash = url.replace(location.pathname, "");

            if (hash.indexOf("!") < 0)
            {
                hash = "!/" + hash;
            }
            location.hash = hash;
        }
    };

    // do a check without affecting the history
    router.check = router.redo = function()
    {
        checkRoutes(true);
    };

    // parse and wash the url to process
    function parseUrl(url)
    {
        var currentUrl = url ? url : location.pathname;

        currentUrl = decodeURI(currentUrl);

        // if no pushstate is availabe we have to use the hash
        if (!hasPushState)
        {
            if (location.hash.indexOf("#!/") === 0)
            {
                currentUrl += location.hash.substring(3);
            }
            else
            {
                return '';
            }
        }

        // and if the last character is a slash, we just remove it
        currentUrl = currentUrl.replace(/\/$/, "");

        return currentUrl;
    }

    // get the current parameters for either a specified url or the current one if parameters is ommited
    router.parameters = function(url)
    {
        // parse the url so that we handle a unified url
        var currentUrl = parseUrl(url);

        // get the list of actions for the current url
        var list = getParameters(currentUrl);

        // if the list is empty, return an empty object
        if (list.length == 0)
        {
            router.currentParameters = {};
        }

        // if we got results, return the first one. at least for now
        else
        {
            router.currentParameters = list[0].data;
        }

        return router.currentParameters;
    }

    function getParameters(url)
    {

        var dataList = [];

       // console.log("ROUTES:");

        for(var i = 0, ii = routeList.length; i < ii; i++)
        {
            var route = routeList[i];

            // check for mathing reg exp
            if (route.type == "regexp")
            {
                var result = url.match(route.route);
                if (result)
                {
                    var data = {};
                    data.matches = result;

                    dataList.push(
                        {
                            route: route,
                            data: data
                        }
                    );

                    // saves the current route id
                    router.currentId = route.id;

                    // break after first hit
                    break;
                }
            }

            // check for mathing string routes
            else
            {
                var currentUrlParts = url.split("/");
                var routeParts = route.route.split("/");

                //console.log("matchCounter ", matchCounter, url, route.route)

                // first check so that they have the same amount of elements at least
                if (routeParts.length == currentUrlParts.length)
                {
                    var data = {};
                    var matched = true;
                    var matchCounter = 0;

                    for(var j = 0, jj = routeParts.length; j < jj; j++)
                    {
                        var isParam = routeParts[j].indexOf(":") === 0;
                        if (isParam)
                        {
                            data[routeParts[j].substring(1)] = decodeURI(currentUrlParts[j]);
                            matchCounter++;
                        }
                        else
                        {
                            if (routeParts[j] == currentUrlParts[j])
                            {
                                matchCounter++;
                            }
                        }
                    }

                    // break after first hit
                    if (routeParts.length == matchCounter)
                    {
                        dataList.push(
                            {
                                route: route,
                                data: data
                            }
                        );

                        // saved the current route id
                        router.currentId = route.id;
                        router.currentParameters = data;

                        break;
                    }

                }
            }

        }

        return dataList;
    }

    function checkRoutes()
    {
        var currentUrl = parseUrl(location.pathname);

        // check if something is catched
        var actionList = getParameters(currentUrl);

        // ietrate trough result (but it will only kick in one)
        for(var i = 0, ii = actionList.length; i < ii; i++)
        {
            actionList[i].route.callback(actionList[i].data);
        }
    }

    function handleRoutes(e)
    {
        if (e != null && e.originalEvent && e.originalEvent.state)
        {
            checkRoutes();
        }
        else if (hasHashState)
        {
            checkRoutes();
        }
        else if (!hasHashState && !hasPushState)
        {
            checkRoutes();
        }
    }

    if (!$.router)
    {
        $.router = router;
    }
    else
    {
        if (window.console && window.console.warn)
        {
            console.warn("jQuery.status already defined. Something is using the same name.");
        }
    }

})( jQuery );
<commit_msg>Fix bug with handling "/" route<commit_after>/*

    plugin name: router
    jquery plugin to handle routes with both hash and push state
    why? why another routing plugin? because i couldnt find one that handles both hash and pushstate
    created by 24hr // camilo.tapia
    author twitter: camilo.tapia

    Copyright 2011  camilo tapia // 24hr (email : camilo.tapia@gmail.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

(function($){

    var hasPushState = (history && history.pushState);
    var hasHashState = !hasPushState && ("onhashchange" in window) && false;
    var router = {};
    var routeList = [];
    var eventAdded = false;
    var currentUsedUrl = location.href; //used for ie to hold the current url
    var firstRoute = true;

    // hold the latest route that was activated
    router.currentId = "";
    router.currentParameters = {};

    router.capabilities = {
        hash: hasHashState,
        pushState: hasPushState,
        timer: !hasHashState && !hasPushState
    };

    // reset all routes
    router.reset = function()
    {
        var router = {};
        var routeList = [];
        router.currentId = "";
        router.currentParameters = {};
    }

    router.add = function(route, id, callback)
    {
        // if we only get a route and a callback, we switch the arguments
        if (typeof id == "function")
        {
            callback = id;
            delete id;
        }

        var isRegExp = typeof route == "object";

        if (!isRegExp)
        {

            // remove the last slash to unifiy all routes
            if (route.lastIndexOf("/") == route.length - 1)
            {
                route = route.substring(0, route.length - 1);
            }

            // if the routes where created with an absolute url ,we have to remove the absolut part anyway, since we cant change that much
            route = route.replace(location.protocol + "//", "").replace(location.hostname, "");
        }

        var routeItem = {
            route: route,
            callback: callback,
            type: isRegExp ? "regexp" : "string",
            id: id
        }

        routeList.push(routeItem);

        // we add the event listener after the first route is added so that we dont need to listen to events in vain
        if (!eventAdded)
        {
            bindStateEvents();
        }
    };

    function bindStateEvents()
    {
        eventAdded = true;

        // default value telling router that we havent replaced the url from a hash. yet.
        router.fromHash = false;

        if (hasPushState)
        {
            // if we get a request with a qualified hash (ie it begins with #!)
            if (location.hash.indexOf("#!/") === 0)
            {
                // replace the state
                var url = location.pathname + location.hash.replace(/^#!\//gi, "");
                history.replaceState({}, "", url);

                // this flag tells router that the url was converted from hash to popstate
                router.fromHash = true;
            }

            $(window).bind("popstate", handleRoutes);
        }
        else if (hasHashState)
        {
            $(window).bind("hashchange.router", handleRoutes);
        }
        else
        {
            // if no events are available we use a timer to check periodically for changes in the url
            setInterval(
                function()
                {
                    if (location.href != currentUsedUrl)
                    {
                        handleRoutes();
                        currentUsedUrl = location.href;
                    }
                }, 500
            );
        }

    }

    bindStateEvents();

    router.go = function(url, title)
    {
        if (hasPushState)
        {
            history.pushState({}, title, url);
            checkRoutes();
        }
        else
        {
            // remove part of url that we dont use
            url = url.replace(location.protocol + "//", "").replace(location.hostname, "");
            var hash = url.replace(location.pathname, "");

            if (hash.indexOf("!") < 0)
            {
                hash = "!/" + hash;
            }
            location.hash = hash;
        }
    };

    // do a check without affecting the history
    router.check = router.redo = function()
    {
        checkRoutes(true);
    };

    // parse and wash the url to process
    function parseUrl(url)
    {
        var currentUrl = url ? url : location.pathname;

        currentUrl = decodeURI(currentUrl);

        // if no pushstate is availabe we have to use the hash
        if (!hasPushState)
        {
            if (location.hash.indexOf("#!/") === 0)
            {
                currentUrl += location.hash.substring(3);
            }
            else
            {
                return '';
            }
        }

        // and if the last character is a slash, we just remove it
        currentUrl = currentUrl.replace(/\/$/, "");

        return currentUrl;
    }

    // get the current parameters for either a specified url or the current one if parameters is ommited
    router.parameters = function(url)
    {
        // parse the url so that we handle a unified url
        var currentUrl = parseUrl(url);

        // get the list of actions for the current url
        var list = getParameters(currentUrl);

        // if the list is empty, return an empty object
        if (list.length == 0)
        {
            router.currentParameters = {};
        }

        // if we got results, return the first one. at least for now
        else
        {
            router.currentParameters = list[0].data;
        }

        return router.currentParameters;
    }

    function getParameters(url)
    {

        var dataList = [];

       // console.log("ROUTES:");

        for(var i = 0, ii = routeList.length; i < ii; i++)
        {
            var route = routeList[i];

            // check for mathing reg exp
            if (route.type == "regexp")
            {
                var result = url.match(route.route);
                if (result)
                {
                    var data = {};
                    data.matches = result;

                    dataList.push(
                        {
                            route: route,
                            data: data
                        }
                    );

                    // saves the current route id
                    router.currentId = route.id;

                    // break after first hit
                    break;
                }
            }

            // check for mathing string routes
            else
            {
                var currentUrlParts = url.split("/");
                var routeParts = route.route.split("/");

                //console.log("matchCounter ", matchCounter, url, route.route)

                // first check so that they have the same amount of elements at least
                if (routeParts.length == currentUrlParts.length)
                {
                    var data = {};
                    var matched = true;
                    var matchCounter = 0;

                    for(var j = 0, jj = routeParts.length; j < jj; j++)
                    {
                        var isParam = routeParts[j].indexOf(":") === 0;
                        if (isParam)
                        {
                            data[routeParts[j].substring(1)] = decodeURI(currentUrlParts[j]);
                            matchCounter++;
                        }
                        else
                        {
                            if (routeParts[j] == currentUrlParts[j])
                            {
                                matchCounter++;
                            }
                        }
                    }

                    // break after first hit
                    if (routeParts.length == matchCounter)
                    {
                        dataList.push(
                            {
                                route: route,
                                data: data
                            }
                        );

                        // saved the current route id
                        router.currentId = route.id;
                        router.currentParameters = data;

                        break;
                    }

                }
            }

        }

        return dataList;
    }

    function checkRoutes()
    {
        var currentUrl = parseUrl(location.pathname);

        // check if something is catched
        var actionList = getParameters(currentUrl);

        // ietrate trough result (but it will only kick in one)
        for(var i = 0, ii = actionList.length; i < ii; i++)
        {
            actionList[i].route.callback(actionList[i].data);
        }
    }

    function handleRoutes(e)
    {
        if (e != null && e.originalEvent && e.originalEvent.state !== undefined)
        {
            checkRoutes();
        }
        else if (hasHashState)
        {
            checkRoutes();
        }
        else if (!hasHashState && !hasPushState)
        {
            checkRoutes();
        }
    }

    if (!$.router)
    {
        $.router = router;
    }
    else
    {
        if (window.console && window.console.warn)
        {
            console.warn("jQuery.status already defined. Something is using the same name.");
        }
    }

})( jQuery );
<|endoftext|>


<commit_before>
// ----------------------------------------------------------------
// CardDetail Class

// ----------------------------------------------------------------
// Model

class CardDetailModel extends CommonModel {
  constructor(
    _initSetting = {
      NAME: 'Card Detail Object'
    }
  ) {
    super(_initSetting);

    this.CARD_DETAIL_AREA_SELECTOR = '#card-detail-area';
    this.TEMPLATE_CARD_DETAIL_SELECTOR = '#card-detail-template';

    this.CARD_DETAIL_ADD_SELECTOR = '#card-detail-add';
    this.CARD_DETAIL_SAVE_SELECTOR = '#card-detail-save';
    this.CARD_DETAIL_DELETE_SELECTOR = '#card-detail-delete';

    this.ID = null;
    this.HASH = null;
    this.CARD = null;
    this.COPY = false;
  }
}

// ----------------------------------------------------------------
// View

class CardDetailView extends CommonView {
  constructor(
    _initSetting = {
      NAME: 'Card Detail View'
    }
  ) {
    super(_initSetting);
  }

  generateCardDetailArea(
    _alertType = 'success',
    _message = null,
    _close = true
  ) {
    $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).empty();
    super.generateAlert(
      this.MODEL.CARD_DETAIL_AREA_SELECTOR,
      _alertType,
      _message,
      _close
    );
    if (this.CARD == null) {
      // カードがある場合
      $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).append(this.getTemplate(
        this.MODEL.TEMPLATE_CARD_DETAIL_SELECTOR,
        {}
      ));
    } else {
      // カードがない場合
      $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).append(this.getTemplate(
        this.MODEL.TEMPLATE_CARD_DETAIL_SELECTOR,
        {card: this.MODEL.CARD}
      ));
    }
  }
}

// ----------------------------------------------------------------
// Event

class CardDetailEvent extends CommonEvent {
  constructor(
    _initSetting = {
      NAME: 'Card Detail Event'
    }
  ) {
    super(_initSetting);
  }

  setEvent() {
    this.setAddClick();
    this.setSaveClick();
    this.setDeleteClick();
  }

  setAddClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_ADD_SELECTOR,
      () => {
        this.CONTROLLER.addCard();
      }
    );
  }

  setSaveClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_SAVE_SELECTOR,
      () => {
        this.CONTROLLER.saveCard();
      }
    );
  }

  setDeleteClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_DELETE_SELECTOR,
      () => {
        this.CONTROLLER.deleteCard();
      }
    );
  }
}

// ----------------------------------------------------------------
// Controller

class CardDetailController extends CommonController {
  constructor(
    _model = {},
    _initSetting = {
      NAME: 'Card Detail Controller',
      MODEL: new CardDetailModel(),
      VIEW: new CardDetailView(),
      EVENT: new CardDetailEvent()
    }
  ) {
    super(_model, _initSetting);

    this.EVENT.setEvent();
  }

  openCard(
    _id = null,
    _hash = null,
    _card = null,
    _copy = false
  ) {
    this.MODEL.ID = _id;
    this.MODEL.HASH = _hash;
    this.MODEL.CARD = _card;
    this.MODEL.COPY = _copy;

    if (_id != null && _hash != null) {
      if (_card == null) {
        // カードがない場合
        if (PS.CONTROLLER.USER.MODEL.LOGIN) {
          // ログイン済み
          // カードの追加
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'カードを追加できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        } else {
          // ログインしていない
          // カードの選択
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'ログインしてカードを選択してください。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(false);
        }
      } else {
        if (_copy) {
          // カードの編集
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'コピーしたカードを追加できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        } else {
          // カードの編集
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'カードを編集できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        }
      }
    }
  }

  addCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH
  ) {

  }

  saveCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH,
    _card = this.MODEL.CARD
  ) {

  }

  deleteCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH,
    _card = this.MODEL.CARD
  ) {
    if (_id != null && _hash != null && _card != null) {

    }
  }
}
<commit_msg>Update function generateCardDetailArea in CardDetailView<commit_after>
// ----------------------------------------------------------------
// CardDetail Class

// ----------------------------------------------------------------
// Model

class CardDetailModel extends CommonModel {
  constructor(
    _initSetting = {
      NAME: 'Card Detail Object'
    }
  ) {
    super(_initSetting);

    this.CARD_DETAIL_AREA_SELECTOR = '#card-detail-area';
    this.TEMPLATE_CARD_DETAIL_SELECTOR = '#card-detail-template';

    this.CARD_DETAIL_ADD_SELECTOR = '#card-detail-add';
    this.CARD_DETAIL_SAVE_SELECTOR = '#card-detail-save';
    this.CARD_DETAIL_DELETE_SELECTOR = '#card-detail-delete';

    this.ID = null;
    this.HASH = null;
    this.CARD = null;
    this.COPY = false;
  }
}

// ----------------------------------------------------------------
// View

class CardDetailView extends CommonView {
  constructor(
    _initSetting = {
      NAME: 'Card Detail View'
    }
  ) {
    super(_initSetting);
  }

  generateCardDetailArea(
    _alertType = this.MODEL.ALERT_SUCCESS,
    _message = null,
    _close = true
  ) {
    $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).empty();
    super.generateAlert(
      this.MODEL.CARD_DETAIL_AREA_SELECTOR,
      _alertType,
      _message,
      _close
    );
    if (this.CARD == null) {
      // カードがある場合
      $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).append(this.getTemplate(
        this.MODEL.TEMPLATE_CARD_DETAIL_SELECTOR,
        {}
      ));
    } else {
      // カードがない場合
      $(this.MODEL.CARD_DETAIL_AREA_SELECTOR).append(this.getTemplate(
        this.MODEL.TEMPLATE_CARD_DETAIL_SELECTOR,
        {
          card: this.MODEL.CARD,
          copy: this.MODEL.COPY
        }
      ));
    }
  }
}

// ----------------------------------------------------------------
// Event

class CardDetailEvent extends CommonEvent {
  constructor(
    _initSetting = {
      NAME: 'Card Detail Event'
    }
  ) {
    super(_initSetting);
  }

  setEvent() {
    this.setAddClick();
    this.setSaveClick();
    this.setDeleteClick();
  }

  setAddClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_ADD_SELECTOR,
      () => {
        this.CONTROLLER.addCard();
      }
    );
  }

  setSaveClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_SAVE_SELECTOR,
      () => {
        this.CONTROLLER.saveCard();
      }
    );
  }

  setDeleteClick() {
    super.setOn(
      'click',
      this.CONTROLLER.MODEL.CARD_DETAIL_DELETE_SELECTOR,
      () => {
        this.CONTROLLER.deleteCard();
      }
    );
  }
}

// ----------------------------------------------------------------
// Controller

class CardDetailController extends CommonController {
  constructor(
    _model = {},
    _initSetting = {
      NAME: 'Card Detail Controller',
      MODEL: new CardDetailModel(),
      VIEW: new CardDetailView(),
      EVENT: new CardDetailEvent()
    }
  ) {
    super(_model, _initSetting);

    this.EVENT.setEvent();
  }

  openCard(
    _id = null,
    _hash = null,
    _card = null,
    _copy = false
  ) {
    this.MODEL.ID = _id;
    this.MODEL.HASH = _hash;
    this.MODEL.CARD = _card;
    this.MODEL.COPY = _copy;

    if (_id != null && _hash != null) {
      if (_card == null) {
        // カードがない場合
        if (PS.CONTROLLER.USER.MODEL.LOGIN) {
          // ログイン済み
          // カードの追加
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'カードを追加できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        } else {
          // ログインしていない
          // カードの選択
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'ログインしてカードを選択してください。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(false);
        }
      } else {
        if (_copy) {
          // カードの編集
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'コピーしたカードを追加できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        } else {
          // カードの編集
          this.VIEW.generateCardDetailArea(
            this.MODEL.ALERT_SUCCESS,
            'カードを編集できます。'
          );
          PS.CONTROLLER.SWITCH.CARD_DETAIL.VIEW.setView(true);
        }
      }
    }
  }

  addCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH
  ) {

  }

  saveCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH,
    _card = this.MODEL.CARD
  ) {

  }

  deleteCard(
    _id = this.MODEL.ID,
    _hash = this.MODEL.HASH,
    _card = this.MODEL.CARD
  ) {
    if (_id != null && _hash != null && _card != null) {

    }
  }
}
<|endoftext|>


<commit_before>;
// Wizard
(function ($) {

    // Enable Strict Mode
    'use strict';

    // Plugin Default Settings
    var defaults = {

        // Callbacks
        onInit: function () {
        },
        onPrev: function () {
        },
        onNext: function () {
        },
        onOther: function () {
        },
        onFinish: function () {
        },
        onStart: function () {
        },
        onBeforeStep: function () {
        },
        onStep: function () {
        },
        disableonfinish: true,
        fade: true,
        stepBar: {
            visible: true,
            type: 'breadcrumb',
            buttonClass: "btn navbar-btn btn-default",
            objectType: "button",
            useAnchor: true,
            useDivider: false,
            divider: '',
            dividerText: '',
            dividerClass: '',
            numberOfVisible: 2
        }
    };

    $.fn.wizard = function (options) {

        // Check to see if default options are Set
        if (typeof options === 'undefined') {
            options = {};
        }

        // Check to see if an element is even selected
        if (this.length === 0) return this;

        // Create Current wizard Object
        var wizard = {};
        var actions = {};
        var steps = {};
        var stepBar = {};

        if (this.length > 1) {
            wizard = this[0];
        }
        else {
            wizard = this;
        }

        var init = function () {

            // Check for HTML5 data attributes instead
            for (var key in defaults) {
                if (defaults.hasOwnProperty(key)) {
                    if (wizard.attr('data-wizard-' + key.toLowerCase())) {
                        options[key] = wizard.data(key.toLowerCase());
                    }
                }
            }

            var self = this, $self = $(this);

            // Merge Custom Plugin Settings with Default
            wizard.settings = $.extend({}, defaults, options);

            actions = wizard.find(".wizard-act");
            steps = wizard.find(".wizard-step");

            wizard.createStepBar();
            wizard._hideAll();

            actions.each(function () {
                // Register the click event for each action.
                $(this).on("click", null, function (evt) {

                    // Determine which action was clicked, call the corresponding function.
                    if ($(this).is('[data-action="next"]')) {
                        wizard._next.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="prev"]')) {
                        wizard._prev.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="finish"]')) {
                        wizard._finish.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="start"]')) {
                        wizard._start.call(self, $(this));
                        return;
                    }
                    else {
                        wizard._other.call(self, $(this));
                        return;
                    }
                });
            });

            wizard._start(self, null);

            $self.css("display", ""); // We're init'd so let's display

            wizard.settings.onInit();
        };

        wizard.createStepBar = function () {

            if (wizard.settings.stepBar.visible) {
                stepBar = wizard.find(".wizard-step-bar");

                var stepIndex = 1;
                var stepCount = steps.length;
                steps.each(function () {

                    // Only show if we are registered to do so.
                    if ($(this).attr("data-stepbar") != "false") {

                        var addDivider = false;

                        if (stepIndex < stepCount) {
                            addDivider = true;
                        }

                        wizard.createStepBarLink($(this).attr("data-step"), stepIndex, addDivider);

                        stepIndex++;
                    };

                });
            }
        }

        wizard.createStepBarLink = function (stepName, stepIndex, addDivider) {

            if (wizard.settings.stepBar.visible) {

                stepBar = wizard.find(".wizard-step-bar");

                if (stepBar) {

                    var step = steps.filter($('[data-step="' + stepName + '"]'));

                    if (step) {

                        var title = $($(step)).attr("data-step-bar-title");
                        if (!title) {
                            title = stepIndex;
                        }

                        var o = document.createElement(wizard.settings.stepBar.objectType);
                        $(o).addClass(wizard.settings.stepBar.buttonClass);
                        $(o).addClass("wizard-step-bar-button");

                        var dataTarget = $(step).attr("data-step");

                        if (wizard.settings.stepBar.useAnchor) {
                            var a = $(document.createElement("a"));

                            $(a).attr("href", "#");
                            $(a).attr("data-target", dataTarget);
                            $(a).html(title);

                            $(o).append(a);

                        }
                        else {
                            $(o).html(title);
                        }

                        if (wizard.settings.stepBar.useDivider) {
                            if (addDivider) {
                                var s = $(document.createElement(wizard.settings.stepBar.divider));
                                $(s).addClass("divider");
                                $(s).addClass(wizard.settings.stepBar.dividerClass);
                                $(s).html(wizard.settings.stepBar.dividerText);
                                $(o).append(s);
                            }
                        }

                        $(o).on("click", function () {
                            var clickTarget = $(this).attr("data-target");
                            wizard.jumpToStep(clickTarget, "other");
                            wizard.updateStepBar(clickTarget);
                        });

                        if (dataTarget) {
                            $(o).attr("data-target", dataTarget);
                        };

                        stepBar.append(o);
                    }
                }
            }
        };

        wizard.updateStepBar = function(stepName) {

            if (stepBar) {

                //wizard.settings.stepBar.objectType
                $(stepBar).find(wizard.settings.stepBar.objectType).each(function() {

                    $(this).removeClass("active");

                    // Show/hide parent steps
                    if ($(this).attr("data-target") == stepName) {
                        $(this).addClass("active");
                    };

                });
            };
        };

        wizard.jumpToStep = function (stepName, an) {

            // Find the step
            var targetStep = steps.filter('[data-step="' + stepName + '"]');

            // Hide all the steps
            wizard._hideAll();

            // Hide actions
            actions.filter("[data-show-on]").each(function() {
                var action = $(this).attr("data-action");
                var show = stepName == $(this).attr("data-show-on");
                if (action) {
                    wizard.displayAction(action, show);
                }
                else {
                    // Allows you to hide actions that have no actions defined.
                    $(this).css("display", show ? "" : "none");
                }
            });

            // Hide actions on a given step.
            actions.filter("[data-hide-on]").each(function() {
                var action = $(this).attr("data-action");
                var show = !(stepName == $(this).attr("data-hide-on"));
                if (action) {
                    wizard.displayAction(action, show);
                }
                else {
                    // Allows you to hide actions that have no actions defined.
                    $(this).css("display", show ? "" : "none");
                }
            });

            // Mark this step active
            targetStep.addClass("active");

            // Show the step (with optional fade effect)
            //wizard.settings.fade ? targetStep.fadeIn() :

            if (an) {
                if (an == "next" || an == "finish") {
                    $(targetStep).slideLeftShow();
                }
                else if (an == "prev" || an == "start") {
                    $(targetStep).slideRightShow();
                }
                else if (an == "other") {
                    $(targetStep).slideUpShow();
                }
                else {
                    targetStep.fadeIn();
                }
            }
            else {
                targetStep.fadeIn();
            }

            // Enable all the actions
            wizard.enableActions.call();

            // First step, disable start
            if ($(targetStep).is(steps.first())) {
                wizard.disableAction("start");
            };

            // Last step, disable finish, if enabled.
            if ($(targetStep).is(steps.last())) {
                if (wizard.settings.disableOnFinish) {
                    wizard.disableAction("finish");
                }
            };

            // No next step, disable action
            if (!$(targetStep).attr("data-step-next")) {
                wizard.disableAction("next");
            };

            // No prev step, disable action
            if (!$(targetStep).attr("data-step-prev")) {
                wizard.disableAction("prev");
            };

            // Inactivate actions
            actions.filter("[data-disable-until]").each(function() {
                wizard.disableAction($(this).attr("data-action"));
                var sn = $(this).attr("data-disable-until");
                if ($(targetStep).is(steps.filter('[data-step="' + sn + '"]'))) {
                    wizard.enableAction($(this).attr("data-action"), (stepName == sn));
                };
            });

            var actionName = $(targetStep).attr("data-action");
            var act = actions.filter('[data-action="' + actionName + '"]');

            wizard.updateStepBar(stepName);

            var parentTarget = $(act).attr("data-stepbar-step");
            if (parentTarget) {
                wizard.updateStepBar(parentTarget);
            }

            // Notify the onStep handler
            wizard.settings.onStep({stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this});

            $(this).trigger('obelisk.wizard.show', {stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this});

            return targetStep;

        };

        wizard.updateStep = function (stepName, targetAttribute, targetValue) {
            steps.filter('[data-step="' + stepName + '"]').attr(targetAttribute, targetValue);
        };

        wizard.displayAction = function (actionName, on, includeParent) {
            if (on == undefined) {
                on = false;
            }
            if (includeParent == undefined) {
                includeParent = false;
            }
            var action = actions.filter('[data-action="' + actionName + '"]');
            if (action) {
                action.css("display", on == true ? "" : "none");
            }
            if (includeParent) {
                action.parent("*").css("display", on == true ? "" : "none");
            }
        };

        // Disable all the actions
        wizard.disableActions = function () {
            actions.each(function () {
                $(this).attr("disabled", "disabled");
            });
        };

        // Enable all the actions
        wizard.enableActions = function () {
            actions.each(function () {
                $(this).removeAttr("disabled");
            });
        };

        // Disable a single action
        wizard.disableAction = function (actionName) {
            actions.filter('[data-action="' + actionName + '"]').attr("disabled", "disabled");
        };

        // Enable a single action
        wizard.enableAction = function (actionName) {
            actions.filter('[data-action="' + actionName + '"]').removeAttr("disabled");
        };

        wizard.jumpToAction = function (actionName) {
            $(actions.filter('[data-action="' + actionName + '"]')).trigger("click");
        };

        wizard._start = function (act) {
            var target = $(act).attr("data-target");
            if (target == undefined) {
                target = $(steps).first().attr("data-step");
            }

            wizard._show(target, "start", act, wizard.settings.onStart);
        };

        wizard._next = function (act) {
            wizard._show($(steps.filter(".active")).attr("data-step-next"), "next", act, wizard.settings.onNext);
        };

        wizard._prev = function (act) {
            wizard._show($(steps.filter(".active")).attr("data-step-prev"), "prev", act, wizard.settings.onPrev);
        };

        wizard._other = function (act) {

            var target = $(act).attr("data-target"), supress; // Supress means don't render a step target.
            if (!target) {
                supress = true;
                target = $(steps.filter(".active")).attr("data-step");
            }
            wizard._show(target, $(act).attr("data-action"), act, wizard.settings.onOther, supress);

            wizard._trigger('obelisk.wizard.other', act, $(act).attr("data-target"));
        };

        wizard._finish = function (act) {
            var target = $(act).attr("data-target");
            if (target == undefined) {
                target = $(steps).last().attr("data-step");
            }
            wizard._show(target, "finish", act, wizard.settings.onFinish);
        };

        wizard._hideAll = function () {
            steps.each(function () {
                $(this).hide();
                $(this).removeClass("active");
            });
        };

        wizard._trigger = function (eventName, act, target) {
            $(this).trigger(eventName, { action: act, target: target, wiz: this });
        };

        wizard._show = function (stepName, actionName, act, callback, suppressReload) {

            var targetStep = steps.filter('[data-step="' + stepName + '"]');

            var result = wizard.settings.onBeforeStep({ stepName: stepName, actionName: actionName, action: act, step: targetStep });
            if (result != undefined && !result) {
                return;
            }

            if (suppressReload == undefined || !suppressReload) {

                wizard.jumpToStep(stepName, actionName);
            }

            // callback
            if (callback) {
                callback({ stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this });
            };
        };

        init(); // Initialize the wizard.

        // Return the wizard object.
        return wizard;
    };

}(jQuery));
<commit_msg>Fixes typos and sliding actions.<commit_after>;
// Wizard
(function ($) {

    // Enable Strict Mode
    'use strict';

    // Plugin Default Settings
    var defaults = {

        // Callbacks
        onInit: function () {
        },
        onPrev: function () {
        },
        onNext: function () {
        },
        onOther: function () {
        },
        onFinish: function () {
        },
        onStart: function () {
        },
        onBeforeStep: function () {
        },
        onStep: function () {
        },
        disableOnfinish: true,
        fade: true,
        stepBar: {
            visible: true,
            type: 'breadcrumb',
            buttonClass: "btn navbar-btn btn-default",
            objectType: "button",
            useAnchor: true,
            useDivider: false,
            divider: '',
            dividerText: '',
            dividerClass: '',
            numberOfVisible: 2
        }
    };

    $.fn.wizard = function (options) {

        // Check to see if default options are Set
        if (typeof options === 'undefined') {
            options = {};
        }

        // Check to see if an element is even selected
        if (this.length === 0) return this;

        // Create Current wizard Object
        var wizard = {};
        var actions = {};
        var steps = {};
        var stepBar = {};

        if (this.length > 1) {
            wizard = this[0];
        }
        else {
            wizard = this;
        }

        var init = function () {

            // Check for HTML5 data attributes instead
            for (var key in defaults) {
                if (defaults.hasOwnProperty(key)) {
                    if (wizard.attr('data-wizard-' + key.toLowerCase())) {
                        options[key] = wizard.data(key.toLowerCase());
                    }
                }
            }

            var self = this, $self = $(this);

            // Merge Custom Plugin Settings with Default
            wizard.settings = $.extend({}, defaults, options);

            actions = wizard.find(".wizard-act");
            steps = wizard.find(".wizard-step");

            wizard.createStepBar();
            wizard._hideAll();

            actions.each(function () {
                // Register the click event for each action.
                $(this).on("click", null, function (evt) {

                    // Determine which action was clicked, call the corresponding function.
                    if ($(this).is('[data-action="next"]')) {
                        wizard._next.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="prev"]')) {
                        wizard._prev.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="finish"]')) {
                        wizard._finish.call(self, $(this));
                        return;
                    }
                    else if ($(this).is('[data-action="start"]')) {
                        wizard._start.call(self, $(this));
                        return;
                    }
                    else {
                        wizard._other.call(self, $(this));
                        return;
                    }
                });
            });

            wizard._start(self, null);

            $self.css("display", ""); // We're init'd so let's display

            wizard.settings.onInit();
        };

        wizard.createStepBar = function () {

            if (wizard.settings.stepBar.visible) {
                stepBar = wizard.find(".wizard-step-bar");

                var stepIndex = 1;
                var stepCount = steps.length;
                steps.each(function () {

                    // Only show if we are registered to do so.
                    if ($(this).attr("data-stepbar") != "false") {

                        var addDivider = false;

                        if (stepIndex < stepCount) {
                            addDivider = true;
                        }

                        wizard.createStepBarLink($(this).attr("data-step"), stepIndex, addDivider);

                        stepIndex++;
                    };

                });
            }
        }

        wizard.createStepBarLink = function (stepName, stepIndex, addDivider) {

            if (wizard.settings.stepBar.visible) {

                stepBar = wizard.find(".wizard-step-bar");

                if (stepBar) {

                    var step = steps.filter($('[data-step="' + stepName + '"]'));

                    if (step) {

                        var title = $($(step)).attr("data-step-bar-title");
                        if (!title) {
                            title = stepIndex;
                        }

                        var o = document.createElement(wizard.settings.stepBar.objectType);
                        $(o).addClass(wizard.settings.stepBar.buttonClass);
                        $(o).addClass("wizard-step-bar-button");

                        var dataTarget = $(step).attr("data-step");

                        if (wizard.settings.stepBar.useAnchor) {
                            var a = $(document.createElement("a"));

                            $(a).attr("href", "#");
                            $(a).attr("data-target", dataTarget);
                            $(a).html(title);

                            $(o).append(a);

                        }
                        else {
                            $(o).html(title);
                        }

                        if (wizard.settings.stepBar.useDivider) {
                            if (addDivider) {
                                var s = $(document.createElement(wizard.settings.stepBar.divider));
                                $(s).addClass("divider");
                                $(s).addClass(wizard.settings.stepBar.dividerClass);
                                $(s).html(wizard.settings.stepBar.dividerText);
                                $(o).append(s);
                            }
                        }

                        $(o).on("click", function () {
                            var clickTarget = $(this).attr("data-target");
                            wizard.jumpToStep(clickTarget, "other");
                            wizard.updateStepBar(clickTarget);
                        });

                        if (dataTarget) {
                            $(o).attr("data-target", dataTarget);
                        };

                        stepBar.append(o);
                    }
                }
            }
        };

        wizard.updateStepBar = function(stepName) {

            if (stepBar) {

                //wizard.settings.stepBar.objectType
                $(stepBar).find(wizard.settings.stepBar.objectType).each(function() {

                    $(this).removeClass("active");

                    // Show/hide parent steps
                    if ($(this).attr("data-target") == stepName) {
                        $(this).addClass("active");
                    };

                });
            };
        };

        wizard.jumpToStep = function (stepName, an) {

            // Find the step
            var targetStep = steps.filter('[data-step="' + stepName + '"]');

            // Hide all the steps
            wizard._hideAll();

            // Hide actions
            actions.filter("[data-show-on]").each(function() {
                var action = $(this).attr("data-action");
                var show = stepName == $(this).attr("data-show-on");
                if (action) {
                    wizard.displayAction(action, show);
                }
                else {
                    // Allows you to hide actions that have no actions defined.
                    $(this).css("display", show ? "" : "none");
                }
            });

            // Hide actions on a given step.
            actions.filter("[data-hide-on]").each(function() {
                var action = $(this).attr("data-action");
                var show = !(stepName == $(this).attr("data-hide-on"));
                if (action) {
                    wizard.displayAction(action, show);
                }
                else {
                    // Allows you to hide actions that have no actions defined.
                    $(this).css("display", show ? "" : "none");
                }
            });

            // Mark this step active
            targetStep.addClass("active");

            // Show the step (with optional fade effect)
            //wizard.settings.fade ? targetStep.fadeIn() :

            if (an) {
                if (an == "next" || an == "finish") {
                    $(targetStep).show('slide', { direction: 'left' });
                }
                else if (an == "prev" || an == "start") {
                    $(targetStep).show('slide', { direction: 'right' });
                }
                else if (an == "other") {
                    $(targetStep).show('slide', { direction: 'up' });
                }
                else {
                    targetStep.fadeIn();
                }
            }
            else {
                targetStep.fadeIn();
            }

            // Enable all the actions
            wizard.enableActions.call();

            // First step, disable start
            if ($(targetStep).is(steps.first())) {
                wizard.disableAction("start");
            };

            // Last step, disable finish, if enabled.
            if ($(targetStep).is(steps.last())) {
                if (wizard.settings.disableOnFinish) {
                    wizard.disableAction("finish");
                }
            };

            // No next step, disable action
            if (!$(targetStep).attr("data-step-next")) {
                wizard.disableAction("next");
            };

            // No prev step, disable action
            if (!$(targetStep).attr("data-step-prev")) {
                wizard.disableAction("prev");
            };

            // Inactivate actions
            actions.filter("[data-disable-until]").each(function() {
                wizard.disableAction($(this).attr("data-action"));
                var sn = $(this).attr("data-disable-until");
                if ($(targetStep).is(steps.filter('[data-step="' + sn + '"]'))) {
                    wizard.enableAction($(this).attr("data-action"), (stepName == sn));
                };
            });

            var actionName = $(targetStep).attr("data-action");
            var act = actions.filter('[data-action="' + actionName + '"]');

            wizard.updateStepBar(stepName);

            var parentTarget = $(act).attr("data-stepbar-step");
            if (parentTarget) {
                wizard.updateStepBar(parentTarget);
            }

            // Notify the onStep handler
            wizard.settings.onStep({stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this});

            $(this).trigger('obelisk.wizard.show', {stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this});

            return targetStep;

        };

        wizard.updateStep = function (stepName, targetAttribute, targetValue) {
            steps.filter('[data-step="' + stepName + '"]').attr(targetAttribute, targetValue);
        };

        wizard.displayAction = function (actionName, on, includeParent) {
            if (on == undefined) {
                on = false;
            }
            if (includeParent == undefined) {
                includeParent = false;
            }
            var action = actions.filter('[data-action="' + actionName + '"]');
            if (action) {
                action.css("display", on == true ? "" : "none");
            }
            if (includeParent) {
                action.parent("*").css("display", on == true ? "" : "none");
            }
        };

        // Disable all the actions
        wizard.disableActions = function () {
            actions.each(function () {
                $(this).attr("disabled", "disabled");
            });
        };

        // Enable all the actions
        wizard.enableActions = function () {
            actions.each(function () {
                $(this).removeAttr("disabled");
            });
        };

        // Disable a single action
        wizard.disableAction = function (actionName) {
            actions.filter('[data-action="' + actionName + '"]').attr("disabled", "disabled");
        };

        // Enable a single action
        wizard.enableAction = function (actionName) {
            actions.filter('[data-action="' + actionName + '"]').removeAttr("disabled");
        };

        wizard.jumpToAction = function (actionName) {
            $(actions.filter('[data-action="' + actionName + '"]')).trigger("click");
        };

        wizard._start = function (act) {
            var target = $(act).attr("data-target");
            if (target == undefined) {
                target = $(steps).first().attr("data-step");
            }

            wizard._show(target, "start", act, wizard.settings.onStart);
        };

        wizard._next = function (act) {
            wizard._show($(steps.filter(".active")).attr("data-step-next"), "next", act, wizard.settings.onNext);
        };

        wizard._prev = function (act) {
            wizard._show($(steps.filter(".active")).attr("data-step-prev"), "prev", act, wizard.settings.onPrev);
        };

        wizard._other = function (act) {

            var target = $(act).attr("data-target"), supress; // Supress means don't render a step target.
            if (!target) {
                supress = true;
                target = $(steps.filter(".active")).attr("data-step");
            }
            wizard._show(target, $(act).attr("data-action"), act, wizard.settings.onOther, supress);

            wizard._trigger('obelisk.wizard.other', act, $(act).attr("data-target"));
        };

        wizard._finish = function (act) {
            var target = $(act).attr("data-target");
            if (target == undefined) {
                target = $(steps).last().attr("data-step");
            }
            wizard._show(target, "finish", act, wizard.settings.onFinish);
        };

        wizard._hideAll = function () {
            steps.each(function () {
                $(this).hide();
                $(this).removeClass("active");
            });
        };

        wizard._trigger = function (eventName, act, target) {
            $(this).trigger(eventName, { action: act, target: target, wiz: this });
        };

        wizard._show = function (stepName, actionName, act, callback, suppressReload) {

            var targetStep = steps.filter('[data-step="' + stepName + '"]');

            var result = wizard.settings.onBeforeStep({ stepName: stepName, actionName: actionName, action: act, step: targetStep });
            if (result != undefined && !result) {
                return;
            }

            if (suppressReload == undefined || !suppressReload) {

                wizard.jumpToStep(stepName, actionName);
            }

            // callback
            if (callback) {
                callback({ stepName: stepName, actionName: actionName, action: act, step: targetStep, wiz: this });
            };
        };

        init(); // Initialize the wizard.

        // Return the wizard object.
        return wizard;
    };

}(jQuery));
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('../../lib/')._;

var log = require('../../lib/').bows('Puddle');

module.exports = function(opts) {

  opts = opts || {};

  var defaults = {
    headSize: 16,
    leadSize: 14,
    displaySize: 24
  };

  _.defaults(opts, defaults);

  var height;

  function puddle(selection, txt) {
    selection.call(puddle.addHead);
    selection.call(puddle.addLead);
  }

  puddle.dataDisplay = function(selection, display) {
    selection.selectAll('text.d3-stats-display').remove();
    var displayGroup = selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': opts.height / 2 + opts.leadSize,
        'class': 'd3-stats-display'
      });

    display.forEach(function(txt) {
      displayGroup.append('tspan')
        .attr('class', txt['class'])
        .text(txt.text);
    });
  };

  puddle.addHead = _.once(function(selection) {
    selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': 0,
        'class': 'd3-stats-head'
      })
      .text(opts.head);
  });

  puddle.addLead = _.once(function(selection) {
    selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': opts.height / 2,
        'class': 'd3-stats-lead'
      })
      .text(opts.lead);
  });

  puddle.width = function(x) {
    if (!arguments.length) return opts.width;
    opts.width = x;
    return puddle;
  };

  puddle.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return puddle;
  };

  puddle.id = opts.id;

  puddle.weight = opts.weight;

  puddle.pie = opts.pie;

  return puddle;
};<commit_msg>add xPosition getter & setter to aid positioning annotations<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('../../lib/')._;

var log = require('../../lib/').bows('Puddle');

module.exports = function(opts) {

  opts = opts || {};

  var defaults = {
    headSize: 16,
    leadSize: 14,
    displaySize: 24
  };

  _.defaults(opts, defaults);

  var height, xPosition;

  function puddle(selection, txt) {
    selection.call(puddle.addHead);
    selection.call(puddle.addLead);
  }

  puddle.dataDisplay = function(selection, display) {
    selection.selectAll('text.d3-stats-display').remove();
    var displayGroup = selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': opts.height / 2 + opts.leadSize,
        'class': 'd3-stats-display'
      });

    display.forEach(function(txt) {
      displayGroup.append('tspan')
        .attr('class', txt['class'])
        .text(txt.text);
    });
  };

  puddle.addHead = _.once(function(selection) {
    selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': 0,
        'class': 'd3-stats-head'
      })
      .text(opts.head);
  });

  puddle.addLead = _.once(function(selection) {
    selection.append('text')
      .attr({
        'x': opts.xOffset,
        'y': opts.height / 2,
        'class': 'd3-stats-lead'
      })
      .text(opts.lead);
  });

  puddle.xPosition = function(x) {
    if (!arguments.length) return xPosition;
    xPosition = x;
    return puddle;
  };

  puddle.width = function(x) {
    if (!arguments.length) return opts.width;
    opts.width = x;
    return puddle;
  };

  puddle.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return puddle;
  };

  puddle.id = opts.id;

  puddle.weight = opts.weight;

  puddle.pie = opts.pie;

  return puddle;
};<|endoftext|>


<commit_before>"use strict";

var FlagLoader = {

  init: function FL_init(cset, tree, loadCallback, errorCallback) {
    // Bug 1159415: Short term tweak to set the firefox38.0.5 flags
    var thisDate = new Date();
    var betaDate = new Date(2015, 4, 11);
    var releaseDate = new Date(2015, 5, 29);
    if ((thisDate < betaDate && tree == 'mozilla-beta') ||
        (betaDate < thisDate && thisDate < releaseDate && tree == 'mozilla-release')) {
      var flags = this.generateFlags('firefox38_0_5');
      loadCallback(flags);
      return;
    }
    // The version for some repositories is a constant, since they're release branches.
    // We can infer their version from the repo name to avoid querying the repo.
    var esrVersion = /-esr(\d+)$/.exec(tree);
    if (esrVersion) {
      var esrName = 'firefox_esr';
      if (tree.indexOf('comm') != -1) {
        esrName = 'thunderbird_esr';
      }
      var flags = this.generateFlags(esrName + esrVersion[1]);
      loadCallback(flags);
      return;
    }
    if (tree.indexOf('mozilla-b2g') != -1) {
      // Hardcode some b2g release branch flags
      var flags;
      switch(tree) {
        case "mozilla-b2g30_v1_4":
          flags = this.generateFlags("b2g_1_4");
          break;
        case "mozilla-b2g32_v2_0":
          flags = this.generateFlags("b2g_2_0");
          break;
        case "mozilla-b2g34_v2_1":
          flags = this.generateFlags("b2g_2_1");
          break;
        case "mozilla-b2g37_v2_2":
          flags = this.generateFlags("b2g_2_2");
          break;
        default:
          errorCallback(null, 'unknown tree');
          flags = {};
          return;
      }
      loadCallback(flags);
      return;
    }
    var treeInfo = Config.treeInfo[tree];
    var productName = 'firefox';
    var fileLocation = '/browser/config/version.txt';
    if (tree.indexOf('comm') != -1) {
      productName = 'thunderbird';
      fileLocation = '/mail/config/version.txt';
    }
    var self = this;
    $.ajax({
      url: Config.hgBaseURL + treeInfo.repo + '/raw-file/' + cset + fileLocation,
      dataType: 'text',
      success: function FL_ajaxSuccessCallback(data) {
        self.parseData(data, productName, loadCallback, errorCallback);
      },
      error: errorCallback
    });
  },

  parseData: function FL_parseData(data, productName, loadCallback, errorCallback) {
    // The version number is of form: "36.0a1", "35.0a2, "34.0" etc.
    var version = /^([1-9][\d]*)(?:\.[\d]+)?/.exec(data);
    if (!version) {
      errorCallback(null, 'Unable to parse version.txt');
      loadCallback({});
      return;
    }
    var flags = this.generateFlags(productName + version[1]);
    loadCallback(flags);
  },

  generateFlags: function FL_generateFlags(flagSuffix) {
    // B2G doesn't use tracking flags, only status
    if(flagSuffix.indexOf('b2g_') != -1) {
      return {'status': 'status_' + flagSuffix};
    } else {
      return {'tracking': 'tracking_' + flagSuffix,
              'status': 'status_' + flagSuffix};
    }
  }
};
<commit_msg>Bug 1161531 - Move the 38.0.5 date check up a week because the merge happened this week, not next<commit_after>"use strict";

var FlagLoader = {

  init: function FL_init(cset, tree, loadCallback, errorCallback) {
    // Bug 1159415: Short term tweak to set the firefox38.0.5 flags
    var thisDate = new Date();
    var betaDate = new Date(2015, 4, 4);
    var releaseDate = new Date(2015, 5, 29);
    if ((thisDate < betaDate && tree == 'mozilla-beta') ||
        (betaDate < thisDate && thisDate < releaseDate && tree == 'mozilla-release')) {
      var flags = this.generateFlags('firefox38_0_5');
      loadCallback(flags);
      return;
    }
    // The version for some repositories is a constant, since they're release branches.
    // We can infer their version from the repo name to avoid querying the repo.
    var esrVersion = /-esr(\d+)$/.exec(tree);
    if (esrVersion) {
      var esrName = 'firefox_esr';
      if (tree.indexOf('comm') != -1) {
        esrName = 'thunderbird_esr';
      }
      var flags = this.generateFlags(esrName + esrVersion[1]);
      loadCallback(flags);
      return;
    }
    if (tree.indexOf('mozilla-b2g') != -1) {
      // Hardcode some b2g release branch flags
      var flags;
      switch(tree) {
        case "mozilla-b2g30_v1_4":
          flags = this.generateFlags("b2g_1_4");
          break;
        case "mozilla-b2g32_v2_0":
          flags = this.generateFlags("b2g_2_0");
          break;
        case "mozilla-b2g34_v2_1":
          flags = this.generateFlags("b2g_2_1");
          break;
        case "mozilla-b2g37_v2_2":
          flags = this.generateFlags("b2g_2_2");
          break;
        default:
          errorCallback(null, 'unknown tree');
          flags = {};
          return;
      }
      loadCallback(flags);
      return;
    }
    var treeInfo = Config.treeInfo[tree];
    var productName = 'firefox';
    var fileLocation = '/browser/config/version.txt';
    if (tree.indexOf('comm') != -1) {
      productName = 'thunderbird';
      fileLocation = '/mail/config/version.txt';
    }
    var self = this;
    $.ajax({
      url: Config.hgBaseURL + treeInfo.repo + '/raw-file/' + cset + fileLocation,
      dataType: 'text',
      success: function FL_ajaxSuccessCallback(data) {
        self.parseData(data, productName, loadCallback, errorCallback);
      },
      error: errorCallback
    });
  },

  parseData: function FL_parseData(data, productName, loadCallback, errorCallback) {
    // The version number is of form: "36.0a1", "35.0a2, "34.0" etc.
    var version = /^([1-9][\d]*)(?:\.[\d]+)?/.exec(data);
    if (!version) {
      errorCallback(null, 'Unable to parse version.txt');
      loadCallback({});
      return;
    }
    var flags = this.generateFlags(productName + version[1]);
    loadCallback(flags);
  },

  generateFlags: function FL_generateFlags(flagSuffix) {
    // B2G doesn't use tracking flags, only status
    if(flagSuffix.indexOf('b2g_') != -1) {
      return {'status': 'status_' + flagSuffix};
    } else {
      return {'tracking': 'tracking_' + flagSuffix,
              'status': 'status_' + flagSuffix};
    }
  }
};
<|endoftext|>


<commit_before><commit_msg>添加Menubar针对setPositionByElement的测试用例<commit_after><|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

//TODO this should extend sun.Button
//TODO this should handle wiring up the callback that goes to the home screen, currently done in NavigationBar
/**
 * The Home button that appears in the navigation bar.
 */
define( function( require ) {
  'use strict';

  // imports
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Shape = require( 'KITE/Shape' );

  function HomeButton( fill, options ) {

    options = _.extend( { cursor: 'pointer' }, options );

    Node.call( this, options );

    var icon = new FontAwesomeNode( 'home', { fill: fill, scale: 0.75 } );
    this.mouseArea = this.touchArea = Shape.rectangle( icon.bounds.minX, icon.bounds.minY, icon.bounds.width, icon.bounds.height );
    this.addChild( icon );
  }

  inherit( Node, HomeButton );

  return HomeButton;
} );<commit_msg>Rewrote the home button to use PushButton, see https://github.com/phetsims/joist/issues/90<commit_after>// Copyright 2002-2013, University of Colorado Boulder

//TODO this should extend sun.Button
//TODO this should handle wiring up the callback that goes to the home screen, currently done in NavigationBar
/**
 * The Home button that appears in the navigation bar.
 */
define( function( require ) {
  'use strict';

  // imports
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var PushButton = require( 'SUN/PushButton' );
  var Highlight = require( 'JOIST/Highlight' );

  function HomeButton( fill, pressedFill, options ) {

    var icon = function( fill, highlighted ) {
      var node = new FontAwesomeNode( 'home', { fill: fill, scale: 0.75 } );
      if ( highlighted ) {
        var highlight = Highlight.createHighlightVisible( node.width + 4, node.height );
        highlight.centerX = node.centerX;
        highlight.centerY = node.centerY;
        return new Node( {children: [node, highlight]} );
      }
      else {
        return node;
      }
    };
    PushButton.call( this, icon( fill, false ), icon( fill, true ), icon( pressedFill, true ), icon( fill, false ) );

    if ( options ) {
      this.mutate( options );
    }
    this.mouseArea = this.touchArea = Shape.rectangle( this.bounds.minX, this.bounds.minY, this.bounds.width, this.bounds.height );
  }

  return inherit( PushButton, HomeButton );
} );<|endoftext|>


<commit_before><commit_msg>Add missing tests for #checkHeaders()<commit_after><|endoftext|>