From 5243478f883e4997134e3abc8da57b7680129c89 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 24 Nov 2014 21:17:50 +0100 Subject: [PATCH] Clean up the minification stuff a bit; still not really in use. --- build.sh | 15 + externs/jquery-1.9.js | 2160 +++++++++++++++++++++++++++++++++++++++++ externs/webstorage.js | 148 +++ www/index.html | 9 +- www/js/chess.js | 1572 ++++++++++++++++++++++++++++++ 5 files changed, 3903 insertions(+), 1 deletion(-) create mode 100755 build.sh create mode 100644 externs/jquery-1.9.js create mode 100644 externs/webstorage.js create mode 100644 www/js/chess.js diff --git a/build.sh b/build.sh new file mode 100755 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 index 0000000..f6f8e89 --- /dev/null +++ b/externs/jquery-1.9.js @@ -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: + * var elt = /** @type {Element} * / (foo.get(0)); + * + * @see http://api.jquery.com/ + * @externs + */ + +/** + * @typedef {(Window|Document|Element|Array.|string|jQuery| + * NodeList)} + */ +var jQuerySelector; + +/** @typedef {function(...)|Array.} */ +var jQueryCallback; + +/** @typedef { + { + accepts: (Object.|undefined), + async: (?boolean|undefined), + beforeSend: (function(jQuery.jqXHR, (jQueryAjaxSettings|Object.))|undefined), + cache: (?boolean|undefined), + complete: (function(jQuery.jqXHR, string)|undefined), + contents: (Object.|undefined), + contentType: (?string|undefined), + context: (Object.|jQueryAjaxSettings|undefined), + converters: (Object.|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.|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.|jQuery|string| + * function())=} arg1 + * @param {(Element|jQuery|Document| + * Object.)=} arg2 + * @return {!jQuery} + */ +function jQuery(arg1, arg2) {} + +/** + * @constructor + * @extends {jQuery} + * @param {(jQuerySelector|Element|Object|Array.|jQuery|string| + * function())=} arg1 + * @param {(Element|jQuery|Document| + * Object.)=} arg2 + * @return {!jQuery} + */ +function $(arg1, arg2) {} + +/** + * @param {(jQuerySelector|Array.|string|jQuery)} arg1 + * @param {Element=} context + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.add = function(arg1, context) {}; + +/** + * @param {(jQuerySelector|Array.|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.|jQuery)=} content + * @return {!jQuery} + */ +jQuery.prototype.after = function(arg1, content) {}; + +/** + * @param {(string|jQueryAjaxSettings|Object.)} arg1 + * @param {(jQueryAjaxSettings|Object.)=} settings + * @return {jQuery.jqXHR} + */ +jQuery.ajax = function(arg1, settings) {}; + +/** + * @param {(string|jQueryAjaxSettings|Object.)} arg1 + * @param {(jQueryAjaxSettings|Object.)=} settings + * @return {jQuery.jqXHR} + */ +$.ajax = function(arg1, settings) {}; + +/** + * @param {function(!jQuery.event,XMLHttpRequest,(jQueryAjaxSettings|Object.))} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxComplete = function(handler) {}; + +/** + * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.),*)} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxError = function(handler) {}; + +/** + * @param {(string|function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR))} dataTypes + * @param {function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR)=} handler + */ +jQuery.ajaxPrefilter = function(dataTypes, handler) {}; + +/** + * @param {(string|function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR))} dataTypes + * @param {function((jQueryAjaxSettings|Object.),(jQueryAjaxSettings|Object.),jQuery.jqXHR)=} handler + */ +$.ajaxPrefilter = function(dataTypes, handler) {}; + +/** + * @param {function(!jQuery.event,jQuery.jqXHR,(jQueryAjaxSettings|Object.))} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxSend = function(handler) {}; + +/** @const {jQueryAjaxSettings|Object.} */ +jQuery.ajaxSettings; + +/** @const {jQueryAjaxSettings|Object.} */ +$.ajaxSettings = {}; + +/** @type {Object.} */ +jQuery.ajaxSettings.flatOptions = {}; + +/** @type {Object.} */ +$.ajaxSettings.flatOptions = {}; + +/** @type {boolean} */ +jQuery.ajaxSettings.processData; + +/** @type {boolean} */ +$.ajaxSettings.processData; + +/** @type {Object.} */ +jQuery.ajaxSettings.responseFields = {}; + +/** @type {Object.} */ +$.ajaxSettings.responseFields = {}; + +/** @param {jQueryAjaxSettings|Object.} options */ +jQuery.ajaxSetup = function(options) {}; + +/** @param {jQueryAjaxSettings|Object.} 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.), ?)} handler + * @return {!jQuery} + */ +jQuery.prototype.ajaxSuccess = function(handler) {}; + +/** + * @deprecated Please use .addBack(selector) instead. + * @return {!jQuery} + * @nosideeffects + */ +jQuery.prototype.andSelf = function() {}; + +/** + * @param {Object.} properties + * @param {(string|number|function()|Object.)=} arg2 + * @param {(string|function())=} easing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.animate = function(properties, arg2, easing, complete) {}; + +/** + * @param {(string|Element|Array.|jQuery|function(number,string))} arg1 + * @param {...(string|Element|Array.|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.)} 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.|jQuery)=} content + * @return {!jQuery} + */ +jQuery.prototype.before = function(arg1, content) {}; + +/** + * @param {(string|Object.)} arg1 + * @param {(Object.|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.)=} 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.)=} 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.)=} 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.)} arg1 + * @param {(string|number|function(number,*))=} arg2 + * @return {(string|!jQuery)} + */ +jQuery.prototype.css = function(arg1, arg2) {}; + +/** @type {Object.} */ +jQuery.cssHooks; + +/** @type {Object.} */ +$.cssHooks; + +/** + * @param {Element} elem + * @param {string=} key + * @param {*=} value + * @return {*} + */ +jQuery.data = function(elem, key, value) {}; + +/** + * @param {(string|Object.)=} 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.)=} 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.)} arg2 + * @param {(function(!jQuery.event=)|Object.)=} 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.)} 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.} */ +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.} */ +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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.focus = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.focusin = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)} 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| + * 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.)} + * @nosideeffects + */ +jQuery.prototype.get = function(index) {}; + +/** + * @param {string} url + * @param {(Object.|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.| + * function(Object.,string,jQuery.jqXHR))=} data + * @param {function(Object.,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.| + * function(Object.,string,jQuery.jqXHR))=} data + * @param {function(Object.,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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.keydown = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.keypress = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} 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)} arg1 + * @param {(function(!jQuery.event=)|Object.|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.)} 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.)} 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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mousedown = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseenter = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseleave = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mousemove = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseout = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.mouseover = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} 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.|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.)=} 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.)} arg1 + * @param {*=} selector + * @param {*=} data + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.on = function(arg1, selector, data, handler) {}; + +/** + * @param {(string|Object.)} 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.|Array.>)} obj + * @param {boolean=} traditional + * @return {string} + */ +jQuery.param = function(obj, traditional) {}; + +/** + * @param {(Object.|Array.>)} 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.} + */ +jQuery.parseHTML = function(data, context, keepScripts) {}; + +/** + * @param {string} data + * @param {(Element|boolean)=} context + * @param {boolean=} keepScripts + * @return {Array.} + */ +$.parseHTML = function(data, context, keepScripts) {}; + +/** + * @param {string} json + * @return {string|number|Object.|Array.|boolean} + */ +jQuery.parseJSON = function(json) {}; + +/** + * @param {string} json + * @return {Object.} + */ +$.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| + * 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| + * 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.)} 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.} elements + * @param {string=} name + * @param {Array.<*>=} args + * @return {!jQuery} + */ +jQuery.prototype.pushStack = function(elements, name, args) {}; + +/** + * @param {(string|Array.|function(function()))=} queueName + * @param {(Array.|function(function()))=} arg2 + * @return {(Array.|!jQuery)} + */ +jQuery.prototype.queue = function(queueName, arg2) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + * @param {(Array.|function())=} arg3 + * @return {(Array.|!jQuery)} + */ +jQuery.queue = function(elem, queueName, arg3) {}; + +/** + * @param {Element} elem + * @param {string=} queueName + * @param {(Array.|function())=} arg3 + * @return {(Array.|!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.)=} 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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.resize = function(arg1, handler) {}; + +/** + * @param {(function(!jQuery.event=)|Object.)=} 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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.select = function(arg1, handler) {}; + +/** + * @return {string} + * @nosideeffects + */ +jQuery.prototype.serialize = function() {}; + +/** + * @return {Array.>} + * @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|number)=} optionsOrDuration + * @param {(function()|string)=} completeOrEasing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.slideDown = + function(optionsOrDuration, completeOrEasing, complete) {}; + +/** + * @param {(Object.|string|number)=} optionsOrDuration + * @param {(function()|string)=} completeOrEasing + * @param {function()=} complete + * @return {!jQuery} + */ +jQuery.prototype.slideToggle = + function(optionsOrDuration, completeOrEasing, complete) {}; + +/** + * @param {(Object.|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.)=} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.submit = function(arg1, handler) {}; + +/** @type {Object.} + * @deprecated Please try to use feature detection instead. + */ +jQuery.support; + +/** @type {Object.} + * @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.} + * @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.|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.)=} arg2 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.undelegate = function(arg1, arg2, handler) {}; + +/** + * @param {Array.} arr + * @return {Array.} + */ +jQuery.unique = function(arr) {}; + +/** + * @param {Array.} arr + * @return {Array.} + */ +$.unique = function(arr) {}; + +/** + * @deprecated Please use .on( "unload", handler ) instead. + * @param {(function(!jQuery.event=)|Object.)} arg1 + * @param {function(!jQuery.event=)=} handler + * @return {!jQuery} + */ +jQuery.prototype.unload = function(arg1, handler) {}; + +/** @return {!jQuery} */ +jQuery.prototype.unwrap = function() {}; + +/** + * @param {(string|Array.|function(number,*))=} arg1 + * @return {(string|number|Array.|!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 index 0000000..eee69d2 --- /dev/null +++ b/externs/webstorage.js @@ -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) {}; + diff --git a/www/index.html b/www/index.html index cf6093e..9186709 100644 --- a/www/index.html +++ b/www/index.html @@ -72,9 +72,16 @@ Ding sound by Aiwa (CC-BY-3.0). 7-man Lomonosov tablebase lookup by ChessOK.

- + + + + + + diff --git a/www/js/chess.js b/www/js/chess.js new file mode 100644 index 0000000..201b9d8 --- /dev/null +++ b/www/js/chess.js @@ -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: "
" }) + */ + 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; + } + + }; +}; -- 2.39.2