Clean up the minification stuff a bit; still not really in use.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 24 Nov 2014 20:17:50 +0000 (21:17 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 24 Nov 2014 20:17:50 +0000 (21:17 +0100)
build.sh [new file with mode: 0755]
externs/jquery-1.9.js [new file with mode: 0644]
externs/webstorage.js [new file with mode: 0644]
www/index.html
www/js/chess.js [new file with mode: 0644]

diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
index 0000000..a03d72b
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+# Download http://dl.google.com/closure-compiler/compiler-latest.zip
+# and unzip it in closure/ before running this script.
+
+java -jar closure/compiler.jar \
+       --language_in ECMASCRIPT5 \
+       --compilation_level SIMPLE \
+       --js_output_file=www/js/remoteglot.min.js \
+       --externs externs/jquery-1.9.js \
+       --externs externs/webstorage.js \
+       www/js/chessboard-0.3.0.js \
+       www/js/chess.js \
+       www/js/remoteglot.js
+
diff --git a/externs/jquery-1.9.js b/externs/jquery-1.9.js
new file mode 100644 (file)
index 0000000..f6f8e89
--- /dev/null
@@ -0,0 +1,2160 @@
+/*
+ * Copyright 2011 The Closure Compiler Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Externs for jQuery 1.9.1
+ *
+ * Note that some functions use different return types depending on the number
+ * of parameters passed in. In these cases, you may need to annotate the type
+ * of the result in your code, so the JSCompiler understands which type you're
+ * expecting. For example:
+ *    <code>var elt = /** @type {Element} * / (foo.get(0));</code>
+ *
+ * @see http://api.jquery.com/
+ * @externs
+ */
+
+/**
+ * @typedef {(Window|Document|Element|Array.<Element>|string|jQuery|
+ *     NodeList)}
+ */
+var jQuerySelector;
+
+/** @typedef {function(...)|Array.<function(...)>} */
+var jQueryCallback;
+
+/** @typedef {
+              {
+               accepts: (Object.<string, string>|undefined),
+               async: (?boolean|undefined),
+               beforeSend: (function(jQuery.jqXHR, (jQueryAjaxSettings|Object.<string, *>))|undefined),
+               cache: (?boolean|undefined),
+               complete: (function(jQuery.jqXHR, string)|undefined),
+               contents: (Object.<string, RegExp>|undefined),
+               contentType: (?string|undefined),
+               context: (Object.<?, ?>|jQueryAjaxSettings|undefined),
+               converters: (Object.<string, Function>|undefined),
+               crossDomain: (?boolean|undefined),
+               data: (Object.<?, ?>|?string|Array.<?>|undefined),
+               dataFilter: (function(string, string):?|undefined),
+               dataType: (?string|undefined),
+               error: (function(jQuery.jqXHR, string, string)|undefined),
+               global: (?boolean|undefined),
+               headers: (Object.<?, ?>|undefined),
+               ifModified: (?boolean|undefined),
+               isLocal: (?boolean|undefined),
+               jsonp: (?string|undefined),
+               jsonpCallback: (?string|function()|undefined),
+               mimeType: (?string|undefined),
+               password: (?string|undefined),
+               processData: (?boolean|undefined),
+               scriptCharset: (?string|undefined),
+               statusCode: (Object.<number, function()>|undefined),
+               success: (function(?, string, jQuery.jqXHR)|undefined),
+               timeout: (?number|undefined),
+               traditional: (?boolean|undefined),
+               type: (?string|undefined),
+               url: (?string|undefined),
+               username: (?string|undefined),
+               xhr: (function():(ActiveXObject|XMLHttpRequest)|undefined),
+               xhrFields: (Object.<?, ?>|undefined)
+              }} */
+var jQueryAjaxSettings;
+
+/**
+ * @constructor
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ *     function())=} arg1
+ * @param {(Element|jQuery|Document|
+ *     Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function jQuery(arg1, arg2) {}
+
+/**
+ * @constructor
+ * @extends {jQuery}
+ * @param {(jQuerySelector|Element|Object|Array.<Element>|jQuery|string|
+ *     function())=} arg1
+ * @param {(Element|jQuery|Document|
+ *     Object.<string, (string|function(!jQuery.event=))>)=} arg2
+ * @return {!jQuery}
+ */
+function $(arg1, arg2) {}
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.add = function(arg1, context) {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|string|jQuery)=} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.addBack = function(arg1) {};
+
+/**
+ * @param {(string|function(number,String))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.addClass = function(arg1) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number))} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.after = function(arg1, content) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string,*>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+jQuery.ajax = function(arg1, settings) {};
+
+/**
+ * @param {(string|jQueryAjaxSettings|Object.<string, *>)} arg1
+ * @param {(jQueryAjaxSettings|Object.<string, *>)=} settings
+ * @return {jQuery.jqXHR}
+ */
+$.ajax = function(arg1, settings) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxComplete = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>),*)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxError = function(handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+jQuery.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {(string|function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR))} dataTypes
+ * @param {function((jQueryAjaxSettings|Object.<string, *>),(jQueryAjaxSettings|Object.<string, *>),jQuery.jqXHR)=} handler
+ */
+$.ajaxPrefilter = function(dataTypes, handler) {};
+
+/**
+ * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.<string, *>))} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSend = function(handler) {};
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+jQuery.ajaxSettings;
+
+/** @const {jQueryAjaxSettings|Object.<string, *>} */
+$.ajaxSettings = {};
+
+/** @type {Object.<string, boolean>} */
+jQuery.ajaxSettings.flatOptions = {};
+
+/** @type {Object.<string, boolean>} */
+$.ajaxSettings.flatOptions = {};
+
+/** @type {boolean} */
+jQuery.ajaxSettings.processData;
+
+/** @type {boolean} */
+$.ajaxSettings.processData;
+
+/** @type {Object.<string, string>} */
+jQuery.ajaxSettings.responseFields = {};
+
+/** @type {Object.<string, string>} */
+$.ajaxSettings.responseFields = {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+jQuery.ajaxSetup = function(options) {};
+
+/** @param {jQueryAjaxSettings|Object.<string, *>} options */
+$.ajaxSetup = function(options) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStart = function(handler) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxStop = function(handler) {};
+
+/**
+ * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.<string, *>), ?)} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ajaxSuccess = function(handler) {};
+
+/**
+ * @deprecated Please use .addBack(selector) instead.
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.andSelf = function() {};
+
+/**
+ * @param {Object.<string,*>} properties
+ * @param {(string|number|function()|Object.<string,*>)=} arg2
+ * @param {(string|function())=} easing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.animate = function(properties, arg2, easing, complete) {};
+
+/**
+ * @param {(string|Element|Array.<Element>|jQuery|function(number,string))} arg1
+ * @param {...(string|Element|Array.<Element>|jQuery)} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.append = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.appendTo = function(target) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,string))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.attr = function(arg1, arg2) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @param {(string|Element|Array.<Element>|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.before = function(arg1, content) {};
+
+/**
+ * @param {(string|Object.<string, function(!jQuery.event=)>)} arg1
+ * @param {(Object.<string, *>|function(!jQuery.event=)|boolean)=} eventData
+ * @param {(function(!jQuery.event=)|boolean)=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.bind = function(arg1, eventData, arg3) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.blur = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @private
+ */
+jQuery.callbacks = function () {};
+
+/**
+ * @param {string=} flags
+ * @return {jQuery.callbacks}
+ */
+jQuery.Callbacks = function (flags) {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.add = function(callbacks) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.disable = function() {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.empty = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fire = function(var_args) {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.fired = function() {};
+
+/** @param {...*} var_args */
+jQuery.callbacks.prototype.fireWith = function(var_args) {};
+
+/**
+ * @param {function()} callback
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.callbacks.prototype.has = function(callback) {};
+
+/** @return {undefined} */
+jQuery.callbacks.prototype.lock = function() {};
+
+/** @return {boolean} */
+jQuery.callbacks.prototype.locked = function() {};
+
+/** @param {function()} callbacks */
+jQuery.callbacks.prototype.remove = function(callbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.change = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.children = function(selector) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.clearQueue = function(queueName) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.click = function(arg1, handler) {};
+
+/**
+ * @param {boolean=} withDataAndEvents
+ * @param {boolean=} deepWithDataAndEvents
+ * @return {!jQuery}
+ * @suppress {checkTypes} see https://code.google.com/p/closure-compiler/issues/detail?id=583
+ */
+jQuery.prototype.clone = function(withDataAndEvents, deepWithDataAndEvents) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element|string)} arg1
+ * @param {Element=} context
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.closest = function(arg1, context) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+jQuery.contains = function(container, contained) {};
+
+/**
+ * @param {Element} container
+ * @param {Element} contained
+ * @return {boolean}
+ */
+$.contains = function(container, contained) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.contents = function() {};
+
+/** @type {Element|Document} */
+jQuery.prototype.context;
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|function(number,*))=} arg2
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.css = function(arg1, arg2) {};
+
+/** @type {Object.<string, *>} */
+jQuery.cssHooks;
+
+/** @type {Object.<string, *>} */
+$.cssHooks;
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.data = function(elem, key, value) {};
+
+/**
+ * @param {(string|Object.<string, *>)=} arg1
+ * @param {*=} value
+ * @return {*}
+ */
+jQuery.prototype.data = function(arg1, value) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} key
+ * @param {*=} value
+ * @return {*}
+ */
+$.data = function(elem, key, value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.dblclick = function(arg1, handler) {};
+
+/**
+ * @constructor
+ * @implements {jQuery.Promise}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+jQuery.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.Deferred}
+ */
+jQuery.Deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @see http://api.jquery.com/category/deferred-object/
+ */
+$.deferred = function(opt_fn) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.deferred}
+ * @param {function()=} opt_fn
+ * @return {jQuery.deferred}
+ */
+$.Deferred = function(opt_fn) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.always
+    = function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} doneCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.done = function(doneCallbacks, doneCallbacks2) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @param {jQueryCallback=} failCallbacks2
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.fail = function(failCallbacks, failCallbacks2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notify = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.notifyWith = function(context, var_args) {};
+
+/**
+ * @deprecated Please use deferred.then() instead.
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.pipe =
+    function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.progress = function(progressCallbacks) {};
+
+/**
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.deferred.prototype.promise = function(target) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.reject = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.rejectWith = function(context, args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolve = function(var_args) {};
+
+/**
+ * @param {Object} context
+ * @param {Array.<*>=} args
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.resolveWith = function(context, args) {};
+
+/** @return {string} */
+jQuery.deferred.prototype.state = function() {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.deferred}
+ */
+jQuery.deferred.prototype.then
+    = function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {number} duration
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.delay = function(duration, queueName) {};
+
+/**
+ * @param {string} selector
+ * @param {(string|Object.<string,*>)} arg2
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.delegate = function(selector, arg2, arg3, handler) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+jQuery.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {string=} queueName
+ * @return {!jQuery}
+ */
+jQuery.prototype.dequeue = function(queueName) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ */
+$.dequeue = function(elem, queueName) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.detach = function(selector) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+jQuery.each = function(collection, callback) {};
+
+/**
+ * @param {function(number,Element)} fnc
+ * @return {!jQuery}
+ */
+jQuery.prototype.each = function(fnc) {};
+
+/**
+ * @param {Object} collection
+ * @param {function((number|string),?)} callback
+ * @return {Object}
+ */
+$.each = function(collection, callback) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.empty = function() {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.end = function() {};
+
+/**
+ * @param {number} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.eq = function(arg1) {};
+
+/** @param {string} message */
+jQuery.error = function(message) {};
+
+/**
+ * @deprecated Please use .on( "error", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.error = function(arg1, handler) {};
+
+/** @param {string} message */
+$.error = function(message) {};
+
+/**
+ * @constructor
+ * @param {string} eventType
+ */
+jQuery.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {jQuery.Event}
+ */
+jQuery.Event = function(eventType, properties) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ */
+$.event = function(eventType) {};
+
+/**
+ * @constructor
+ * @extends {jQuery.event}
+ * @param {string} eventType
+ * @param {Object=} properties
+ * @return {$.Event}
+ */
+$.Event = function(eventType, properties) {};
+
+/** @type {Element} */
+jQuery.event.prototype.currentTarget;
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.data;
+
+/** @type {Element} */
+jQuery.event.prototype.delegateTarget;
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isDefaultPrevented = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isImmediatePropagationStopped = function() {};
+
+/**
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.event.prototype.isPropagationStopped = function() {};
+
+/** @type {string} */
+jQuery.event.prototype.namespace;
+
+/** @type {Event} */
+jQuery.event.prototype.originalEvent;
+
+/** @type {number} */
+jQuery.event.prototype.pageX;
+
+/** @type {number} */
+jQuery.event.prototype.pageY;
+
+/** @return {undefined} */
+jQuery.event.prototype.preventDefault = function() {};
+
+/** @type {Object.<string, *>} */
+jQuery.event.prototype.props;
+
+/** @type {Element} */
+jQuery.event.prototype.relatedTarget;
+
+/** @type {*} */
+jQuery.event.prototype.result;
+
+/** @return {undefined} */
+jQuery.event.prototype.stopImmediatePropagation = function() {};
+
+/** @return {undefined} */
+jQuery.event.prototype.stopPropagation = function() {};
+
+/** @type {Element} */
+jQuery.event.prototype.target;
+
+/** @type {number} */
+jQuery.event.prototype.timeStamp;
+
+/** @type {string} */
+jQuery.event.prototype.type;
+
+/** @type {number} */
+jQuery.event.prototype.which;
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+jQuery.prototype.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(Object|boolean)} arg1
+ * @param {...*} var_args
+ * @return {Object}
+ */
+$.extend = function(arg1, var_args) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeIn = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeOut = function(duration, arg2, callback) {};
+
+/**
+ * @param {(string|number)} duration
+ * @param {number} opacity
+ * @param {(function()|string)=} arg3
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeTo = function(duration, opacity, arg3, callback) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(string|function())=} easing
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.fadeToggle = function(duration, easing, callback) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|Element|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.filter = function(arg1) {};
+
+/**
+ * @param {(jQuerySelector|jQuery|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.find = function(arg1) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.first = function() {};
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+jQuery.fn = jQuery.prototype;
+
+/** @see http://docs.jquery.com/Plugins/Authoring */
+$.fn = $.prototype;
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focus = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusin = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.focusout = function(arg1, handler) {};
+
+/** @const */
+jQuery.fx = {};
+
+/** @const */
+$.fx = {};
+
+/** @type {number} */
+jQuery.fx.interval;
+
+/** @type {number} */
+$.fx.interval;
+
+/** @type {boolean} */
+jQuery.fx.off;
+
+/** @type {boolean} */
+$.fx.off;
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ *     function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {number=} index
+ * @return {(Element|Array.<Element>)}
+ * @nosideeffects
+ */
+jQuery.prototype.get = function(index) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ *     function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.get = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ *     function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+jQuery.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|
+ *     function(Object.<string,*>,string,jQuery.jqXHR))=} data
+ * @param {function(Object.<string,*>,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ * @see http://api.jquery.com/jquery.getjson/#jQuery-getJSON-url-data-success
+ */
+$.getJSON = function(url, data, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+jQuery.getScript = function(url, success) {};
+
+/**
+ * @param {string} url
+ * @param {function(Node,string,jQuery.jqXHR)=} success
+ * @return {jQuery.jqXHR}
+ */
+$.getScript = function(url, success) {};
+
+/** @param {string} code */
+jQuery.globalEval = function(code) {};
+
+/** @param {string} code */
+$.globalEval = function(code) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+jQuery.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {Array.<*>} arr
+ * @param {function(*,number)} fnc
+ * @param {boolean=} invert
+ * @return {Array.<*>}
+ */
+$.grep = function(arr, fnc, invert) {};
+
+/**
+ * @param {(string|Element)} arg1
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.has = function(arg1) {};
+
+/**
+ * @param {string} className
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.prototype.hasClass = function(className) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.hasData = function(elem) {};
+
+/**
+ * @param {Element} elem
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.hasData = function(elem) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.height = function(arg1) {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.hide = function(duration, arg2, callback) {};
+
+/** @param {boolean} hold */
+jQuery.holdReady = function(hold) {};
+
+/** @param {boolean} hold */
+$.holdReady = function(hold) {};
+
+/**
+ * @param {function(!jQuery.event=)} arg1
+ * @param {function(!jQuery.event=)=} handlerOut
+ * @return {!jQuery}
+ */
+jQuery.prototype.hover = function(arg1, handlerOut) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.html = function(arg1) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {*} value
+ * @param {Array.<*>} arr
+ * @param {number=} fromIndex
+ * @return {number}
+ * @nosideeffects
+ */
+$.inArray = function(value, arr, fromIndex) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)=} arg1
+ * @return {number}
+ */
+jQuery.prototype.index = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerHeight = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.innerWidth = function() {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertAfter = function(target) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.insertBefore = function(target) {};
+
+/**
+ * @param {(jQuerySelector|function(number)|jQuery|Element)} arg1
+ * @return {boolean}
+ */
+jQuery.prototype.is = function(arg1) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isArray = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isEmptyObject = function(obj) {};
+
+/**
+ * @param {Object} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isEmptyObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isFunction = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isFunction = function(obj) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isNumeric = function(value) {};
+
+/**
+ * @param {*} value
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isNumeric = function(value) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isPlainObject = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isWindow = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isWindow = function(obj) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+jQuery.isXMLDoc = function(node) {};
+
+/**
+ * @param {Element} node
+ * @return {boolean}
+ * @nosideeffects
+ */
+$.isXMLDoc = function(node) {};
+
+/** @type {string} */
+jQuery.prototype.jquery;
+
+/**
+ * @constructor
+ * @extends {XMLHttpRequest}
+ * @implements {jQuery.Promise}
+ * @private
+ * @see http://api.jquery.com/jQuery.ajax/#jqXHR
+ */
+jQuery.jqXHR = function () {};
+
+/**
+ * @override
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.always =
+    function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.complete = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.error = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @deprecated
+ * @override
+ */
+jQuery.jqXHR.prototype.onreadystatechange = function (callback) {};
+
+/**
+ * @override
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.pipe =
+    function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @deprecated
+ * @param {function()} callback
+ * @return {jQuery.jqXHR}
+*/
+jQuery.jqXHR.prototype.success = function (callback) {};
+
+/**
+ * @override
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.jqXHR}
+ */
+jQuery.jqXHR.prototype.then =
+    function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keydown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keypress = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.keyup = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.last = function() {};
+
+/** @type {number} */
+jQuery.prototype.length;
+
+/**
+ * @deprecated Please avoid the document loading Event invocation of
+ *     .load() and use .on( "load", handler ) instead. (The AJAX
+ *     module invocation signature is OK.)
+ * @param {(function(!jQuery.event=)|Object.<string, *>|string)} arg1
+ * @param {(function(!jQuery.event=)|Object.<string,*>|string)=} arg2
+ * @param {function(string,string,XMLHttpRequest)=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.load = function(arg1, arg2, complete) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+jQuery.makeArray = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {Array.<*>}
+ */
+$.makeArray = function(obj) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+jQuery.map = function(arg1, callback) {};
+
+/**
+ * @param {function(number,Element)} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.map = function(callback) {};
+
+/**
+ * @param {(Array.<*>|Object.<string, *>)} arg1
+ * @param {(function(*,number)|function(*,(string|number)))} callback
+ * @return {Array.<*>}
+ */
+$.map = function(arg1, callback) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+jQuery.merge = function(first, second) {};
+
+/**
+ * @param {Array.<*>} first
+ * @param {Array.<*>} second
+ * @return {Array.<*>}
+ */
+$.merge = function(first, second) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousedown = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseenter = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseleave = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mousemove = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseout = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseover = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.mouseup = function(arg1, handler) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.next = function(selector) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.nextUntil = function(arg1, filter) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+jQuery.noConflict = function(removeAll) {};
+
+/**
+ * @param {boolean=} removeAll
+ * @return {Object}
+ */
+$.noConflict = function(removeAll) {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+jQuery.noop = function() {};
+
+/**
+ * @return {function()}
+ * @nosideeffects
+ */
+$.noop = function() {};
+
+/**
+ * @param {(jQuerySelector|Array.<Element>|function(number)|jQuery)} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.not = function(arg1) {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.now = function() {};
+
+/**
+ * @return {number}
+ * @nosideeffects
+ */
+$.now = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)=} arg1
+ * @param {(string|function(!jQuery.event=))=} selector
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.off = function(arg1, selector, handler) {};
+
+/**
+ * @param {({left:number,top:number}|
+ *     function(number,{top:number,left:number}))=} arg1
+ * @return {({left:number,top:number}|!jQuery)}
+ */
+jQuery.prototype.offset = function(arg1) {};
+
+/**
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.offsetParent = function() {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} selector
+ * @param {*=} data
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.on = function(arg1, selector, data, handler) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {*=} arg2
+ * @param {*=} arg3
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.one = function(arg1, arg2, arg3, handler) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerHeight = function(includeMargin) {};
+
+/**
+ * @param {boolean=} includeMargin
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.outerWidth = function(includeMargin) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+jQuery.param = function(obj, traditional) {};
+
+/**
+ * @param {(Object.<string, *>|Array.<Object.<string, *>>)} obj
+ * @param {boolean=} traditional
+ * @return {string}
+ */
+$.param = function(obj, traditional) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parent = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parents = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.parentsUntil = function(arg1, filter) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+jQuery.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} data
+ * @param {(Element|boolean)=} context
+ * @param {boolean=} keepScripts
+ * @return {Array.<Element>}
+ */
+$.parseHTML = function(data, context, keepScripts) {};
+
+/**
+ * @param {string} json
+ * @return {string|number|Object.<string, *>|Array.<?>|boolean}
+ */
+jQuery.parseJSON = function(json) {};
+
+/**
+ * @param {string} json
+ * @return {Object.<string, *>}
+ */
+$.parseJSON = function(json) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+jQuery.parseXML = function(data) {};
+
+/**
+ * @param {string} data
+ * @return {Document}
+ */
+$.parseXML = function(data) {};
+
+/**
+ * @return {{left:number,top:number}}
+ * @nosideeffects
+ */
+jQuery.prototype.position = function() {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ *     function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+jQuery.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {string} url
+ * @param {(Object.<string,*>|string|
+ *     function(string,string,jQuery.jqXHR))=} data
+ * @param {(function(string,string,jQuery.jqXHR)|string)=} success
+ * @param {string=} dataType
+ * @return {jQuery.jqXHR}
+ */
+$.post = function(url, data, success, dataType) {};
+
+/**
+ * @param {(string|Element|jQuery|function(number,string))} arg1
+ * @param {(string|Element|jQuery)=} content
+ * @return {!jQuery}
+ */
+jQuery.prototype.prepend = function(arg1, content) {};
+
+/**
+ * @param {(jQuerySelector|Element|jQuery)} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.prependTo = function(target) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prev = function(selector) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevAll = function(selector) {};
+
+/**
+ * @param {(jQuerySelector|Element)=} arg1
+ * @param {jQuerySelector=} filter
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.prevUntil = function(arg1, filter) {};
+
+/**
+ * @param {(string|Object)=} type
+ * @param {Object=} target
+ * @return {jQuery.Promise}
+ */
+jQuery.prototype.promise = function(type, target) {};
+
+/**
+ * @interface
+ * @private
+ * @see http://api.jquery.com/Types/#Promise
+ */
+jQuery.Promise = function () {};
+
+/**
+ * @param {jQueryCallback} alwaysCallbacks
+ * @param {jQueryCallback=} alwaysCallbacks2
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.always =
+    function(alwaysCallbacks, alwaysCallbacks2) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.done = function(doneCallbacks) {};
+
+/**
+ * @param {jQueryCallback} failCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.fail = function(failCallbacks) {};
+
+/**
+ * @param {function()=} doneFilter
+ * @param {function()=} failFilter
+ * @param {function()=} progressFilter
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.pipe =
+    function(doneFilter, failFilter, progressFilter) {};
+
+/**
+ * @param {jQueryCallback} doneCallbacks
+ * @param {jQueryCallback=} failCallbacks
+ * @param {jQueryCallback=} progressCallbacks
+ * @return {jQuery.Promise}
+ */
+jQuery.Promise.prototype.then =
+    function(doneCallbacks, failCallbacks, progressCallbacks) {};
+
+/**
+ * @param {(string|Object.<string,*>)} arg1
+ * @param {(string|number|boolean|function(number,String))=} arg2
+ * @return {(string|boolean|!jQuery)}
+ */
+jQuery.prototype.prop = function(arg1, arg2) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+jQuery.proxy = function(var_args) {};
+
+/**
+ * @param {...*} var_args
+ * @return {function()}
+ */
+$.proxy = function(var_args) {};
+
+/**
+ * @param {Array.<Element>} elements
+ * @param {string=} name
+ * @param {Array.<*>=} args
+ * @return {!jQuery}
+ */
+jQuery.prototype.pushStack = function(elements, name, args) {};
+
+/**
+ * @param {(string|Array.<function()>|function(function()))=} queueName
+ * @param {(Array.<function()>|function(function()))=} arg2
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.prototype.queue = function(queueName, arg2) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+jQuery.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} queueName
+ * @param {(Array.<function()>|function())=} arg3
+ * @return {(Array.<Element>|!jQuery)}
+ */
+$.queue = function(elem, queueName, arg3) {};
+
+/**
+ * @param {function()} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.ready = function(handler) {};
+
+/**
+ * @param {string=} selector
+ * @return {!jQuery}
+ */
+jQuery.prototype.remove = function(selector) {};
+
+/**
+ * @param {string} attributeName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeAttr = function(attributeName) {};
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeClass = function(arg1) {};
+
+/**
+ * @param {(string|Array.<string>)=} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeData = function(arg1) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+jQuery.removeData = function(elem, name) {};
+
+/**
+ * @param {Element} elem
+ * @param {string=} name
+ * @return {!jQuery}
+ */
+$.removeData = function(elem, name) {};
+
+/**
+ * @param {string} propertyName
+ * @return {!jQuery}
+ */
+jQuery.prototype.removeProp = function(propertyName) {};
+
+/**
+ * @param {jQuerySelector} target
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceAll = function(target) {};
+
+/**
+ * @param {(string|Element|jQuery|function())} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.replaceWith = function(arg1) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.resize = function(arg1, handler) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.scroll = function(arg1, handler) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollLeft = function(value) {};
+
+/**
+ * @param {number=} value
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.scrollTop = function(value) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.select = function(arg1, handler) {};
+
+/**
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.prototype.serialize = function() {};
+
+/**
+ * @return {Array.<Object.<string, *>>}
+ * @nosideeffects
+ */
+jQuery.prototype.serializeArray = function() {};
+
+/**
+ * @param {(string|number|function())=} duration
+ * @param {(function()|string)=} arg2
+ * @param {function()=} callback
+ * @return {!jQuery}
+ */
+jQuery.prototype.show = function(duration, arg2, callback) {};
+
+/**
+ * @param {jQuerySelector=} selector
+ * @return {!jQuery}
+ * @nosideeffects
+ */
+jQuery.prototype.siblings = function(selector) {};
+
+/**
+ * @deprecated Please use the .length property instead.
+ * @return {number}
+ * @nosideeffects
+ */
+jQuery.prototype.size = function() {};
+
+/**
+ * @param {number} start
+ * @param {number=} end
+ * @return {!jQuery}
+ */
+jQuery.prototype.slice = function(start, end) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideDown =
+    function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideToggle =
+    function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(Object.<string,*>|string|number)=} optionsOrDuration
+ * @param {(function()|string)=} completeOrEasing
+ * @param {function()=} complete
+ * @return {!jQuery}
+ */
+jQuery.prototype.slideUp =
+    function(optionsOrDuration, completeOrEasing, complete) {};
+
+/**
+ * @param {(boolean|string)=} arg1
+ * @param {boolean=} arg2
+ * @param {boolean=} jumpToEnd
+ * @return {!jQuery}
+ */
+jQuery.prototype.stop = function(arg1, arg2, jumpToEnd) {};
+
+/**
+ * @param {(function(!jQuery.event=)|Object.<string, *>)=} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.submit = function(arg1, handler) {};
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+jQuery.support;
+
+/** @type {Object.<string, *>}
+ * @deprecated Please try to use feature detection instead.
+ */
+$.support;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+jQuery.support.boxModel;
+
+/**
+ * @deprecated Please try to use feature detection instead.
+ * @type {boolean}
+ */
+$.support.boxModel;
+
+/** @type {boolean} */
+jQuery.support.changeBubbles;
+
+/** @type {boolean} */
+$.support.changeBubbles;
+
+/** @type {boolean} */
+jQuery.support.cors;
+
+/** @type {boolean} */
+$.support.cors;
+
+/** @type {boolean} */
+jQuery.support.cssFloat;
+
+/** @type {boolean} */
+$.support.cssFloat;
+
+/** @type {boolean} */
+jQuery.support.hrefNormalized;
+
+/** @type {boolean} */
+$.support.hrefNormalized;
+
+/** @type {boolean} */
+jQuery.support.htmlSerialize;
+
+/** @type {boolean} */
+$.support.htmlSerialize;
+
+/** @type {boolean} */
+jQuery.support.leadingWhitespace;
+
+/** @type {boolean} */
+$.support.leadingWhitespace;
+
+/** @type {boolean} */
+jQuery.support.noCloneEvent;
+
+/** @type {boolean} */
+$.support.noCloneEvent;
+
+/** @type {boolean} */
+jQuery.support.opacity;
+
+/** @type {boolean} */
+$.support.opacity;
+
+/** @type {boolean} */
+jQuery.support.style;
+
+/** @type {boolean} */
+$.support.style;
+
+/** @type {boolean} */
+jQuery.support.submitBubbles;
+
+/** @type {boolean} */
+$.support.submitBubbles;
+
+/** @type {boolean} */
+jQuery.support.tbody;
+
+/** @type {boolean} */
+$.support.tbody;
+
+/**
+ * @param {(string|function(number,string))=} arg1
+ * @return {(string|!jQuery)}
+ */
+jQuery.prototype.text = function(arg1) {};
+
+/**
+ * @return {Array.<Element>}
+ * @nosideeffects
+ */
+jQuery.prototype.toArray = function() {};
+
+/**
+ * Refers to the method from the Effects category. There used to be a toggle
+ * method on the Events category which was removed starting version 1.9.
+ * @param {(number|string|Object.<string,*>|boolean)=} arg1
+ * @param {(function()|string)=} arg2
+ * @param {function()=} arg3
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggle = function(arg1, arg2, arg3) {};
+
+/**
+ * @param {(string|boolean|function(number,string,boolean))=} arg1
+ * @param {boolean=} flag
+ * @return {!jQuery}
+ */
+jQuery.prototype.toggleClass = function(arg1, flag) {};
+
+/**
+ * @param {(string|jQuery.event)} arg1
+ * @param {...*} var_args
+ * @return {!jQuery}
+ */
+jQuery.prototype.trigger = function(arg1, var_args) {};
+
+/**
+ * @param {string|jQuery.event} eventType
+ * @param {Array.<*>=} extraParameters
+ * @return {*}
+ */
+jQuery.prototype.triggerHandler = function(eventType, extraParameters) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.trim = function(str) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+ * @nosideeffects
+ */
+$.trim = function(str) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+jQuery.type = function(obj) {};
+
+/**
+ * @param {*} obj
+ * @return {string}
+ * @nosideeffects
+ */
+$.type = function(obj) {};
+
+/**
+ * @param {(string|function(!jQuery.event=)|jQuery.event)=} arg1
+ * @param {(function(!jQuery.event=)|boolean)=} arg2
+ * @return {!jQuery}
+ */
+jQuery.prototype.unbind = function(arg1, arg2) {};
+
+/**
+ * @param {string=} arg1
+ * @param {(string|Object.<string,*>)=} arg2
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.undelegate = function(arg1, arg2, handler) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+jQuery.unique = function(arr) {};
+
+/**
+ * @param {Array.<Element>} arr
+ * @return {Array.<Element>}
+ */
+$.unique = function(arr) {};
+
+/**
+ * @deprecated Please use .on( "unload", handler ) instead.
+ * @param {(function(!jQuery.event=)|Object.<string, *>)} arg1
+ * @param {function(!jQuery.event=)=} handler
+ * @return {!jQuery}
+ */
+jQuery.prototype.unload = function(arg1, handler) {};
+
+/** @return {!jQuery} */
+jQuery.prototype.unwrap = function() {};
+
+/**
+ * @param {(string|Array.<string>|function(number,*))=} arg1
+ * @return {(string|number|Array.<string>|!jQuery)}
+ */
+jQuery.prototype.val = function(arg1) {};
+
+/**
+ * Note: The official documentation (https://api.jquery.com/jQuery.when/) says
+ * jQuery.when accepts deferreds, but it actually accepts any type, e.g.:
+ *
+ * jQuery.when(jQuery.ready, jQuery.ajax(''), jQuery('#my-element'), 1)
+ *
+ * If an argument is not an "observable" (a promise-like object) it is wrapped
+ * into a promise.
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+jQuery.when = function(deferred, deferreds) {};
+
+/**
+ * Note: See jQuery.when().
+ * @param {*} deferred
+ * @param {...*} deferreds
+ * @return {jQuery.Promise}
+ */
+$.when = function(deferred, deferreds) {};
+
+/**
+ * @param {(string|number|function(number,number))=} arg1
+ * @return {(number|!jQuery)}
+ */
+jQuery.prototype.width = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrap = function(arg1) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery)} wrappingElement
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapAll = function(wrappingElement) {};
+
+/**
+ * @param {(string|jQuerySelector|Element|jQuery|function(number))} arg1
+ * @return {!jQuery}
+ */
+jQuery.prototype.wrapInner = function(arg1) {};
+
diff --git a/externs/webstorage.js b/externs/webstorage.js
new file mode 100644 (file)
index 0000000..eee69d2
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009 The Closure Compiler Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @fileoverview Definitions for W3C's WebStorage specification.
+ * This file depends on html5.js.
+ * @externs
+ */
+
+/**
+ * @interface
+ * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-interface
+ */
+function Storage() {}
+
+/**
+ * @type {number}
+ * @const
+ */
+Storage.prototype.length;
+
+/**
+ * @param {number} index
+ * @return {?string}
+ */
+Storage.prototype.key = function(index) {};
+
+/**
+ * @param {string} key
+ * @return {?string}
+ */
+Storage.prototype.getItem = function(key) {};
+
+/**
+ * @param {string} key
+ * @param {string} data
+ * @return {void}
+ */
+Storage.prototype.setItem = function(key, data) {};
+
+/**
+ * @param {string} key
+ * @return {void}
+ */
+Storage.prototype.removeItem = function(key) {};
+
+/**
+ * @return {void}
+ */
+Storage.prototype.clear = function() {};
+
+/**
+ * @interface
+ * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-sessionstorage-attribute
+ */
+function WindowSessionStorage() {}
+
+/**
+ * @type {Storage}
+ */
+WindowSessionStorage.prototype.sessionStorage;
+
+/**
+ * Window implements WindowSessionStorage
+ *
+ * @type {Storage}
+ */
+Window.prototype.sessionStorage;
+
+/**
+ * @interface
+ * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-localstorage-attribute
+ */
+function WindowLocalStorage() {}
+
+/**
+ * @type {Storage}
+ */
+WindowLocalStorage.prototype.localStorage;
+
+/**
+ * Window implements WindowLocalStorage
+ *
+ * @type {Storage}
+ */
+Window.prototype.localStorage;
+
+/**
+ * This is the storage event interface.
+ * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-event
+ * @extends {Event}
+ * @constructor
+ */
+function StorageEvent() {}
+
+/**
+ * @type {string}
+ */
+StorageEvent.prototype.key;
+
+/**
+ * @type {?string}
+ */
+StorageEvent.prototype.oldValue;
+
+/**
+ * @type {?string}
+ */
+StorageEvent.prototype.newValue;
+
+/**
+ * @type {string}
+ */
+StorageEvent.prototype.url;
+
+/**
+ * @type {?Storage}
+ */
+StorageEvent.prototype.storageArea;
+
+/**
+ * @param {string} typeArg
+ * @param {boolean} canBubbleArg
+ * @param {boolean} cancelableArg
+ * @param {string} keyArg
+ * @param {?string} oldValueArg
+ * @param {?string} newValueArg
+ * @param {string} urlArg
+ * @param {?Storage} storageAreaArg
+ * @return {void}
+ */
+StorageEvent.prototype.initStorageEvent = function(typeArg, canBubbleArg,
+                                                   cancelableArg, keyArg,
+                                                   oldValueArg, newValueArg,
+                                                   urlArg, storageAreaArg) {};
+
index cf6093e..9186709 100644 (file)
   Ding sound by <a href="https://www.freesound.org/people/Aiwha/sounds/196106/">Aiwa</a> (CC-BY-3.0).
   7-man Lomonosov tablebase lookup by <a href="http://tb7.chessok.com/">ChessOK</a>.</p>
 
-<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+
+<!-- For faster development -->
 <script type="text/javascript" src="js/chessboard-0.3.0.min.js"></script>
 <script type="text/javascript" src="js/chess.min.js"></script>
 <script type="text/javascript" src="js/remoteglot.js"></script>
+
+<!-- Minified version of the previous three, compiled together -->
+<!--
+<script type="text/javascript" src="js/remoteglot.min.js"></script>
+-->
 </body>
 </html>
diff --git a/www/js/chess.js b/www/js/chess.js
new file mode 100644 (file)
index 0000000..201b9d8
--- /dev/null
@@ -0,0 +1,1572 @@
+'use strict';
+/*
+ * Copyright (c) 2014, Jeff Hlywa (jhlywa@gmail.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *----------------------------------------------------------------------------*/
+
+/* minified license below  */
+
+/*! Copyright (c) 2014, Jeff Hlywa (jhlywa@gmail.com)
+ *  Released under the BSD license
+ *  https://github.com/jhlywa/chess.js/blob/master/LICENSE
+ */
+
+/** @constructor */
+var Chess = function(fen) {
+
+  /* jshint indent: false */
+
+  var BLACK = 'b';
+  var WHITE = 'w';
+
+  var EMPTY = -1;
+
+  var PAWN = 'p';
+  var KNIGHT = 'n';
+  var BISHOP = 'b';
+  var ROOK = 'r';
+  var QUEEN = 'q';
+  var KING = 'k';
+
+  var SYMBOLS = 'pnbrqkPNBRQK';
+
+  var DEFAULT_POSITION = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
+
+  var POSSIBLE_RESULTS = ['1-0', '0-1', '1/2-1/2', '*'];
+
+  var PAWN_OFFSETS = {
+    b: [16, 32, 17, 15],
+    w: [-16, -32, -17, -15]
+  };
+
+  var PIECE_OFFSETS = {
+    n: [-18, -33, -31, -14,  18, 33, 31,  14],
+    b: [-17, -15,  17,  15],
+    r: [-16,   1,  16,  -1],
+    q: [-17, -16, -15,   1,  17, 16, 15,  -1],
+    k: [-17, -16, -15,   1,  17, 16, 15,  -1]
+  };
+
+  var ATTACKS = [
+    20, 0, 0, 0, 0, 0, 0, 24,  0, 0, 0, 0, 0, 0,20, 0,
+     0,20, 0, 0, 0, 0, 0, 24,  0, 0, 0, 0, 0,20, 0, 0,
+     0, 0,20, 0, 0, 0, 0, 24,  0, 0, 0, 0,20, 0, 0, 0,
+     0, 0, 0,20, 0, 0, 0, 24,  0, 0, 0,20, 0, 0, 0, 0,
+     0, 0, 0, 0,20, 0, 0, 24,  0, 0,20, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0,20, 2, 24,  2,20, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
+    24,24,24,24,24,24,56,  0, 56,24,24,24,24,24,24, 0,
+     0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0,20, 2, 24,  2,20, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0,20, 0, 0, 24,  0, 0,20, 0, 0, 0, 0, 0,
+     0, 0, 0,20, 0, 0, 0, 24,  0, 0, 0,20, 0, 0, 0, 0,
+     0, 0,20, 0, 0, 0, 0, 24,  0, 0, 0, 0,20, 0, 0, 0,
+     0,20, 0, 0, 0, 0, 0, 24,  0, 0, 0, 0, 0,20, 0, 0,
+    20, 0, 0, 0, 0, 0, 0, 24,  0, 0, 0, 0, 0, 0,20
+  ];
+
+  var RAYS = [
+     17,  0,  0,  0,  0,  0,  0, 16,  0,  0,  0,  0,  0,  0, 15, 0,
+      0, 17,  0,  0,  0,  0,  0, 16,  0,  0,  0,  0,  0, 15,  0, 0,
+      0,  0, 17,  0,  0,  0,  0, 16,  0,  0,  0,  0, 15,  0,  0, 0,
+      0,  0,  0, 17,  0,  0,  0, 16,  0,  0,  0, 15,  0,  0,  0, 0,
+      0,  0,  0,  0, 17,  0,  0, 16,  0,  0, 15,  0,  0,  0,  0, 0,
+      0,  0,  0,  0,  0, 17,  0, 16,  0, 15,  0,  0,  0,  0,  0, 0,
+      0,  0,  0,  0,  0,  0, 17, 16, 15,  0,  0,  0,  0,  0,  0, 0,
+      1,  1,  1,  1,  1,  1,  1,  0, -1, -1,  -1,-1, -1, -1, -1, 0,
+      0,  0,  0,  0,  0,  0,-15,-16,-17,  0,  0,  0,  0,  0,  0, 0,
+      0,  0,  0,  0,  0,-15,  0,-16,  0,-17,  0,  0,  0,  0,  0, 0,
+      0,  0,  0,  0,-15,  0,  0,-16,  0,  0,-17,  0,  0,  0,  0, 0,
+      0,  0,  0,-15,  0,  0,  0,-16,  0,  0,  0,-17,  0,  0,  0, 0,
+      0,  0,-15,  0,  0,  0,  0,-16,  0,  0,  0,  0,-17,  0,  0, 0,
+      0,-15,  0,  0,  0,  0,  0,-16,  0,  0,  0,  0,  0,-17,  0, 0,
+    -15,  0,  0,  0,  0,  0,  0,-16,  0,  0,  0,  0,  0,  0,-17
+  ];
+
+  var SHIFTS = { p: 0, n: 1, b: 2, r: 3, q: 4, k: 5 };
+
+  var FLAGS = {
+    NORMAL: 'n',
+    CAPTURE: 'c',
+    BIG_PAWN: 'b',
+    EP_CAPTURE: 'e',
+    PROMOTION: 'p',
+    KSIDE_CASTLE: 'k',
+    QSIDE_CASTLE: 'q'
+  };
+
+  var BITS = {
+    NORMAL: 1,
+    CAPTURE: 2,
+    BIG_PAWN: 4,
+    EP_CAPTURE: 8,
+    PROMOTION: 16,
+    KSIDE_CASTLE: 32,
+    QSIDE_CASTLE: 64
+  };
+
+  var RANK_1 = 7;
+  var RANK_2 = 6;
+  var RANK_3 = 5;
+  var RANK_4 = 4;
+  var RANK_5 = 3;
+  var RANK_6 = 2;
+  var RANK_7 = 1;
+  var RANK_8 = 0;
+
+  var SQUARES = {
+    a8:   0, b8:   1, c8:   2, d8:   3, e8:   4, f8:   5, g8:   6, h8:   7,
+    a7:  16, b7:  17, c7:  18, d7:  19, e7:  20, f7:  21, g7:  22, h7:  23,
+    a6:  32, b6:  33, c6:  34, d6:  35, e6:  36, f6:  37, g6:  38, h6:  39,
+    a5:  48, b5:  49, c5:  50, d5:  51, e5:  52, f5:  53, g5:  54, h5:  55,
+    a4:  64, b4:  65, c4:  66, d4:  67, e4:  68, f4:  69, g4:  70, h4:  71,
+    a3:  80, b3:  81, c3:  82, d3:  83, e3:  84, f3:  85, g3:  86, h3:  87,
+    a2:  96, b2:  97, c2:  98, d2:  99, e2: 100, f2: 101, g2: 102, h2: 103,
+    a1: 112, b1: 113, c1: 114, d1: 115, e1: 116, f1: 117, g1: 118, h1: 119
+  };
+
+  var ROOKS = {
+    w: [{square: SQUARES.a1, flag: BITS.QSIDE_CASTLE},
+        {square: SQUARES.h1, flag: BITS.KSIDE_CASTLE}],
+    b: [{square: SQUARES.a8, flag: BITS.QSIDE_CASTLE},
+        {square: SQUARES.h8, flag: BITS.KSIDE_CASTLE}]
+  };
+
+  var board = new Array(128);
+  var kings = {w: EMPTY, b: EMPTY};
+  var turn = WHITE;
+  var castling = {w: 0, b: 0};
+  var ep_square = EMPTY;
+  var half_moves = 0;
+  var move_number = 1;
+  var history = [];
+  var header = {};
+
+  /* if the user passes in a fen string, load it, else default to
+   * starting position
+   */
+  if (typeof fen === 'undefined') {
+    load(DEFAULT_POSITION);
+  } else {
+    load(fen);
+  }
+
+  function clear() {
+    board = new Array(128);
+    kings = {w: EMPTY, b: EMPTY};
+    turn = WHITE;
+    castling = {w: 0, b: 0};
+    ep_square = EMPTY;
+    half_moves = 0;
+    move_number = 1;
+    history = [];
+    header = {};
+    update_setup(generate_fen());
+  }
+
+  function reset() {
+    load(DEFAULT_POSITION);
+  }
+
+  function load(fen) {
+    var tokens = fen.split(/\s+/);
+    var position = tokens[0];
+    var square = 0;
+    var valid = SYMBOLS + '12345678/';
+
+    if (!validate_fen(fen).valid) {
+      return false;
+    }
+
+    clear();
+
+    for (var i = 0; i < position.length; i++) {
+      var piece = position.charAt(i);
+
+      if (piece === '/') {
+        square += 8;
+      } else if (is_digit(piece)) {
+        square += parseInt(piece, 10);
+      } else {
+        var color = (piece < 'a') ? WHITE : BLACK;
+        put({type: piece.toLowerCase(), color: color}, algebraic(square));
+        square++;
+      }
+    }
+
+    turn = tokens[1];
+
+    if (tokens[2].indexOf('K') > -1) {
+      castling.w |= BITS.KSIDE_CASTLE;
+    }
+    if (tokens[2].indexOf('Q') > -1) {
+      castling.w |= BITS.QSIDE_CASTLE;
+    }
+    if (tokens[2].indexOf('k') > -1) {
+      castling.b |= BITS.KSIDE_CASTLE;
+    }
+    if (tokens[2].indexOf('q') > -1) {
+      castling.b |= BITS.QSIDE_CASTLE;
+    }
+
+    ep_square = (tokens[3] === '-') ? EMPTY : SQUARES[tokens[3]];
+    half_moves = parseInt(tokens[4], 10);
+    move_number = parseInt(tokens[5], 10);
+
+    update_setup(generate_fen());
+
+    return true;
+  }
+
+  function validate_fen(fen) {
+    var errors = {
+       0: 'No errors.',
+       1: 'FEN string must contain six space-delimited fields.',
+       2: '6th field (move number) must be a positive integer.',
+       3: '5th field (half move counter) must be a non-negative integer.',
+       4: '4th field (en-passant square) is invalid.',
+       5: '3rd field (castling availability) is invalid.',
+       6: '2nd field (side to move) is invalid.',
+       7: '1st field (piece positions) does not contain 8 \'/\'-delimited rows.',
+       8: '1st field (piece positions) is invalid [consecutive numbers].',
+       9: '1st field (piece positions) is invalid [invalid piece].',
+      10: '1st field (piece positions) is invalid [row too large].',
+    };
+
+    /* 1st criterion: 6 space-seperated fields? */
+    var tokens = fen.split(/\s+/);
+    if (tokens.length !== 6) {
+      return {valid: false, error_number: 1, error: errors[1]};
+    }
+
+    /* 2nd criterion: move number field is a integer value > 0? */
+    if (isNaN(tokens[5]) || (parseInt(tokens[5], 10) <= 0)) {
+      return {valid: false, error_number: 2, error: errors[2]};
+    }
+
+    /* 3rd criterion: half move counter is an integer >= 0? */
+    if (isNaN(tokens[4]) || (parseInt(tokens[4], 10) < 0)) {
+      return {valid: false, error_number: 3, error: errors[3]};
+    }
+
+    /* 4th criterion: 4th field is a valid e.p.-string? */
+    if (!/^(-|[abcdefgh][36])$/.test(tokens[3])) {
+      return {valid: false, error_number: 4, error: errors[4]};
+    }
+
+    /* 5th criterion: 3th field is a valid castle-string? */
+    if( !/^(KQ?k?q?|Qk?q?|kq?|q|-)$/.test(tokens[2])) {
+      return {valid: false, error_number: 5, error: errors[5]};
+    }
+
+    /* 6th criterion: 2nd field is "w" (white) or "b" (black)? */
+    if (!/^(w|b)$/.test(tokens[1])) {
+      return {valid: false, error_number: 6, error: errors[6]};
+    }
+
+    /* 7th criterion: 1st field contains 8 rows? */
+    var rows = tokens[0].split('/');
+    if (rows.length !== 8) {
+      return {valid: false, error_number: 7, error: errors[7]};
+    }
+
+    /* 8th criterion: every row is valid? */
+    for (var i = 0; i < rows.length; i++) {
+      /* check for right sum of fields AND not two numbers in succession */
+      var sum_fields = 0;
+      var previous_was_number = false;
+
+      for (var k = 0; k < rows[i].length; k++) {
+        if (!isNaN(rows[i][k])) {
+          if (previous_was_number) {
+            return {valid: false, error_number: 8, error: errors[8]};
+          }
+          sum_fields += parseInt(rows[i][k], 10);
+          previous_was_number = true;
+        } else {
+          if (!/^[prnbqkPRNBQK]$/.test(rows[i][k])) {
+            return {valid: false, error_number: 9, error: errors[9]};
+          }
+          sum_fields += 1;
+          previous_was_number = false;
+        }
+      }
+      if (sum_fields !== 8) {
+        return {valid: false, error_number: 10, error: errors[10]};
+      }
+    }
+
+    /* everything's okay! */
+    return {valid: true, error_number: 0, error: errors[0]};
+  }
+
+  function generate_fen() {
+    var empty = 0;
+    var fen = '';
+
+    for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
+      if (board[i] == null) {
+        empty++;
+      } else {
+        if (empty > 0) {
+          fen += empty;
+          empty = 0;
+        }
+        var color = board[i].color;
+        var piece = board[i].type;
+
+        fen += (color === WHITE) ?
+                 piece.toUpperCase() : piece.toLowerCase();
+      }
+
+      if ((i + 1) & 0x88) {
+        if (empty > 0) {
+          fen += empty;
+        }
+
+        if (i !== SQUARES.h1) {
+          fen += '/';
+        }
+
+        empty = 0;
+        i += 8;
+      }
+    }
+
+    var cflags = '';
+    if (castling[WHITE] & BITS.KSIDE_CASTLE) { cflags += 'K'; }
+    if (castling[WHITE] & BITS.QSIDE_CASTLE) { cflags += 'Q'; }
+    if (castling[BLACK] & BITS.KSIDE_CASTLE) { cflags += 'k'; }
+    if (castling[BLACK] & BITS.QSIDE_CASTLE) { cflags += 'q'; }
+
+    /* do we have an empty castling flag? */
+    cflags = cflags || '-';
+    var epflags = (ep_square === EMPTY) ? '-' : algebraic(ep_square);
+
+    return [fen, turn, cflags, epflags, half_moves, move_number].join(' ');
+  }
+
+  function set_header(args) {
+    for (var i = 0; i < args.length; i += 2) {
+      if (typeof args[i] === 'string' &&
+          typeof args[i + 1] === 'string') {
+        header[args[i]] = args[i + 1];
+      }
+    }
+    return header;
+  }
+
+  /* called when the initial board setup is changed with put() or remove().
+   * modifies the SetUp and FEN properties of the header object.  if the FEN is
+   * equal to the default position, the SetUp and FEN are deleted
+   * the setup is only updated if history.length is zero, ie moves haven't been
+   * made.
+   */
+  function update_setup(fen) {
+    if (history.length > 0) return;
+
+    if (fen !== DEFAULT_POSITION) {
+      header['SetUp'] = '1';
+      header['FEN'] = fen;
+    } else {
+      delete header['SetUp'];
+      delete header['FEN'];
+    }
+  }
+
+  function get(square) {
+    var piece = board[SQUARES[square]];
+    return (piece) ? {type: piece.type, color: piece.color} : null;
+  }
+
+  function put(piece, square) {
+    /* check for valid piece object */
+    if (!('type' in piece && 'color' in piece)) {
+      return false;
+    }
+
+    /* check for piece */
+    if (SYMBOLS.indexOf(piece.type.toLowerCase()) === -1) {
+      return false;
+    }
+
+    /* check for valid square */
+    if (!(square in SQUARES)) {
+      return false;
+    }
+
+    var sq = SQUARES[square];
+
+    /* don't let the user place more than one king */
+    if (piece.type == KING &&
+        !(kings[piece.color] == EMPTY || kings[piece.color] == sq)) {
+      return false;
+    }
+
+    board[sq] = {type: piece.type, color: piece.color};
+    if (piece.type === KING) {
+      kings[piece.color] = sq;
+    }
+
+    update_setup(generate_fen());
+
+    return true;
+  }
+
+  function remove(square) {
+    var piece = get(square);
+    board[SQUARES[square]] = null;
+    if (piece && piece.type === KING) {
+      kings[piece.color] = EMPTY;
+    }
+
+    update_setup(generate_fen());
+
+    return piece;
+  }
+
+  function build_move(board, from, to, flags, promotion) {
+    var move = {
+      color: turn,
+      from: from,
+      to: to,
+      flags: flags,
+      piece: board[from].type
+    };
+
+    if (promotion) {
+      move.flags |= BITS.PROMOTION;
+      move.promotion = promotion;
+    }
+
+    if (board[to]) {
+      move.captured = board[to].type;
+    } else if (flags & BITS.EP_CAPTURE) {
+        move.captured = PAWN;
+    }
+    return move;
+  }
+
+  function generate_moves(options) {
+    function add_move(board, moves, from, to, flags) {
+      /* if pawn promotion */
+      if (board[from].type === PAWN &&
+         (rank(to) === RANK_8 || rank(to) === RANK_1)) {
+          var pieces = [QUEEN, ROOK, BISHOP, KNIGHT];
+          for (var i = 0, len = pieces.length; i < len; i++) {
+            moves.push(build_move(board, from, to, flags, pieces[i]));
+          }
+      } else {
+       moves.push(build_move(board, from, to, flags));
+      }
+    }
+
+    var moves = [];
+    var us = turn;
+    var them = swap_color(us);
+    var second_rank = {b: RANK_7, w: RANK_2};
+
+    var first_sq = SQUARES.a8;
+    var last_sq = SQUARES.h1;
+    var single_square = false;
+
+    /* do we want legal moves? */
+    var legal = (typeof options !== 'undefined' && 'legal' in options) ?
+                options.legal : true;
+
+    /* are we generating moves for a single square? */
+    if (typeof options !== 'undefined' && 'square' in options) {
+      if (options.square in SQUARES) {
+        first_sq = last_sq = SQUARES[options.square];
+        single_square = true;
+      } else {
+        /* invalid square */
+        return [];
+      }
+    }
+
+    for (var i = first_sq; i <= last_sq; i++) {
+      /* did we run off the end of the board */
+      if (i & 0x88) { i += 7; continue; }
+
+      var piece = board[i];
+      if (piece == null || piece.color !== us) {
+        continue;
+      }
+
+      if (piece.type === PAWN) {
+        /* single square, non-capturing */
+        var square = i + PAWN_OFFSETS[us][0];
+        if (board[square] == null) {
+            add_move(board, moves, i, square, BITS.NORMAL);
+
+          /* double square */
+          square = i + PAWN_OFFSETS[us][1];
+          if (second_rank[us] === rank(i) && board[square] == null) {
+            add_move(board, moves, i, square, BITS.BIG_PAWN);
+          }
+        }
+
+        /* pawn captures */
+        for (var j = 2; j < 4; j++) {
+          square = i + PAWN_OFFSETS[us][j];
+          if (square & 0x88) continue;
+
+          if (board[square] != null &&
+              board[square].color === them) {
+              add_move(board, moves, i, square, BITS.CAPTURE);
+          } else if (square === ep_square) {
+              add_move(board, moves, i, ep_square, BITS.EP_CAPTURE);
+          }
+        }
+      } else {
+        for (var j = 0, len = PIECE_OFFSETS[piece.type].length; j < len; j++) {
+          var offset = PIECE_OFFSETS[piece.type][j];
+          var square = i;
+
+          while (true) {
+            square += offset;
+            if (square & 0x88) break;
+
+            if (board[square] == null) {
+              add_move(board, moves, i, square, BITS.NORMAL);
+            } else {
+              if (board[square].color === us) break;
+              add_move(board, moves, i, square, BITS.CAPTURE);
+              break;
+            }
+
+            /* break, if knight or king */
+            if (piece.type === 'n' || piece.type === 'k') break;
+          }
+        }
+      }
+    }
+
+    /* check for castling if: a) we're generating all moves, or b) we're doing
+     * single square move generation on the king's square
+     */
+    if ((!single_square) || last_sq === kings[us]) {
+      /* king-side castling */
+      if (castling[us] & BITS.KSIDE_CASTLE) {
+        var castling_from = kings[us];
+        var castling_to = castling_from + 2;
+
+        if (board[castling_from + 1] == null &&
+            board[castling_to]       == null &&
+            !attacked(them, kings[us]) &&
+            !attacked(them, castling_from + 1) &&
+            !attacked(them, castling_to)) {
+          add_move(board, moves, kings[us] , castling_to,
+                   BITS.KSIDE_CASTLE);
+        }
+      }
+
+      /* queen-side castling */
+      if (castling[us] & BITS.QSIDE_CASTLE) {
+        var castling_from = kings[us];
+        var castling_to = castling_from - 2;
+
+        if (board[castling_from - 1] == null &&
+            board[castling_from - 2] == null &&
+            board[castling_from - 3] == null &&
+            !attacked(them, kings[us]) &&
+            !attacked(them, castling_from - 1) &&
+            !attacked(them, castling_to)) {
+          add_move(board, moves, kings[us], castling_to,
+                   BITS.QSIDE_CASTLE);
+        }
+      }
+    }
+
+    /* return all pseudo-legal moves (this includes moves that allow the king
+     * to be captured)
+     */
+    if (!legal) {
+      return moves;
+    }
+
+    /* filter out illegal moves */
+    var legal_moves = [];
+    for (var i = 0, len = moves.length; i < len; i++) {
+      make_move(moves[i]);
+      if (!king_attacked(us)) {
+        legal_moves.push(moves[i]);
+      }
+      undo_move();
+    }
+
+    return legal_moves;
+  }
+
+  /* convert a move from 0x88 coordinates to Standard Algebraic Notation
+   * (SAN)
+   */
+  function move_to_san(move) {
+    var output = '';
+
+    if (move.flags & BITS.KSIDE_CASTLE) {
+      output = 'O-O';
+    } else if (move.flags & BITS.QSIDE_CASTLE) {
+      output = 'O-O-O';
+    } else {
+      var disambiguator = get_disambiguator(move);
+
+      if (move.piece !== PAWN) {
+        output += move.piece.toUpperCase() + disambiguator;
+      }
+
+      if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {
+        if (move.piece === PAWN) {
+          output += algebraic(move.from)[0];
+        }
+        output += 'x';
+      }
+
+      output += algebraic(move.to);
+
+      if (move.flags & BITS.PROMOTION) {
+        output += '=' + move.promotion.toUpperCase();
+      }
+    }
+
+    make_move(move);
+    if (in_check()) {
+      if (in_checkmate()) {
+        output += '#';
+      } else {
+        output += '+';
+      }
+    }
+    undo_move();
+
+    return output;
+  }
+
+  function attacked(color, square) {
+    for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
+      /* did we run off the end of the board */
+      if (i & 0x88) { i += 7; continue; }
+
+      /* if empty square or wrong color */
+      if (board[i] == null || board[i].color !== color) continue;
+
+      var piece = board[i];
+      var difference = i - square;
+      var index = difference + 119;
+
+      if (ATTACKS[index] & (1 << SHIFTS[piece.type])) {
+        if (piece.type === PAWN) {
+          if (difference > 0) {
+            if (piece.color === WHITE) return true;
+          } else {
+            if (piece.color === BLACK) return true;
+          }
+          continue;
+        }
+
+        /* if the piece is a knight or a king */
+        if (piece.type === 'n' || piece.type === 'k') return true;
+
+        var offset = RAYS[index];
+        var j = i + offset;
+
+        var blocked = false;
+        while (j !== square) {
+          if (board[j] != null) { blocked = true; break; }
+          j += offset;
+        }
+
+        if (!blocked) return true;
+      }
+    }
+
+    return false;
+  }
+
+  function king_attacked(color) {
+    return attacked(swap_color(color), kings[color]);
+  }
+
+  function in_check() {
+    return king_attacked(turn);
+  }
+
+  function in_checkmate() {
+    return in_check() && generate_moves().length === 0;
+  }
+
+  function in_stalemate() {
+    return !in_check() && generate_moves().length === 0;
+  }
+
+  function insufficient_material() {
+    var pieces = {};
+    var bishops = [];
+    var num_pieces = 0;
+    var sq_color = 0;
+
+    for (var i = SQUARES.a8; i<= SQUARES.h1; i++) {
+      sq_color = (sq_color + 1) % 2;
+      if (i & 0x88) { i += 7; continue; }
+
+      var piece = board[i];
+      if (piece) {
+        pieces[piece.type] = (piece.type in pieces) ?
+                              pieces[piece.type] + 1 : 1;
+        if (piece.type === BISHOP) {
+          bishops.push(sq_color);
+        }
+        num_pieces++;
+      }
+    }
+
+    /* k vs. k */
+    if (num_pieces === 2) { return true; }
+
+    /* k vs. kn .... or .... k vs. kb */
+    else if (num_pieces === 3 && (pieces[BISHOP] === 1 ||
+                                 pieces[KNIGHT] === 1)) { return true; }
+
+    /* kb vs. kb where any number of bishops are all on the same color */
+    else if (num_pieces === pieces[BISHOP] + 2) {
+      var sum = 0;
+      var len = bishops.length;
+      for (var i = 0; i < len; i++) {
+        sum += bishops[i];
+      }
+      if (sum === 0 || sum === len) { return true; }
+    }
+
+    return false;
+  }
+
+  function in_threefold_repetition() {
+    /* TODO: while this function is fine for casual use, a better
+     * implementation would use a Zobrist key (instead of FEN). the
+     * Zobrist key would be maintained in the make_move/undo_move functions,
+     * avoiding the costly that we do below.
+     */
+    var moves = [];
+    var positions = {};
+    var repetition = false;
+
+    while (true) {
+      var move = undo_move();
+      if (!move) break;
+      moves.push(move);
+    }
+
+    while (true) {
+      /* remove the last two fields in the FEN string, they're not needed
+       * when checking for draw by rep */
+      var fen = generate_fen().split(' ').slice(0,4).join(' ');
+
+      /* has the position occurred three or move times */
+      positions[fen] = (fen in positions) ? positions[fen] + 1 : 1;
+      if (positions[fen] >= 3) {
+        repetition = true;
+      }
+
+      if (!moves.length) {
+        break;
+      }
+      make_move(moves.pop());
+    }
+
+    return repetition;
+  }
+
+  function push(move) {
+    history.push({
+      move: move,
+      kings: {b: kings.b, w: kings.w},
+      turn: turn,
+      castling: {b: castling.b, w: castling.w},
+      ep_square: ep_square,
+      half_moves: half_moves,
+      move_number: move_number
+    });
+  }
+
+  function make_move(move) {
+    var us = turn;
+    var them = swap_color(us);
+    push(move);
+
+    board[move.to] = board[move.from];
+    board[move.from] = null;
+
+    /* if ep capture, remove the captured pawn */
+    if (move.flags & BITS.EP_CAPTURE) {
+      if (turn === BLACK) {
+        board[move.to - 16] = null;
+      } else {
+        board[move.to + 16] = null;
+      }
+    }
+
+    /* if pawn promotion, replace with new piece */
+    if (move.flags & BITS.PROMOTION) {
+      board[move.to] = {type: move.promotion, color: us};
+    }
+
+    /* if we moved the king */
+    if (board[move.to].type === KING) {
+      kings[board[move.to].color] = move.to;
+
+      /* if we castled, move the rook next to the king */
+      if (move.flags & BITS.KSIDE_CASTLE) {
+        var castling_to = move.to - 1;
+        var castling_from = move.to + 1;
+        board[castling_to] = board[castling_from];
+        board[castling_from] = null;
+      } else if (move.flags & BITS.QSIDE_CASTLE) {
+        var castling_to = move.to + 1;
+        var castling_from = move.to - 2;
+        board[castling_to] = board[castling_from];
+        board[castling_from] = null;
+      }
+
+      /* turn off castling */
+      castling[us] = '';
+    }
+
+    /* turn off castling if we move a rook */
+    if (castling[us]) {
+      for (var i = 0, len = ROOKS[us].length; i < len; i++) {
+        if (move.from === ROOKS[us][i].square &&
+            castling[us] & ROOKS[us][i].flag) {
+          castling[us] ^= ROOKS[us][i].flag;
+          break;
+        }
+      }
+    }
+
+    /* turn off castling if we capture a rook */
+    if (castling[them]) {
+      for (var i = 0, len = ROOKS[them].length; i < len; i++) {
+        if (move.to === ROOKS[them][i].square &&
+            castling[them] & ROOKS[them][i].flag) {
+          castling[them] ^= ROOKS[them][i].flag;
+          break;
+        }
+      }
+    }
+
+    /* if big pawn move, update the en passant square */
+    if (move.flags & BITS.BIG_PAWN) {
+      if (turn === 'b') {
+        ep_square = move.to - 16;
+      } else {
+        ep_square = move.to + 16;
+      }
+    } else {
+      ep_square = EMPTY;
+    }
+
+    /* reset the 50 move counter if a pawn is moved or a piece is captured */
+    if (move.piece === PAWN) {
+      half_moves = 0;
+    } else if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {
+      half_moves = 0;
+    } else {
+      half_moves++;
+    }
+
+    if (turn === BLACK) {
+      move_number++;
+    }
+    turn = swap_color(turn);
+  }
+
+  function undo_move() {
+    var old = history.pop();
+    if (old == null) { return null; }
+
+    var move = old.move;
+    kings = old.kings;
+    turn = old.turn;
+    castling = old.castling;
+    ep_square = old.ep_square;
+    half_moves = old.half_moves;
+    move_number = old.move_number;
+
+    var us = turn;
+    var them = swap_color(turn);
+
+    board[move.from] = board[move.to];
+    board[move.from].type = move.piece;  // to undo any promotions
+    board[move.to] = null;
+
+    if (move.flags & BITS.CAPTURE) {
+      board[move.to] = {type: move.captured, color: them};
+    } else if (move.flags & BITS.EP_CAPTURE) {
+      var index;
+      if (us === BLACK) {
+        index = move.to - 16;
+      } else {
+        index = move.to + 16;
+      }
+      board[index] = {type: PAWN, color: them};
+    }
+
+
+    if (move.flags & (BITS.KSIDE_CASTLE | BITS.QSIDE_CASTLE)) {
+      var castling_to, castling_from;
+      if (move.flags & BITS.KSIDE_CASTLE) {
+        castling_to = move.to + 1;
+        castling_from = move.to - 1;
+      } else if (move.flags & BITS.QSIDE_CASTLE) {
+        castling_to = move.to - 2;
+        castling_from = move.to + 1;
+      }
+
+      board[castling_to] = board[castling_from];
+      board[castling_from] = null;
+    }
+
+    return move;
+  }
+
+  /* this function is used to uniquely identify ambiguous moves */
+  function get_disambiguator(move) {
+    var moves = generate_moves();
+
+    var from = move.from;
+    var to = move.to;
+    var piece = move.piece;
+
+    var ambiguities = 0;
+    var same_rank = 0;
+    var same_file = 0;
+
+    for (var i = 0, len = moves.length; i < len; i++) {
+      var ambig_from = moves[i].from;
+      var ambig_to = moves[i].to;
+      var ambig_piece = moves[i].piece;
+
+      /* if a move of the same piece type ends on the same to square, we'll
+       * need to add a disambiguator to the algebraic notation
+       */
+      if (piece === ambig_piece && from !== ambig_from && to === ambig_to) {
+        ambiguities++;
+
+        if (rank(from) === rank(ambig_from)) {
+          same_rank++;
+        }
+
+        if (file(from) === file(ambig_from)) {
+          same_file++;
+        }
+      }
+    }
+
+    if (ambiguities > 0) {
+      /* if there exists a similar moving piece on the same rank and file as
+       * the move in question, use the square as the disambiguator
+       */
+      if (same_rank > 0 && same_file > 0) {
+        return algebraic(from);
+      }
+      /* if the moving piece rests on the same file, use the rank symbol as the
+       * disambiguator
+       */
+      else if (same_file > 0) {
+        return algebraic(from).charAt(1);
+      }
+      /* else use the file symbol */
+      else {
+        return algebraic(from).charAt(0);
+      }
+    }
+
+    return '';
+  }
+
+  function ascii() {
+    var s = '   +------------------------+\n';
+    for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
+      /* display the rank */
+      if (file(i) === 0) {
+        s += ' ' + '87654321'[rank(i)] + ' |';
+      }
+
+      /* empty piece */
+      if (board[i] == null) {
+        s += ' . ';
+      } else {
+        var piece = board[i].type;
+        var color = board[i].color;
+        var symbol = (color === WHITE) ?
+                     piece.toUpperCase() : piece.toLowerCase();
+        s += ' ' + symbol + ' ';
+      }
+
+      if ((i + 1) & 0x88) {
+        s += '|\n';
+        i += 8;
+      }
+    }
+    s += '   +------------------------+\n';
+    s += '     a  b  c  d  e  f  g  h\n';
+
+    return s;
+  }
+
+  /*****************************************************************************
+   * UTILITY FUNCTIONS
+   ****************************************************************************/
+  function rank(i) {
+    return i >> 4;
+  }
+
+  function file(i) {
+    return i & 15;
+  }
+
+  function algebraic(i){
+    var f = file(i), r = rank(i);
+    return 'abcdefgh'.substring(f,f+1) + '87654321'.substring(r,r+1);
+  }
+
+  function swap_color(c) {
+    return c === WHITE ? BLACK : WHITE;
+  }
+
+  function is_digit(c) {
+    return '0123456789'.indexOf(c) !== -1;
+  }
+
+  /* pretty = external move object */
+  function make_pretty(ugly_move) {
+    var move = clone(ugly_move);
+    move.san = move_to_san(move);
+    move.to = algebraic(move.to);
+    move.from = algebraic(move.from);
+
+    var flags = '';
+
+    for (var flag in BITS) {
+      if (BITS[flag] & move.flags) {
+        flags += FLAGS[flag];
+      }
+    }
+    move.flags = flags;
+
+    return move;
+  }
+
+  function clone(obj) {
+    var dupe = (obj instanceof Array) ? [] : {};
+
+    for (var property in obj) {
+      if (typeof property === 'object') {
+        dupe[property] = clone(obj[property]);
+      } else {
+        dupe[property] = obj[property];
+      }
+    }
+
+    return dupe;
+  }
+
+  function trim(str) {
+    return str.replace(/^\s+|\s+$/g, '');
+  }
+
+  /*****************************************************************************
+   * DEBUGGING UTILITIES
+   ****************************************************************************/
+  function perft(depth) {
+    var moves = generate_moves({legal: false});
+    var nodes = 0;
+    var color = turn;
+
+    for (var i = 0, len = moves.length; i < len; i++) {
+      make_move(moves[i]);
+      if (!king_attacked(color)) {
+        if (depth - 1 > 0) {
+          var child_nodes = perft(depth - 1);
+          nodes += child_nodes;
+        } else {
+          nodes++;
+        }
+      }
+      undo_move();
+    }
+
+    return nodes;
+  }
+
+  return {
+    /***************************************************************************
+     * PUBLIC CONSTANTS (is there a better way to do this?)
+     **************************************************************************/
+    WHITE: WHITE,
+    BLACK: BLACK,
+    PAWN: PAWN,
+    KNIGHT: KNIGHT,
+    BISHOP: BISHOP,
+    ROOK: ROOK,
+    QUEEN: QUEEN,
+    KING: KING,
+    SQUARES: (function() {
+                /* from the ECMA-262 spec (section 12.6.4):
+                 * "The mechanics of enumerating the properties ... is
+                 * implementation dependent"
+                 * so: for (var sq in SQUARES) { keys.push(sq); } might not be
+                 * ordered correctly
+                 */
+                var keys = [];
+                for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
+                  if (i & 0x88) { i += 7; continue; }
+                  keys.push(algebraic(i));
+                }
+                return keys;
+              })(),
+    FLAGS: FLAGS,
+
+    /***************************************************************************
+     * PUBLIC API
+     **************************************************************************/
+    load: function(fen) {
+      return load(fen);
+    },
+
+    reset: function() {
+      return reset();
+    },
+
+    moves: function(options) {
+      /* The internal representation of a chess move is in 0x88 format, and
+       * not meant to be human-readable.  The code below converts the 0x88
+       * square coordinates to algebraic coordinates.  It also prunes an
+       * unnecessary move keys resulting from a verbose call.
+       */
+
+      var ugly_moves = generate_moves(options);
+      var moves = [];
+
+      for (var i = 0, len = ugly_moves.length; i < len; i++) {
+
+        /* does the user want a full move object (most likely not), or just
+         * SAN
+         */
+        if (typeof options !== 'undefined' && 'verbose' in options &&
+            options.verbose) {
+          moves.push(make_pretty(ugly_moves[i]));
+        } else {
+          moves.push(move_to_san(ugly_moves[i]));
+        }
+      }
+
+      return moves;
+    },
+
+    in_check: function() {
+      return in_check();
+    },
+
+    in_checkmate: function() {
+      return in_checkmate();
+    },
+
+    in_stalemate: function() {
+      return in_stalemate();
+    },
+
+    in_draw: function() {
+      return half_moves >= 100 ||
+             in_stalemate() ||
+             insufficient_material() ||
+             in_threefold_repetition();
+    },
+
+    insufficient_material: function() {
+      return insufficient_material();
+    },
+
+    in_threefold_repetition: function() {
+      return in_threefold_repetition();
+    },
+
+    game_over: function() {
+      return half_moves >= 100 ||
+             in_checkmate() ||
+             in_stalemate() ||
+             insufficient_material() ||
+             in_threefold_repetition();
+    },
+
+    validate_fen: function(fen) {
+      return validate_fen(fen);
+    },
+
+    fen: function() {
+      return generate_fen();
+    },
+
+    pgn: function(options) {
+      /* using the specification from http://www.chessclub.com/help/PGN-spec
+       * example for html usage: .pgn({ max_width: 72, newline_char: "<br />" })
+       */
+      var newline = (typeof options === 'object' &&
+                     typeof options.newline_char === 'string') ?
+                     options.newline_char : '\n';
+      var max_width = (typeof options === 'object' &&
+                       typeof options.max_width === 'number') ?
+                       options.max_width : 0;
+      var result = [];
+      var header_exists = false;
+
+      /* add the PGN header headerrmation */
+      for (var i in header) {
+        /* TODO: order of enumerated properties in header object is not
+         * guaranteed, see ECMA-262 spec (section 12.6.4)
+         */
+        result.push('[' + i + ' \"' + header[i] + '\"]' + newline);
+        header_exists = true;
+      }
+
+      if (header_exists && history.length) {
+        result.push(newline);
+      }
+
+      /* pop all of history onto reversed_history */
+      var reversed_history = [];
+      while (history.length > 0) {
+        reversed_history.push(undo_move());
+      }
+
+      var moves = [];
+      var move_string = '';
+      var pgn_move_number = 1;
+
+      /* build the list of moves.  a move_string looks like: "3. e3 e6" */
+      while (reversed_history.length > 0) {
+        var move = reversed_history.pop();
+
+        /* if the position started with black to move, start PGN with 1. ... */
+        if (pgn_move_number === 1 && move.color === 'b') {
+          move_string = '1. ...';
+          pgn_move_number++;
+        } else if (move.color === 'w') {
+          /* store the previous generated move_string if we have one */
+          if (move_string.length) {
+            moves.push(move_string);
+          }
+          move_string = pgn_move_number + '.';
+          pgn_move_number++;
+        }
+
+        move_string = move_string + ' ' + move_to_san(move);
+        make_move(move);
+      }
+
+      /* are there any other leftover moves? */
+      if (move_string.length) {
+        moves.push(move_string);
+      }
+
+      /* is there a result? */
+      if (typeof header.Result !== 'undefined') {
+        moves.push(header.Result);
+      }
+
+      /* history should be back to what is was before we started generating PGN,
+       * so join together moves
+       */
+      if (max_width === 0) {
+        return result.join('') + moves.join(' ');
+      }
+
+      /* wrap the PGN output at max_width */
+      var current_width = 0;
+      for (var i = 0; i < moves.length; i++) {
+        /* if the current move will push past max_width */
+        if (current_width + moves[i].length > max_width && i !== 0) {
+
+          /* don't end the line with whitespace */
+          if (result[result.length - 1] === ' ') {
+            result.pop();
+          }
+
+          result.push(newline);
+          current_width = 0;
+        } else if (i !== 0) {
+          result.push(' ');
+          current_width++;
+        }
+        result.push(moves[i]);
+        current_width += moves[i].length;
+      }
+
+      return result.join('');
+    },
+
+    load_pgn: function(pgn, options) {
+      function mask(str) {
+        return str.replace(/\\/g, '\\');
+      }
+
+      /* convert a move from Standard Algebraic Notation (SAN) to 0x88
+       * coordinates
+      */
+      function move_from_san(move) {
+        var moves = generate_moves();
+        for (var i = 0, len = moves.length; i < len; i++) {
+          /* strip off any trailing move decorations: e.g Nf3+?! */
+          if (move.replace(/[+#?!=]+$/,'') ==
+              move_to_san(moves[i]).replace(/[+#?!=]+$/,'')) {
+            return moves[i];
+          }
+        }
+        return null;
+      }
+
+      function get_move_obj(move) {
+        return move_from_san(trim(move));
+      }
+
+      function has_keys(object) {
+        var has_keys = false;
+        for (var key in object) {
+          has_keys = true;
+        }
+        return has_keys;
+      }
+
+      function parse_pgn_header(header, options) {
+        var newline_char = (typeof options === 'object' &&
+                            typeof options.newline_char === 'string') ?
+                            options.newline_char : '\r?\n';
+        var header_obj = {};
+        var headers = header.split(new RegExp(mask(newline_char)));
+        var key = '';
+        var value = '';
+
+        for (var i = 0; i < headers.length; i++) {
+          key = headers[i].replace(/^\[([A-Z][A-Za-z]*)\s.*\]$/, '$1');
+          value = headers[i].replace(/^\[[A-Za-z]+\s"(.*)"\]$/, '$1');
+          if (trim(key).length > 0) {
+            header_obj[key] = value;
+          }
+        }
+
+        return header_obj;
+      }
+
+      var newline_char = (typeof options === 'object' &&
+                          typeof options.newline_char === 'string') ?
+                          options.newline_char : '\r?\n';
+        var regex = new RegExp('^(\\[(.|' + mask(newline_char) + ')*\\])' +
+                               '(' + mask(newline_char) + ')*' +
+                               '1.(' + mask(newline_char) + '|.)*$', 'g');
+
+      /* get header part of the PGN file */
+      var header_string = pgn.replace(regex, '$1');
+
+      /* no info part given, begins with moves */
+      if (header_string[0] !== '[') {
+        header_string = '';
+      }
+
+     reset();
+
+      /* parse PGN header */
+      var headers = parse_pgn_header(header_string, options);
+      for (var key in headers) {
+        set_header([key, headers[key]]);
+      }
+
+      /* delete header to get the moves */
+      var ms = pgn.replace(header_string, '').replace(new RegExp(mask(newline_char), 'g'), ' ');
+
+      /* delete comments */
+      ms = ms.replace(/(\{[^}]+\})+?/g, '');
+
+      /* delete move numbers */
+      ms = ms.replace(/\d+\./g, '');
+
+
+      /* trim and get array of moves */
+      var moves = trim(ms).split(new RegExp(/\s+/));
+
+      /* delete empty entries */
+      moves = moves.join(',').replace(/,,+/g, ',').split(',');
+      var move = '';
+
+      for (var half_move = 0; half_move < moves.length - 1; half_move++) {
+        move = get_move_obj(moves[half_move]);
+
+        /* move not possible! (don't clear the board to examine to show the
+         * latest valid position)
+         */
+        if (move == null) {
+          return false;
+        } else {
+          make_move(move);
+        }
+      }
+
+      /* examine last move */
+      move = moves[moves.length - 1];
+      if (POSSIBLE_RESULTS.indexOf(move) > -1) {
+        if (has_keys(header) && typeof header.Result === 'undefined') {
+          set_header(['Result', move]);
+        }
+      }
+      else {
+        move = get_move_obj(move);
+        if (move == null) {
+          return false;
+        } else {
+          make_move(move);
+        }
+      }
+      return true;
+    },
+
+    header: function() {
+      return set_header(arguments);
+    },
+
+    ascii: function() {
+      return ascii();
+    },
+
+    turn: function() {
+      return turn;
+    },
+
+    move: function(move) {
+      /* The move function can be called with in the following parameters:
+       *
+       * .move('Nxb7')      <- where 'move' is a case-sensitive SAN string
+       *
+       * .move({ from: 'h7', <- where the 'move' is a move object (additional
+       *         to :'h8',      fields are ignored)
+       *         promotion: 'q',
+       *      })
+       */
+      var move_obj = null;
+      var moves = generate_moves();
+
+      if (typeof move === 'string') {
+        /* convert the move string to a move object */
+        for (var i = 0, len = moves.length; i < len; i++) {
+          if (move === move_to_san(moves[i])) {
+            move_obj = moves[i];
+            break;
+          }
+        }
+      } else if (typeof move === 'object') {
+        /* convert the pretty move object to an ugly move object */
+        for (var i = 0, len = moves.length; i < len; i++) {
+          if (move.from === algebraic(moves[i].from) &&
+              move.to === algebraic(moves[i].to) &&
+              (!('promotion' in moves[i]) ||
+              move.promotion === moves[i].promotion)) {
+            move_obj = moves[i];
+            break;
+          }
+        }
+      }
+
+      /* failed to find move */
+      if (!move_obj) {
+        return null;
+      }
+
+      /* need to make a copy of move because we can't generate SAN after the
+       * move is made
+       */
+      var pretty_move = make_pretty(move_obj);
+
+      make_move(move_obj);
+
+      return pretty_move;
+    },
+
+    undo: function() {
+      var move = undo_move();
+      return (move) ? make_pretty(move) : null;
+    },
+
+    clear: function() {
+      return clear();
+    },
+
+    put: function(piece, square) {
+      return put(piece, square);
+    },
+
+    get: function(square) {
+      return get(square);
+    },
+
+    remove: function(square) {
+      return remove(square);
+    },
+
+    perft: function(depth) {
+      return perft(depth);
+    },
+
+    square_color: function(square) {
+      if (square in SQUARES) {
+        var sq_0x88 = SQUARES[square];
+        return ((rank(sq_0x88) + file(sq_0x88)) % 2 === 0) ? 'light' : 'dark';
+      }
+
+      return null;
+    },
+
+    history: function(options) {
+      var reversed_history = [];
+      var move_history = [];
+      var verbose = (typeof options !== 'undefined' && 'verbose' in options &&
+                     options.verbose);
+
+      while (history.length > 0) {
+        reversed_history.push(undo_move());
+      }
+
+      while (reversed_history.length > 0) {
+        var move = reversed_history.pop();
+        if (verbose) {
+          move_history.push(make_pretty(move));
+        } else {
+          move_history.push(move_to_san(move));
+        }
+        make_move(move);
+      }
+
+      return move_history;
+    }
+
+  };
+};