diff --git a/.gitattributes b/.gitattributes
index 594bf50ed..7affbdbf9 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,2 @@
-*.sh text eol=lf
-*.txt text eol=lf
+* text=auto eol=lf
diff --git a/freeciv-web/src/main/webapp/css/bluecurve.css b/freeciv-web/src/main/webapp/css/bluecurve.css
index 18e9f2080..c5218c1a8 100644
--- a/freeciv-web/src/main/webapp/css/bluecurve.css
+++ b/freeciv-web/src/main/webapp/css/bluecurve.css
@@ -1,79 +1,79 @@
-/*
- back: rgb(230,230,230)
- dark: rgb(90,97,90)
- medium rgb(189,190,189)
- */
-
-.dynamic-slider-control {
- position: relative;
- -moz-user-focus: normal;
- -moz-user-select: none;
- cursor: default;
-}
-
-.horizontal {
- width: 200px;
- height: 27px;
-}
-
-.vertical {
- width: 29px;
- height: 200px;
-}
-
-.dynamic-slider-control input {
- display: none;
-}
-
-.dynamic-slider-control .handle {
- position: absolute;
- font-size: 1px;
- overflow: hidden;
- -moz-user-select: none;
- cursor: default;
-}
-
-.dynamic-slider-control.horizontal .handle {
- width: 31px;
- height: 14px;
- background-image: url("/images/handle.horizontal.png");
-}
-
-.dynamic-slider-control.horizontal .handle div {}
-.dynamic-slider-control.horizontal .handle.hover {}
-
-.dynamic-slider-control.vertical .handle {
- width: 15px;
- height: 31px;
- background-image: url("/images/handle.vertical.png");
-}
-
-.dynamic-slider-control.vertical .handle.hover {}
-
-.dynamic-slider-control .line {
- position: absolute;
- font-size: 0.01mm;
- overflow: hidden;
- border: 1px solid rgb(90,97,90);
- background: rgb(189,190,189);
-
- behavior: url("css/boxsizing.htc"); /* ie path bug */
- box-sizing: content-box;
- -moz-box-sizing: content-box;
-}
-.dynamic-slider-control.vertical .line {
- width: 3px;
-}
-
-.dynamic-slider-control.horizontal .line {
- height: 3px;
-}
-
-.dynamic-slider-control .line div {
- width: 1px;
- height: 1px;
-
- border: 1px solid;
- border-color: rgb(230,230,230) rgb(189,190,189)
- rgb(189,190,189) rgb(230,230,230);
-}
+/*
+ back: rgb(230,230,230)
+ dark: rgb(90,97,90)
+ medium rgb(189,190,189)
+ */
+
+.dynamic-slider-control {
+ position: relative;
+ -moz-user-focus: normal;
+ -moz-user-select: none;
+ cursor: default;
+}
+
+.horizontal {
+ width: 200px;
+ height: 27px;
+}
+
+.vertical {
+ width: 29px;
+ height: 200px;
+}
+
+.dynamic-slider-control input {
+ display: none;
+}
+
+.dynamic-slider-control .handle {
+ position: absolute;
+ font-size: 1px;
+ overflow: hidden;
+ -moz-user-select: none;
+ cursor: default;
+}
+
+.dynamic-slider-control.horizontal .handle {
+ width: 31px;
+ height: 14px;
+ background-image: url("/images/handle.horizontal.png");
+}
+
+.dynamic-slider-control.horizontal .handle div {}
+.dynamic-slider-control.horizontal .handle.hover {}
+
+.dynamic-slider-control.vertical .handle {
+ width: 15px;
+ height: 31px;
+ background-image: url("/images/handle.vertical.png");
+}
+
+.dynamic-slider-control.vertical .handle.hover {}
+
+.dynamic-slider-control .line {
+ position: absolute;
+ font-size: 0.01mm;
+ overflow: hidden;
+ border: 1px solid rgb(90,97,90);
+ background: rgb(189,190,189);
+
+ behavior: url("css/boxsizing.htc"); /* ie path bug */
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+}
+.dynamic-slider-control.vertical .line {
+ width: 3px;
+}
+
+.dynamic-slider-control.horizontal .line {
+ height: 3px;
+}
+
+.dynamic-slider-control .line div {
+ width: 1px;
+ height: 1px;
+
+ border: 1px solid;
+ border-color: rgb(230,230,230) rgb(189,190,189)
+ rgb(189,190,189) rgb(230,230,230);
+}
diff --git a/freeciv-web/src/main/webapp/javascript/libs/bmp_lib.js b/freeciv-web/src/main/webapp/javascript/libs/bmp_lib.js
index 34ecb1dc1..b3c069d00 100644
--- a/freeciv-web/src/main/webapp/javascript/libs/bmp_lib.js
+++ b/freeciv-web/src/main/webapp/javascript/libs/bmp_lib.js
@@ -1,410 +1,410 @@
-/**********************************************************************
- Freeciv - Copyright (C) 2009 - Andreas Røsdal andrearo@pvv.ntnu.no
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-***********************************************************************/
-
-
-/**
- * BMP Library for JavaScript
- *
- * Copyright 2008 Neil Fraser.
- * http://neil.fraser.name/software/bmp_lib/
- *
- * 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.
- */
-
-// Root object for BMP Library.
-var bmp_lib = {};
-
-
-/**
- * Replace the contents of an image or a table with a picture.
- * @param {string|Element} table The ID of an image or a table,
- * or the actual image or table element.
- * @param {Array} grid The image data.
- * @param {Array} opt_palette Optional palette data.
- * @throws {string} If the element is invalid.
- */
-bmp_lib.render = function(element, grid, opt_palette) {
- if (typeof element == 'string') {
- element = document.getElementById(element);
- }
- if (!element || !element.tagName) {
- throw('bmp_lib.render: Invalid element: ' + element);
- } else if (element.tagName == 'IMG') {
- element.src = this.imageSource(grid, opt_palette);
- } else if (element.tagName == 'TABLE') {
- var data = this.tableBody(grid, opt_palette);
- // IE throws "Unknown runtime error" if one sets table.innerHTML.
- // Using insertRow/insertCell works, but takes 10 times longer.
- // Instead, build a new table, then swap out the old one.
- if ('outerHTML' in element) {
- // IE, Safari, Opera.
- // Clear existing table.
- var rowCount = element.rows.length;
- for (var y = rowCount - 1; y >= 0; y--) {
- element.deleteRow(y);
- }
- // Fetch the opening table tag.
- var tableTag = element.outerHTML;
- tableTag = tableTag.substring(0, tableTag.indexOf('>') + 1);
- var tempDiv = document.createElement('DIV');
- tempDiv.innerHTML = tableTag + data + '';
- element.parentElement.replaceChild(tempDiv.firstChild, element);
- } else {
- // Firefox.
- element.innerHTML = data;
- }
- } else {
- throw('bmp_lib.render: Invalid HTML tag: ' + element.tagName);
- }
-};
-
-
-/**
- * Create a BMP image and encode it so that it may be set directly to the
- * 'src' attribute of an HTML image tag.
- * @param {Array} grid The image data.
- * @param {Array} opt_palette Optional palette data.
- * @return {string} Base64-encoded image data with header.
- */
-bmp_lib.imageSource = function(grid, opt_palette) {
- var a = this.normalize_(grid, opt_palette)
- var data = this.createBmp_(a[0], a[1]);
- return 'data:image/bmp;base64,' + this.encode64_(data);
-};
-
-
-/**
- * Create table contents with each cell depicting one pixel.
- * May be written directly between the
and
tags.
- * @param {Array} grid The image data.
- * @param {Array} opt_palette Optional palette data.
- * @return {string} HTML soup depicting the image as a table.
- */
-bmp_lib.tableBody = function(grid, opt_palette) {
- // Note: run-length encoding was attempted but resulted in a slight slowdown.
- var a = this.normalize_(grid, opt_palette);
- grid = a[0];
- var palette = a[1];
- var height = grid.length;
- var width = height && grid[0].length;
- var rgb;
- var table = [];
- for (var y = 0; y < height; y++) {
- row = [];
- for (var x = 0; x < width; x++) {
- if (palette) {
- rgb = palette[grid[y].charCodeAt(x)];
- } else {
- rgb = grid[y][x];
- }
- var colour = this.dec2hex_(rgb[0]) + this.dec2hex_(rgb[1]) +
- this.dec2hex_(rgb[2]);
- row[x] = '
';
- }
- row.unshift('
');
- row.push('
');
- table[y] = row.join('');
- }
- return table.join('\n');
-};
-
-
-/**
- * Verifies that the grid and palette are one of the three known types. If
- * the grid is a 2D array of numbers, convert it to an array of binary strings.
- * @param {Array} grid The image data.
- * @param {Array} opt_palette Optional palette data.
- * @return {Array.} A tuple containing the a normalized grid and palette.
- * @private
- */
-bmp_lib.normalize_ = function(grid, opt_palette) {
- var palette;
- // Check what type of data was provided.
- if (grid.length == 0) {
- // 0x0 picture.
- palette = null;
- } else if (typeof grid[0] == 'string' && opt_palette) {
- // Array of strings, with palette.
- palette = opt_palette;
- } else if (typeof grid[0] == 'object' && typeof grid[0][0] == 'number' &&
- opt_palette) {
- // 2D array of numbers, with palette. Convert to array of strings.
- grid = this.arrayArrayToArrayStr_(grid);
- palette = opt_palette;
- } else if (typeof grid[0] == 'object' && typeof grid[0][0] == 'object' &&
- grid[0][0].length >= 3) {
- // 2D array of [r, g, b] tuples, without palette. True-colour mode.
- palette = null;
- } else {
- // WTF?
- throw('Invalid argument types.');
- }
- return [grid, palette];
-};
-
-
-/**
- * Assemble a BMP based on the image data and an optional palette.
- * If a palette is provided and contains 256 or fewer colours, the BMP is
- * in 8-bit paletted mode, otherwise it is in 24-bit true-colour mode.
- * @param {Array} grid The image data.
- * @param {Array?} palette Optional palette data.
- * @return {string} BMP as binary string.
- * @private
- */
-bmp_lib.createBmp_ = function(grid, palette) {
- // xxxx and yyyy are placeholders for offsets (computed later).
- var bitmapFileHeader = 'BMxxxx\0\0\0\0yyyy';
-
- // Assemble the info header.
- var height = grid.length;
- var width = height && grid[0].length;
- var biHeight = this.multixbyteEncode_(height, 4);
- var biWidth = this.multixbyteEncode_(width, 4);
- var bfOffBits = this.multixbyteEncode_(40, 4);
- var bitCount;
- if (palette && palette.length <= 256) {
- bitCount = 8;
- } else {
- bitCount = 24;
- if (palette) {
- // Convert the oversized palette into inline-colours.
- grid = this.depalette_(grid, palette);
- }
- palette = null;
- }
- var biBitCount = this.multixbyteEncode_(bitCount, 2);
- var bitmapInfoHeader = bfOffBits + biWidth + biHeight + '\x01\0' +
- biBitCount + '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0';
-
- // Compute the palette.
- var rgbQuad;
- if (bitCount != 24) {
- var palette_str = String(palette);
- if (bmp_lib.createBmp_.palette_str_cache == palette_str) {
- // The previously computed palette was identical. Use it.
- rgbQuad = bmp_lib.createBmp_.rgbQuad_cache;
- } else {
- rgbQuad = [];
- var r = 0;
- var g = 0;
- var b = 0;
- for (var x = 0; x < 256; x++) {
- if (x < palette.length) {
- r = palette[x][0];
- g = palette[x][1];
- b = palette[x][2];
- }
- rgbQuad[x] = String.fromCharCode(b, g, r, 0);
- }
- rgbQuad = rgbQuad.join('');
- // Cache this result in case the next call uses the same palette.
- bmp_lib.createBmp_.palette_str_cache = palette_str;
- bmp_lib.createBmp_.rgbQuad_cache = rgbQuad;
- }
- } else {
- rgbQuad = '';
- }
-
- var padding;
- if (width % 4 == 1) {
- padding = '\0\0\0';
- } else if (width % 4 == 2) {
- padding = '\0\0';
- } else if (width % 4 == 3) {
- padding = '\0';
- } else {
- padding = '';
- }
- if (bitCount == 24) {
- padding = padding + padding + padding;
- }
-
- var data = [];
- // BMPs are drawn from the bottom up.
- for (var y = 0; y < height; y++) {
- var row = grid[height - y - 1];
- if (bitCount == 8) {
- data[y] = row + padding;
- } else if (bitCount == 24) {
- for (var x = 0; x < width; x++) {
- data.push(String.fromCharCode(row[x][2], row[x][1], row[x][0]));
- }
- data.push(padding);
- }
- }
- data = data.join('');
-
- var bitmap = bitmapFileHeader + bitmapInfoHeader + rgbQuad + data;
- // Specify the offset from the beginning of the file to the bitmap data.
- bitmap = bitmap.replace(/yyyy/, this.multixbyteEncode_(
- bitmapFileHeader.length + bitmapInfoHeader.length + rgbQuad.length, 4));
- // Insert the size of the bitmap in xbytes.
- bitmap = bitmap.replace(/xxxx/, this.multixbyteEncode_(bitmap.length, 4));
- return bitmap;
-};
-
-// Cached palette to avoid recomputing identical palettes.
-bmp_lib.createBmp_.palette_str_cache = '';
-bmp_lib.createBmp_.rgbQuad_cache = '';
-
-
-/**
- * Return a binary string of the specified xbyte length that encodes the
- * specified number. LITTLE-ENDIAN!
- * @param {number} number The numeric value to be encoded.
- * @param {Array?} palette The number of xbytes to use.
- * @return {string} BMP as binary string.
- * @throws {string} If the number is too big to fit in the xbyte space.
- * @private
- */
-bmp_lib.multixbyteEncode_ = function(number, xbytes) {
- if (number < 0 || xbytes < 0) {
- throw('Negative numbers not allowed.');
- }
- var oldbase = 1;
- var string = '';
- for (var x = 0; x < xbytes; x++) {
- var xbyte = 0;
- if (number != 0) {
- var base = oldbase * 256;
- xbyte = number % base;
- number = number - xbyte;
- xbyte = xbyte / oldbase;
- oldbase = base;
- }
- string += String.fromCharCode(xbyte);
- }
- if (number != 0) {
- throw('Overflow, number too big for string length');
- }
- return string;
-};
-
-
-/**
- * Converts a 2D array of numbers into an array of binary strings.
- * @param {Array.>} grid Array of Arrays of numbers.
- * @return {Array.} Array of strings.
- * @private
- */
-bmp_lib.arrayArrayToArrayStr_ = function(arrayArray) {
- var arrayStr = Array(arrayArray.length);
- for (var y = 0; y < arrayArray.length; y++) {
- line = [];
- for (var x = 0; x < arrayArray[y].length; x++) {
- line[x] = String.fromCharCode(arrayArray[y][x]);
- }
- arrayStr[y] = line.join('');
- }
- return arrayStr;
-};
-
-
-/**
- * Convert a paletted image into a 24-bit true-colour image.
- * Used when a palette has more than 256 colours.
- * @param {Array.} grid The image data.
- * @param {Array.>} palette Palette data.
- * @return {Array.>>} 2D array of RGB tuples.
- * @private
- */
-bmp_lib.depalette_ = function(oldGrid, palette) {
- var newGrid = Array(oldGrid.length);
- for (var y = 0; y < oldGrid.length; y++) {
- newGrid[y] = [];
- for (var x = 0; x < oldGrid[y].length; x++) {
- newGrid[y][x] = palette[oldGrid[y].charCodeAt(x)];
- }
- }
- return newGrid;
-};
-
-
-/**
- * Converts decimal to hex. Used for HTML colour codes.
- * @param {number} decimal 0-255.
- * @return {string} '00'-'FF'.
- * @private
- */
-bmp_lib.dec2hex_ = function(decimal) {
- var a = decimal % 16;
- var b = (decimal - a) / 16;
- return bmp_lib.dec2hex_.hexChars.charAt(b) +
- bmp_lib.dec2hex_.hexChars.charAt(a);
-};
-
-bmp_lib.dec2hex_.hexChars = '0123456789ABCDEF';
-
-
-// This code was written by Tyler Akins and has been placed in the
-// public domain. It would be nice if you left this header intact.
-// Base64 code from Tyler Akins -- http://rumkin.com
-
-/**
- * Encode a binary string as Base64.
- * @param {string} input Binary string.
- * @return {string} Base64-encoded string.
- * @private
- */
-bmp_lib.encode64_ = function(input) {
- var output = '';
- var i = 0;
-
- do {
- var chr1 = input.charCodeAt(i++);
- var chr2 = input.charCodeAt(i++);
- var chr3 = input.charCodeAt(i++);
-
- var enc1 = chr1 >> 2;
- var enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- var enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- var enc4 = chr3 & 63;
-
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
-
- output = output + bmp_lib.encode64_.keyStr.charAt(enc1) +
- bmp_lib.encode64_.keyStr.charAt(enc2) +
- bmp_lib.encode64_.keyStr.charAt(enc3) +
- bmp_lib.encode64_.keyStr.charAt(enc4);
- } while (i < input.length);
-
- return output;
-};
-
-bmp_lib.encode64_.keyStr =
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
-
-// Some browsers (Gecko, Webkit) have a native function for base64 encoding.
-if ('btoa' in window && typeof window.btoa == 'function' &&
- window.btoa('hello') == 'aGVsbG8=') {
- // Overwrite previous function with native call.
- bmp_lib.encode64_ = function(input) {
- return window.btoa(input);
- }
-}
-
+/**********************************************************************
+ Freeciv - Copyright (C) 2009 - Andreas Røsdal andrearo@pvv.ntnu.no
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+***********************************************************************/
+
+
+/**
+ * BMP Library for JavaScript
+ *
+ * Copyright 2008 Neil Fraser.
+ * http://neil.fraser.name/software/bmp_lib/
+ *
+ * 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.
+ */
+
+// Root object for BMP Library.
+var bmp_lib = {};
+
+
+/**
+ * Replace the contents of an image or a table with a picture.
+ * @param {string|Element} table The ID of an image or a table,
+ * or the actual image or table element.
+ * @param {Array} grid The image data.
+ * @param {Array} opt_palette Optional palette data.
+ * @throws {string} If the element is invalid.
+ */
+bmp_lib.render = function(element, grid, opt_palette) {
+ if (typeof element == 'string') {
+ element = document.getElementById(element);
+ }
+ if (!element || !element.tagName) {
+ throw('bmp_lib.render: Invalid element: ' + element);
+ } else if (element.tagName == 'IMG') {
+ element.src = this.imageSource(grid, opt_palette);
+ } else if (element.tagName == 'TABLE') {
+ var data = this.tableBody(grid, opt_palette);
+ // IE throws "Unknown runtime error" if one sets table.innerHTML.
+ // Using insertRow/insertCell works, but takes 10 times longer.
+ // Instead, build a new table, then swap out the old one.
+ if ('outerHTML' in element) {
+ // IE, Safari, Opera.
+ // Clear existing table.
+ var rowCount = element.rows.length;
+ for (var y = rowCount - 1; y >= 0; y--) {
+ element.deleteRow(y);
+ }
+ // Fetch the opening table tag.
+ var tableTag = element.outerHTML;
+ tableTag = tableTag.substring(0, tableTag.indexOf('>') + 1);
+ var tempDiv = document.createElement('DIV');
+ tempDiv.innerHTML = tableTag + data + '';
+ element.parentElement.replaceChild(tempDiv.firstChild, element);
+ } else {
+ // Firefox.
+ element.innerHTML = data;
+ }
+ } else {
+ throw('bmp_lib.render: Invalid HTML tag: ' + element.tagName);
+ }
+};
+
+
+/**
+ * Create a BMP image and encode it so that it may be set directly to the
+ * 'src' attribute of an HTML image tag.
+ * @param {Array} grid The image data.
+ * @param {Array} opt_palette Optional palette data.
+ * @return {string} Base64-encoded image data with header.
+ */
+bmp_lib.imageSource = function(grid, opt_palette) {
+ var a = this.normalize_(grid, opt_palette)
+ var data = this.createBmp_(a[0], a[1]);
+ return 'data:image/bmp;base64,' + this.encode64_(data);
+};
+
+
+/**
+ * Create table contents with each cell depicting one pixel.
+ * May be written directly between the
and
tags.
+ * @param {Array} grid The image data.
+ * @param {Array} opt_palette Optional palette data.
+ * @return {string} HTML soup depicting the image as a table.
+ */
+bmp_lib.tableBody = function(grid, opt_palette) {
+ // Note: run-length encoding was attempted but resulted in a slight slowdown.
+ var a = this.normalize_(grid, opt_palette);
+ grid = a[0];
+ var palette = a[1];
+ var height = grid.length;
+ var width = height && grid[0].length;
+ var rgb;
+ var table = [];
+ for (var y = 0; y < height; y++) {
+ row = [];
+ for (var x = 0; x < width; x++) {
+ if (palette) {
+ rgb = palette[grid[y].charCodeAt(x)];
+ } else {
+ rgb = grid[y][x];
+ }
+ var colour = this.dec2hex_(rgb[0]) + this.dec2hex_(rgb[1]) +
+ this.dec2hex_(rgb[2]);
+ row[x] = '
';
+ }
+ row.unshift('
');
+ row.push('
');
+ table[y] = row.join('');
+ }
+ return table.join('\n');
+};
+
+
+/**
+ * Verifies that the grid and palette are one of the three known types. If
+ * the grid is a 2D array of numbers, convert it to an array of binary strings.
+ * @param {Array} grid The image data.
+ * @param {Array} opt_palette Optional palette data.
+ * @return {Array.} A tuple containing the a normalized grid and palette.
+ * @private
+ */
+bmp_lib.normalize_ = function(grid, opt_palette) {
+ var palette;
+ // Check what type of data was provided.
+ if (grid.length == 0) {
+ // 0x0 picture.
+ palette = null;
+ } else if (typeof grid[0] == 'string' && opt_palette) {
+ // Array of strings, with palette.
+ palette = opt_palette;
+ } else if (typeof grid[0] == 'object' && typeof grid[0][0] == 'number' &&
+ opt_palette) {
+ // 2D array of numbers, with palette. Convert to array of strings.
+ grid = this.arrayArrayToArrayStr_(grid);
+ palette = opt_palette;
+ } else if (typeof grid[0] == 'object' && typeof grid[0][0] == 'object' &&
+ grid[0][0].length >= 3) {
+ // 2D array of [r, g, b] tuples, without palette. True-colour mode.
+ palette = null;
+ } else {
+ // WTF?
+ throw('Invalid argument types.');
+ }
+ return [grid, palette];
+};
+
+
+/**
+ * Assemble a BMP based on the image data and an optional palette.
+ * If a palette is provided and contains 256 or fewer colours, the BMP is
+ * in 8-bit paletted mode, otherwise it is in 24-bit true-colour mode.
+ * @param {Array} grid The image data.
+ * @param {Array?} palette Optional palette data.
+ * @return {string} BMP as binary string.
+ * @private
+ */
+bmp_lib.createBmp_ = function(grid, palette) {
+ // xxxx and yyyy are placeholders for offsets (computed later).
+ var bitmapFileHeader = 'BMxxxx\0\0\0\0yyyy';
+
+ // Assemble the info header.
+ var height = grid.length;
+ var width = height && grid[0].length;
+ var biHeight = this.multixbyteEncode_(height, 4);
+ var biWidth = this.multixbyteEncode_(width, 4);
+ var bfOffBits = this.multixbyteEncode_(40, 4);
+ var bitCount;
+ if (palette && palette.length <= 256) {
+ bitCount = 8;
+ } else {
+ bitCount = 24;
+ if (palette) {
+ // Convert the oversized palette into inline-colours.
+ grid = this.depalette_(grid, palette);
+ }
+ palette = null;
+ }
+ var biBitCount = this.multixbyteEncode_(bitCount, 2);
+ var bitmapInfoHeader = bfOffBits + biWidth + biHeight + '\x01\0' +
+ biBitCount + '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0';
+
+ // Compute the palette.
+ var rgbQuad;
+ if (bitCount != 24) {
+ var palette_str = String(palette);
+ if (bmp_lib.createBmp_.palette_str_cache == palette_str) {
+ // The previously computed palette was identical. Use it.
+ rgbQuad = bmp_lib.createBmp_.rgbQuad_cache;
+ } else {
+ rgbQuad = [];
+ var r = 0;
+ var g = 0;
+ var b = 0;
+ for (var x = 0; x < 256; x++) {
+ if (x < palette.length) {
+ r = palette[x][0];
+ g = palette[x][1];
+ b = palette[x][2];
+ }
+ rgbQuad[x] = String.fromCharCode(b, g, r, 0);
+ }
+ rgbQuad = rgbQuad.join('');
+ // Cache this result in case the next call uses the same palette.
+ bmp_lib.createBmp_.palette_str_cache = palette_str;
+ bmp_lib.createBmp_.rgbQuad_cache = rgbQuad;
+ }
+ } else {
+ rgbQuad = '';
+ }
+
+ var padding;
+ if (width % 4 == 1) {
+ padding = '\0\0\0';
+ } else if (width % 4 == 2) {
+ padding = '\0\0';
+ } else if (width % 4 == 3) {
+ padding = '\0';
+ } else {
+ padding = '';
+ }
+ if (bitCount == 24) {
+ padding = padding + padding + padding;
+ }
+
+ var data = [];
+ // BMPs are drawn from the bottom up.
+ for (var y = 0; y < height; y++) {
+ var row = grid[height - y - 1];
+ if (bitCount == 8) {
+ data[y] = row + padding;
+ } else if (bitCount == 24) {
+ for (var x = 0; x < width; x++) {
+ data.push(String.fromCharCode(row[x][2], row[x][1], row[x][0]));
+ }
+ data.push(padding);
+ }
+ }
+ data = data.join('');
+
+ var bitmap = bitmapFileHeader + bitmapInfoHeader + rgbQuad + data;
+ // Specify the offset from the beginning of the file to the bitmap data.
+ bitmap = bitmap.replace(/yyyy/, this.multixbyteEncode_(
+ bitmapFileHeader.length + bitmapInfoHeader.length + rgbQuad.length, 4));
+ // Insert the size of the bitmap in xbytes.
+ bitmap = bitmap.replace(/xxxx/, this.multixbyteEncode_(bitmap.length, 4));
+ return bitmap;
+};
+
+// Cached palette to avoid recomputing identical palettes.
+bmp_lib.createBmp_.palette_str_cache = '';
+bmp_lib.createBmp_.rgbQuad_cache = '';
+
+
+/**
+ * Return a binary string of the specified xbyte length that encodes the
+ * specified number. LITTLE-ENDIAN!
+ * @param {number} number The numeric value to be encoded.
+ * @param {Array?} palette The number of xbytes to use.
+ * @return {string} BMP as binary string.
+ * @throws {string} If the number is too big to fit in the xbyte space.
+ * @private
+ */
+bmp_lib.multixbyteEncode_ = function(number, xbytes) {
+ if (number < 0 || xbytes < 0) {
+ throw('Negative numbers not allowed.');
+ }
+ var oldbase = 1;
+ var string = '';
+ for (var x = 0; x < xbytes; x++) {
+ var xbyte = 0;
+ if (number != 0) {
+ var base = oldbase * 256;
+ xbyte = number % base;
+ number = number - xbyte;
+ xbyte = xbyte / oldbase;
+ oldbase = base;
+ }
+ string += String.fromCharCode(xbyte);
+ }
+ if (number != 0) {
+ throw('Overflow, number too big for string length');
+ }
+ return string;
+};
+
+
+/**
+ * Converts a 2D array of numbers into an array of binary strings.
+ * @param {Array.>} grid Array of Arrays of numbers.
+ * @return {Array.} Array of strings.
+ * @private
+ */
+bmp_lib.arrayArrayToArrayStr_ = function(arrayArray) {
+ var arrayStr = Array(arrayArray.length);
+ for (var y = 0; y < arrayArray.length; y++) {
+ line = [];
+ for (var x = 0; x < arrayArray[y].length; x++) {
+ line[x] = String.fromCharCode(arrayArray[y][x]);
+ }
+ arrayStr[y] = line.join('');
+ }
+ return arrayStr;
+};
+
+
+/**
+ * Convert a paletted image into a 24-bit true-colour image.
+ * Used when a palette has more than 256 colours.
+ * @param {Array.} grid The image data.
+ * @param {Array.>} palette Palette data.
+ * @return {Array.>>} 2D array of RGB tuples.
+ * @private
+ */
+bmp_lib.depalette_ = function(oldGrid, palette) {
+ var newGrid = Array(oldGrid.length);
+ for (var y = 0; y < oldGrid.length; y++) {
+ newGrid[y] = [];
+ for (var x = 0; x < oldGrid[y].length; x++) {
+ newGrid[y][x] = palette[oldGrid[y].charCodeAt(x)];
+ }
+ }
+ return newGrid;
+};
+
+
+/**
+ * Converts decimal to hex. Used for HTML colour codes.
+ * @param {number} decimal 0-255.
+ * @return {string} '00'-'FF'.
+ * @private
+ */
+bmp_lib.dec2hex_ = function(decimal) {
+ var a = decimal % 16;
+ var b = (decimal - a) / 16;
+ return bmp_lib.dec2hex_.hexChars.charAt(b) +
+ bmp_lib.dec2hex_.hexChars.charAt(a);
+};
+
+bmp_lib.dec2hex_.hexChars = '0123456789ABCDEF';
+
+
+// This code was written by Tyler Akins and has been placed in the
+// public domain. It would be nice if you left this header intact.
+// Base64 code from Tyler Akins -- http://rumkin.com
+
+/**
+ * Encode a binary string as Base64.
+ * @param {string} input Binary string.
+ * @return {string} Base64-encoded string.
+ * @private
+ */
+bmp_lib.encode64_ = function(input) {
+ var output = '';
+ var i = 0;
+
+ do {
+ var chr1 = input.charCodeAt(i++);
+ var chr2 = input.charCodeAt(i++);
+ var chr3 = input.charCodeAt(i++);
+
+ var enc1 = chr1 >> 2;
+ var enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ var enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ var enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output + bmp_lib.encode64_.keyStr.charAt(enc1) +
+ bmp_lib.encode64_.keyStr.charAt(enc2) +
+ bmp_lib.encode64_.keyStr.charAt(enc3) +
+ bmp_lib.encode64_.keyStr.charAt(enc4);
+ } while (i < input.length);
+
+ return output;
+};
+
+bmp_lib.encode64_.keyStr =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+
+// Some browsers (Gecko, Webkit) have a native function for base64 encoding.
+if ('btoa' in window && typeof window.btoa == 'function' &&
+ window.btoa('hello') == 'aGVsbG8=') {
+ // Overwrite previous function with native call.
+ bmp_lib.encode64_ = function(input) {
+ return window.btoa(input);
+ }
+}
+
diff --git a/freeciv-web/src/main/webapp/javascript/libs/range.js b/freeciv-web/src/main/webapp/javascript/libs/range.js
index f7d390dec..c36ee8c9b 100644
--- a/freeciv-web/src/main/webapp/javascript/libs/range.js
+++ b/freeciv-web/src/main/webapp/javascript/libs/range.js
@@ -1,121 +1,121 @@
-/**********************************************************************
- Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
- Copyright (C) 2009-2015 The Freeciv-web project
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-***********************************************************************/
-
-
-
-function Range() {
- this._value = 0;
- this._minimum = 0;
- this._maximum = 100;
- this._extent = 0;
-
- this._isChanging = false;
-}
-
-Range.prototype.setValue = function (value) {
- value = Math.round(parseFloat(value));
- if (isNaN(value)) return;
- if (this._value != value) {
- if (value + this._extent > this._maximum)
- this._value = this._maximum - this._extent;
- else if (value < this._minimum)
- this._value = this._minimum;
- else
- this._value = value;
- if (!this._isChanging && typeof this.onchange == "function")
- this.onchange();
- }
-};
-
-Range.prototype.getValue = function () {
- return this._value;
-};
-
-Range.prototype.setExtent = function (extent) {
- if (this._extent != extent) {
- if (extent < 0)
- this._extent = 0;
- else if (this._value + extent > this._maximum)
- this._extent = this._maximum - this._value;
- else
- this._extent = extent;
- if (!this._isChanging && typeof this.onchange == "function")
- this.onchange();
- }
-};
-
-Range.prototype.getExtent = function () {
- return this._extent;
-};
-
-Range.prototype.setMinimum = function (minimum) {
- if (this._minimum != minimum) {
- var oldIsChanging = this._isChanging;
- this._isChanging = true;
-
- this._minimum = minimum;
-
- if (minimum > this._value)
- this.setValue(minimum);
- if (minimum > this._maximum) {
- this._extent = 0;
- this.setMaximum(minimum);
- this.setValue(minimum);
- }
- if (minimum + this._extent > this._maximum)
- this._extent = this._maximum - this._minimum;
-
- this._isChanging = oldIsChanging;
- if (!this._isChanging && typeof this.onchange == "function")
- this.onchange();
- }
-};
-
-Range.prototype.getMinimum = function () {
- return this._minimum;
-};
-
-Range.prototype.setMaximum = function (maximum) {
- if (this._maximum != maximum) {
- var oldIsChanging = this._isChanging;
- this._isChanging = true;
-
- this._maximum = maximum;
-
- if (maximum < this._value)
- this.setValue(maximum - this._extent);
- if (maximum < this._minimum) {
- this._extent = 0;
- this.setMinimum(maximum);
- this.setValue(this._maximum);
- }
- if (maximum < this._minimum + this._extent)
- this._extent = this._maximum - this._minimum;
- if (maximum < this._value + this._extent)
- this._extent = this._maximum - this._value;
-
- this._isChanging = oldIsChanging;
- if (!this._isChanging && typeof this.onchange == "function")
- this.onchange();
- }
-};
-
-Range.prototype.getMaximum = function () {
- return this._maximum;
-};
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2015 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+
+
+function Range() {
+ this._value = 0;
+ this._minimum = 0;
+ this._maximum = 100;
+ this._extent = 0;
+
+ this._isChanging = false;
+}
+
+Range.prototype.setValue = function (value) {
+ value = Math.round(parseFloat(value));
+ if (isNaN(value)) return;
+ if (this._value != value) {
+ if (value + this._extent > this._maximum)
+ this._value = this._maximum - this._extent;
+ else if (value < this._minimum)
+ this._value = this._minimum;
+ else
+ this._value = value;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getValue = function () {
+ return this._value;
+};
+
+Range.prototype.setExtent = function (extent) {
+ if (this._extent != extent) {
+ if (extent < 0)
+ this._extent = 0;
+ else if (this._value + extent > this._maximum)
+ this._extent = this._maximum - this._value;
+ else
+ this._extent = extent;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getExtent = function () {
+ return this._extent;
+};
+
+Range.prototype.setMinimum = function (minimum) {
+ if (this._minimum != minimum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._minimum = minimum;
+
+ if (minimum > this._value)
+ this.setValue(minimum);
+ if (minimum > this._maximum) {
+ this._extent = 0;
+ this.setMaximum(minimum);
+ this.setValue(minimum);
+ }
+ if (minimum + this._extent > this._maximum)
+ this._extent = this._maximum - this._minimum;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMinimum = function () {
+ return this._minimum;
+};
+
+Range.prototype.setMaximum = function (maximum) {
+ if (this._maximum != maximum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._maximum = maximum;
+
+ if (maximum < this._value)
+ this.setValue(maximum - this._extent);
+ if (maximum < this._minimum) {
+ this._extent = 0;
+ this.setMinimum(maximum);
+ this.setValue(this._maximum);
+ }
+ if (maximum < this._minimum + this._extent)
+ this._extent = this._maximum - this._minimum;
+ if (maximum < this._value + this._extent)
+ this._extent = this._maximum - this._value;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMaximum = function () {
+ return this._maximum;
+};
diff --git a/freeciv-web/src/main/webapp/javascript/libs/slider.js b/freeciv-web/src/main/webapp/javascript/libs/slider.js
index 0f81c995a..4f10d4fda 100644
--- a/freeciv-web/src/main/webapp/javascript/libs/slider.js
+++ b/freeciv-web/src/main/webapp/javascript/libs/slider.js
@@ -1,469 +1,469 @@
-/**********************************************************************
- Freeciv - Copyright (C) 2009 - Andreas Røsdal andrearo@pvv.ntnu.no
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-***********************************************************************/
-
-
-Slider.isSupported = typeof document.createElement != "undefined" &&
- typeof document.documentElement != "undefined" &&
- typeof document.documentElement.offsetWidth == "number";
-
-
-function Slider(oElement, oInput, sOrientation) {
- if (!oElement) return;
- this._orientation = sOrientation || "horizontal";
- this._range = new Range();
- this._range.setExtent(0);
- this._blockIncrement = 10;
- this._unitIncrement = 1;
- this._timer = new Timer(100);
-
-
- if (Slider.isSupported && oElement) {
-
- this.document = oElement.ownerDocument || oElement.document;
-
- this.element = oElement;
- this.element.slider = this;
- this.element.unselectable = "on";
-
- // add class name tag to class name
- this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
-
- // create line
- this.line = this.document.createElement("DIV");
- this.line.className = "line";
- this.line.unselectable = "on";
- this.line.appendChild(this.document.createElement("DIV"));
- this.element.appendChild(this.line);
-
- // create handle
- this.handle = this.document.createElement("DIV");
- this.handle.className = "handle";
- this.handle.unselectable = "on";
- this.handle.appendChild(this.document.createElement("DIV"));
- this.handle.firstChild.appendChild(
- this.document.createTextNode(String.fromCharCode(160)));
- this.element.appendChild(this.handle);
- }
-
- this.input = oInput;
-
- // events
- var oThis = this;
- this._range.onchange = function () {
- oThis.recalculate();
- if (typeof oThis.onchange == "function")
- oThis.onchange();
- };
-
- if (Slider.isSupported && oElement) {
- this.element.onfocus = Slider.eventHandlers.onfocus;
- this.element.onblur = Slider.eventHandlers.onblur;
- this.element.onmousedown = Slider.eventHandlers.onmousedown;
- this.element.onmouseover = Slider.eventHandlers.onmouseover;
- this.element.onmouseout = Slider.eventHandlers.onmouseout;
- this.element.onkeydown = Slider.eventHandlers.onkeydown;
- this.element.onkeypress = Slider.eventHandlers.onkeypress;
- this.element.onmousewheel = Slider.eventHandlers.onmousewheel;
- this.handle.onselectstart =
- this.element.onselectstart = function () { return false; };
-
- this._timer.ontimer = function () {
- oThis.ontimer();
- };
-
- // extra recalculate for ie
- window.setTimeout(function() {
- oThis.recalculate();
- }, 1);
- }
- else {
- this.input.onchange = function (e) {
- oThis.setValue(oThis.input.value);
- };
- }
-}
-
-Slider.eventHandlers = {
-
- // helpers to make events a bit easier
- getEvent: function (e, el) {
- if (!e) {
- if (el)
- e = el.document.parentWindow.event;
- else
- e = window.event;
- }
- if (!e.srcElement) {
- var el = e.target;
- while (el != null && el.nodeType != 1)
- el = el.parentNode;
- e.srcElement = el;
- }
- if (typeof e.offsetX == "undefined") {
- e.offsetX = e.layerX;
- e.offsetY = e.layerY;
- }
-
- return e;
- },
-
- getDocument: function (e) {
- if (e.target)
- return e.target.ownerDocument;
- return e.srcElement.document;
- },
-
- getSlider: function (e) {
- var el = e.target || e.srcElement;
- while (el != null && el.slider == null) {
- el = el.parentNode;
- }
- if (el)
- return el.slider;
- return null;
- },
-
- getLine: function (e) {
- var el = e.target || e.srcElement;
- while (el != null && el.className != "line") {
- el = el.parentNode;
- }
- return el;
- },
-
- getHandle: function (e) {
- var el = e.target || e.srcElement;
- var re = /handle/;
- while (el != null && !re.test(el.className)) {
- el = el.parentNode;
- }
- return el;
- },
- // end helpers
-
- onfocus: function (e) {
- var s = this.slider;
- s._focused = true;
- s.handle.className = "handle hover";
- },
-
- onblur: function (e) {
- var s = this.slider
- s._focused = false;
- s.handle.className = "handle";
- },
-
- onmouseover: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var s = this.slider;
- if (e.srcElement == s.handle)
- s.handle.className = "handle hover";
- },
-
- onmouseout: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var s = this.slider;
- if (e.srcElement == s.handle && !s._focused)
- s.handle.className = "handle";
- },
-
- onmousedown: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var s = this.slider;
- if (s.element.focus)
- s.element.focus();
-
- Slider._currentInstance = s;
- var doc = s.document;
-
- if (doc.addEventListener) {
- doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
- doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
- }
- else if (doc.attachEvent) {
- doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
- doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
- doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
- s.element.setCapture();
- }
-
- if (Slider.eventHandlers.getHandle(e)) { // start drag
- Slider._sliderDragData = {
- screenX: e.screenX,
- screenY: e.screenY,
- dx: e.screenX - s.handle.offsetLeft,
- dy: e.screenY - s.handle.offsetTop,
- startValue: s.getValue(),
- slider: s
- };
- }
- else {
- var lineEl = Slider.eventHandlers.getLine(e);
- s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
- s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
- s._increasing = null;
- s.ontimer();
- }
- },
-
- onmousemove: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
-
- if (Slider._sliderDragData) { // drag
- var s = Slider._sliderDragData.slider;
-
- var boundSize = s.getMaximum() - s.getMinimum();
- var size, pos, reset;
-
- if (s._orientation == "horizontal") {
- size = s.element.offsetWidth - s.handle.offsetWidth;
- pos = e.screenX - Slider._sliderDragData.dx;
- reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
- }
- else {
- size = s.element.offsetHeight - s.handle.offsetHeight;
- pos = s.element.offsetHeight - s.handle.offsetHeight -
- (e.screenY - Slider._sliderDragData.dy);
- reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
- }
- s.setValue(reset ? Slider._sliderDragData.startValue :
- s.getMinimum() + boundSize * pos / size);
- return false;
- }
- else {
- var s = Slider._currentInstance;
- if (s != null) {
- var lineEl = Slider.eventHandlers.getLine(e);
- s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
- s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
- }
- }
-
- },
-
- onmouseup: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var s = Slider._currentInstance;
- var doc = s.document;
- if (doc.removeEventListener) {
- doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
- doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
- }
- else if (doc.detachEvent) {
- doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
- doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
- doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
- s.element.releaseCapture();
- }
-
- if (Slider._sliderDragData) { // end drag
- Slider._sliderDragData = null;
- }
- else {
- s._timer.stop();
- s._increasing = null;
- }
- Slider._currentInstance = null;
- },
-
- onkeydown: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- //var s = Slider.eventHandlers.getSlider(e);
- var s = this.slider;
- var kc = e.keyCode;
- switch (kc) {
- case 33: // page up
- s.setValue(s.getValue() + s.getBlockIncrement());
- break;
- case 34: // page down
- s.setValue(s.getValue() - s.getBlockIncrement());
- break;
- case 35: // end
- s.setValue(s.getOrientation() == "horizontal" ?
- s.getMaximum() :
- s.getMinimum());
- break;
- case 36: // home
- s.setValue(s.getOrientation() == "horizontal" ?
- s.getMinimum() :
- s.getMaximum());
- break;
- case 38: // up
- case 39: // right
- s.setValue(s.getValue() + s.getUnitIncrement());
- break;
-
- case 37: // left
- case 40: // down
- s.setValue(s.getValue() - s.getUnitIncrement());
- break;
- }
-
- if (kc >= 33 && kc <= 40) {
- return false;
- }
- },
-
- onkeypress: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var kc = e.keyCode;
- if (kc >= 33 && kc <= 40) {
- return false;
- }
- },
-
- onmousewheel: function (e) {
- e = Slider.eventHandlers.getEvent(e, this);
- var s = this.slider;
- if (s._focused) {
- s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
- // windows inverts this on horizontal sliders. That does not
- // make sense to me
- return false;
- }
- }
-};
-
-
-
-Slider.prototype.classNameTag = "dynamic-slider-control",
-
-Slider.prototype.setValue = function (v) {
- this._range.setValue(v);
- this.input.value = this.getValue();
-};
-
-Slider.prototype.getValue = function () {
- return this._range.getValue();
-};
-
-Slider.prototype.setMinimum = function (v) {
- this._range.setMinimum(v);
- this.input.value = this.getValue();
-};
-
-Slider.prototype.getMinimum = function () {
- return this._range.getMinimum();
-};
-
-Slider.prototype.setMaximum = function (v) {
- this._range.setMaximum(v);
- this.input.value = this.getValue();
-};
-
-Slider.prototype.getMaximum = function () {
- return this._range.getMaximum();
-};
-
-Slider.prototype.setUnitIncrement = function (v) {
- this._unitIncrement = v;
-};
-
-Slider.prototype.getUnitIncrement = function () {
- return this._unitIncrement;
-};
-
-Slider.prototype.setBlockIncrement = function (v) {
- this._blockIncrement = v;
-};
-
-Slider.prototype.getBlockIncrement = function () {
- return this._blockIncrement;
-};
-
-Slider.prototype.getOrientation = function () {
- return this._orientation;
-};
-
-Slider.prototype.setOrientation = function (sOrientation) {
- if (sOrientation != this._orientation) {
- if (Slider.isSupported && this.element) {
- // add class name tag to class name
- this.element.className = this.element.className.replace(this._orientation,
- sOrientation);
- }
- this._orientation = sOrientation;
- this.recalculate();
-
- }
-};
-
-Slider.prototype.recalculate = function() {
- if (!Slider.isSupported || !this.element) return;
-
- var w = this.element.offsetWidth;
- var h = this.element.offsetHeight;
- var hw = this.handle.offsetWidth;
- var hh = this.handle.offsetHeight;
- var lw = this.line.offsetWidth;
- var lh = this.line.offsetHeight;
-
- // this assumes a border-box layout
-
- if (this._orientation == "horizontal") {
- this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
- (this.getMaximum() - this.getMinimum()) + "px";
- this.handle.style.top = (h - hh) / 2 + "px";
-
- this.line.style.top = (h - lh) / 2 + "px";
- this.line.style.left = hw / 2 + "px";
- //this.line.style.right = hw / 2 + "px";
- this.line.style.width = Math.max(0, w - hw - 2)+ "px";
- this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
- }
- else {
- this.handle.style.left = (w - hw) / 2 + "px";
- this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
- (this.getMaximum() - this.getMinimum()) + "px";
-
- this.line.style.left = (w - lw) / 2 + "px";
- this.line.style.top = hh / 2 + "px";
- this.line.style.height = Math.max(0, h - hh - 2) + "px"; //hard coded border width
- //this.line.style.bottom = hh / 2 + "px";
- this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px"; //hard coded border width
- }
-};
-
-Slider.prototype.ontimer = function () {
- var hw = this.handle.offsetWidth;
- var hh = this.handle.offsetHeight;
- var hl = this.handle.offsetLeft;
- var ht = this.handle.offsetTop;
-
- if (this._orientation == "horizontal") {
- if (this._mouseX > hl + hw &&
- (this._increasing == null || this._increasing)) {
- this.setValue(this.getValue() + this.getBlockIncrement());
- this._increasing = true;
- }
- else if (this._mouseX < hl &&
- (this._increasing == null || !this._increasing)) {
- this.setValue(this.getValue() - this.getBlockIncrement());
- this._increasing = false;
- }
- }
- else {
- if (this._mouseY > ht + hh &&
- (this._increasing == null || !this._increasing)) {
- this.setValue(this.getValue() - this.getBlockIncrement());
- this._increasing = false;
- }
- else if (this._mouseY < ht &&
- (this._increasing == null || this._increasing)) {
- this.setValue(this.getValue() + this.getBlockIncrement());
- this._increasing = true;
- }
- }
-
- this._timer.start();
+/**********************************************************************
+ Freeciv - Copyright (C) 2009 - Andreas Røsdal andrearo@pvv.ntnu.no
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+***********************************************************************/
+
+
+Slider.isSupported = typeof document.createElement != "undefined" &&
+ typeof document.documentElement != "undefined" &&
+ typeof document.documentElement.offsetWidth == "number";
+
+
+function Slider(oElement, oInput, sOrientation) {
+ if (!oElement) return;
+ this._orientation = sOrientation || "horizontal";
+ this._range = new Range();
+ this._range.setExtent(0);
+ this._blockIncrement = 10;
+ this._unitIncrement = 1;
+ this._timer = new Timer(100);
+
+
+ if (Slider.isSupported && oElement) {
+
+ this.document = oElement.ownerDocument || oElement.document;
+
+ this.element = oElement;
+ this.element.slider = this;
+ this.element.unselectable = "on";
+
+ // add class name tag to class name
+ this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
+
+ // create line
+ this.line = this.document.createElement("DIV");
+ this.line.className = "line";
+ this.line.unselectable = "on";
+ this.line.appendChild(this.document.createElement("DIV"));
+ this.element.appendChild(this.line);
+
+ // create handle
+ this.handle = this.document.createElement("DIV");
+ this.handle.className = "handle";
+ this.handle.unselectable = "on";
+ this.handle.appendChild(this.document.createElement("DIV"));
+ this.handle.firstChild.appendChild(
+ this.document.createTextNode(String.fromCharCode(160)));
+ this.element.appendChild(this.handle);
+ }
+
+ this.input = oInput;
+
+ // events
+ var oThis = this;
+ this._range.onchange = function () {
+ oThis.recalculate();
+ if (typeof oThis.onchange == "function")
+ oThis.onchange();
+ };
+
+ if (Slider.isSupported && oElement) {
+ this.element.onfocus = Slider.eventHandlers.onfocus;
+ this.element.onblur = Slider.eventHandlers.onblur;
+ this.element.onmousedown = Slider.eventHandlers.onmousedown;
+ this.element.onmouseover = Slider.eventHandlers.onmouseover;
+ this.element.onmouseout = Slider.eventHandlers.onmouseout;
+ this.element.onkeydown = Slider.eventHandlers.onkeydown;
+ this.element.onkeypress = Slider.eventHandlers.onkeypress;
+ this.element.onmousewheel = Slider.eventHandlers.onmousewheel;
+ this.handle.onselectstart =
+ this.element.onselectstart = function () { return false; };
+
+ this._timer.ontimer = function () {
+ oThis.ontimer();
+ };
+
+ // extra recalculate for ie
+ window.setTimeout(function() {
+ oThis.recalculate();
+ }, 1);
+ }
+ else {
+ this.input.onchange = function (e) {
+ oThis.setValue(oThis.input.value);
+ };
+ }
+}
+
+Slider.eventHandlers = {
+
+ // helpers to make events a bit easier
+ getEvent: function (e, el) {
+ if (!e) {
+ if (el)
+ e = el.document.parentWindow.event;
+ else
+ e = window.event;
+ }
+ if (!e.srcElement) {
+ var el = e.target;
+ while (el != null && el.nodeType != 1)
+ el = el.parentNode;
+ e.srcElement = el;
+ }
+ if (typeof e.offsetX == "undefined") {
+ e.offsetX = e.layerX;
+ e.offsetY = e.layerY;
+ }
+
+ return e;
+ },
+
+ getDocument: function (e) {
+ if (e.target)
+ return e.target.ownerDocument;
+ return e.srcElement.document;
+ },
+
+ getSlider: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.slider == null) {
+ el = el.parentNode;
+ }
+ if (el)
+ return el.slider;
+ return null;
+ },
+
+ getLine: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.className != "line") {
+ el = el.parentNode;
+ }
+ return el;
+ },
+
+ getHandle: function (e) {
+ var el = e.target || e.srcElement;
+ var re = /handle/;
+ while (el != null && !re.test(el.className)) {
+ el = el.parentNode;
+ }
+ return el;
+ },
+ // end helpers
+
+ onfocus: function (e) {
+ var s = this.slider;
+ s._focused = true;
+ s.handle.className = "handle hover";
+ },
+
+ onblur: function (e) {
+ var s = this.slider
+ s._focused = false;
+ s.handle.className = "handle";
+ },
+
+ onmouseover: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle)
+ s.handle.className = "handle hover";
+ },
+
+ onmouseout: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle && !s._focused)
+ s.handle.className = "handle";
+ },
+
+ onmousedown: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s.element.focus)
+ s.element.focus();
+
+ Slider._currentInstance = s;
+ var doc = s.document;
+
+ if (doc.addEventListener) {
+ doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.attachEvent) {
+ doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.setCapture();
+ }
+
+ if (Slider.eventHandlers.getHandle(e)) { // start drag
+ Slider._sliderDragData = {
+ screenX: e.screenX,
+ screenY: e.screenY,
+ dx: e.screenX - s.handle.offsetLeft,
+ dy: e.screenY - s.handle.offsetTop,
+ startValue: s.getValue(),
+ slider: s
+ };
+ }
+ else {
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ s._increasing = null;
+ s.ontimer();
+ }
+ },
+
+ onmousemove: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+
+ if (Slider._sliderDragData) { // drag
+ var s = Slider._sliderDragData.slider;
+
+ var boundSize = s.getMaximum() - s.getMinimum();
+ var size, pos, reset;
+
+ if (s._orientation == "horizontal") {
+ size = s.element.offsetWidth - s.handle.offsetWidth;
+ pos = e.screenX - Slider._sliderDragData.dx;
+ reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
+ }
+ else {
+ size = s.element.offsetHeight - s.handle.offsetHeight;
+ pos = s.element.offsetHeight - s.handle.offsetHeight -
+ (e.screenY - Slider._sliderDragData.dy);
+ reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
+ }
+ s.setValue(reset ? Slider._sliderDragData.startValue :
+ s.getMinimum() + boundSize * pos / size);
+ return false;
+ }
+ else {
+ var s = Slider._currentInstance;
+ if (s != null) {
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ }
+ }
+
+ },
+
+ onmouseup: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = Slider._currentInstance;
+ var doc = s.document;
+ if (doc.removeEventListener) {
+ doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.detachEvent) {
+ doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.releaseCapture();
+ }
+
+ if (Slider._sliderDragData) { // end drag
+ Slider._sliderDragData = null;
+ }
+ else {
+ s._timer.stop();
+ s._increasing = null;
+ }
+ Slider._currentInstance = null;
+ },
+
+ onkeydown: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ //var s = Slider.eventHandlers.getSlider(e);
+ var s = this.slider;
+ var kc = e.keyCode;
+ switch (kc) {
+ case 33: // page up
+ s.setValue(s.getValue() + s.getBlockIncrement());
+ break;
+ case 34: // page down
+ s.setValue(s.getValue() - s.getBlockIncrement());
+ break;
+ case 35: // end
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMaximum() :
+ s.getMinimum());
+ break;
+ case 36: // home
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMinimum() :
+ s.getMaximum());
+ break;
+ case 38: // up
+ case 39: // right
+ s.setValue(s.getValue() + s.getUnitIncrement());
+ break;
+
+ case 37: // left
+ case 40: // down
+ s.setValue(s.getValue() - s.getUnitIncrement());
+ break;
+ }
+
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onkeypress: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var kc = e.keyCode;
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onmousewheel: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s._focused) {
+ s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
+ // windows inverts this on horizontal sliders. That does not
+ // make sense to me
+ return false;
+ }
+ }
+};
+
+
+
+Slider.prototype.classNameTag = "dynamic-slider-control",
+
+Slider.prototype.setValue = function (v) {
+ this._range.setValue(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getValue = function () {
+ return this._range.getValue();
+};
+
+Slider.prototype.setMinimum = function (v) {
+ this._range.setMinimum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMinimum = function () {
+ return this._range.getMinimum();
+};
+
+Slider.prototype.setMaximum = function (v) {
+ this._range.setMaximum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMaximum = function () {
+ return this._range.getMaximum();
+};
+
+Slider.prototype.setUnitIncrement = function (v) {
+ this._unitIncrement = v;
+};
+
+Slider.prototype.getUnitIncrement = function () {
+ return this._unitIncrement;
+};
+
+Slider.prototype.setBlockIncrement = function (v) {
+ this._blockIncrement = v;
+};
+
+Slider.prototype.getBlockIncrement = function () {
+ return this._blockIncrement;
+};
+
+Slider.prototype.getOrientation = function () {
+ return this._orientation;
+};
+
+Slider.prototype.setOrientation = function (sOrientation) {
+ if (sOrientation != this._orientation) {
+ if (Slider.isSupported && this.element) {
+ // add class name tag to class name
+ this.element.className = this.element.className.replace(this._orientation,
+ sOrientation);
+ }
+ this._orientation = sOrientation;
+ this.recalculate();
+
+ }
+};
+
+Slider.prototype.recalculate = function() {
+ if (!Slider.isSupported || !this.element) return;
+
+ var w = this.element.offsetWidth;
+ var h = this.element.offsetHeight;
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var lw = this.line.offsetWidth;
+ var lh = this.line.offsetHeight;
+
+ // this assumes a border-box layout
+
+ if (this._orientation == "horizontal") {
+ this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+ this.handle.style.top = (h - hh) / 2 + "px";
+
+ this.line.style.top = (h - lh) / 2 + "px";
+ this.line.style.left = hw / 2 + "px";
+ //this.line.style.right = hw / 2 + "px";
+ this.line.style.width = Math.max(0, w - hw - 2)+ "px";
+ this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
+ }
+ else {
+ this.handle.style.left = (w - hw) / 2 + "px";
+ this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+
+ this.line.style.left = (w - lw) / 2 + "px";
+ this.line.style.top = hh / 2 + "px";
+ this.line.style.height = Math.max(0, h - hh - 2) + "px"; //hard coded border width
+ //this.line.style.bottom = hh / 2 + "px";
+ this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px"; //hard coded border width
+ }
+};
+
+Slider.prototype.ontimer = function () {
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var hl = this.handle.offsetLeft;
+ var ht = this.handle.offsetTop;
+
+ if (this._orientation == "horizontal") {
+ if (this._mouseX > hl + hw &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ else if (this._mouseX < hl &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ }
+ else {
+ if (this._mouseY > ht + hh &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ else if (this._mouseY < ht &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ }
+
+ this._timer.start();
};
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/javascript/libs/timer.js b/freeciv-web/src/main/webapp/javascript/libs/timer.js
index 2904171ce..601d8cfe3 100644
--- a/freeciv-web/src/main/webapp/javascript/libs/timer.js
+++ b/freeciv-web/src/main/webapp/javascript/libs/timer.js
@@ -1,55 +1,55 @@
-/**********************************************************************
- Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
- Copyright (C) 2009-2015 The Freeciv-web project
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-***********************************************************************/
-
-
-
-function Timer(nPauseTime) {
- this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
- this._timer = null;
- this._isStarted = false;
-}
-
-Timer.prototype.start = function () {
- if (this.isStarted())
- this.stop();
- var oThis = this;
- this._timer = window.setTimeout(function () {
- if (typeof oThis.ontimer == "function")
- oThis.ontimer();
- }, this._pauseTime);
- this._isStarted = false;
-};
-
-Timer.prototype.stop = function () {
- if (this._timer != null)
- window.clearTimeout(this._timer);
- this._isStarted = false;
-};
-
-Timer.prototype.isStarted = function () {
- return this._isStarted;
-};
-
-Timer.prototype.getPauseTime = function () {
- return this._pauseTime;
-};
-
-Timer.prototype.setPauseTime = function (nPauseTime) {
- this._pauseTime = nPauseTime;
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2015 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+
+
+function Timer(nPauseTime) {
+ this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
+ this._timer = null;
+ this._isStarted = false;
+}
+
+Timer.prototype.start = function () {
+ if (this.isStarted())
+ this.stop();
+ var oThis = this;
+ this._timer = window.setTimeout(function () {
+ if (typeof oThis.ontimer == "function")
+ oThis.ontimer();
+ }, this._pauseTime);
+ this._isStarted = false;
+};
+
+Timer.prototype.stop = function () {
+ if (this._timer != null)
+ window.clearTimeout(this._timer);
+ this._isStarted = false;
+};
+
+Timer.prototype.isStarted = function () {
+ return this._isStarted;
+};
+
+Timer.prototype.getPauseTime = function () {
+ return this._pauseTime;
+};
+
+Timer.prototype.setPauseTime = function (nPauseTime) {
+ this._pauseTime = nPauseTime;
};
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/javascript/pbem.js b/freeciv-web/src/main/webapp/javascript/pbem.js
index 6735d34eb..ef53906b1 100644
--- a/freeciv-web/src/main/webapp/javascript/pbem.js
+++ b/freeciv-web/src/main/webapp/javascript/pbem.js
@@ -1,634 +1,634 @@
-/**********************************************************************
- Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
- Copyright (C) 2009-2015 The Freeciv-web project
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-***********************************************************************/
-
-var opponents = [];
-var pbem_phase_ended = false;
-
-var invited_players = [];
-
-/**************************************************************************
- Shows the Freeciv play-by-email dialog.
-**************************************************************************/
-function show_pbem_dialog()
-{
- var title = "Welcome to Freeciv-web";
- var message = "";
-
- if ($.getUrlVar('invited_by') != null) {
- var invited = $.getUrlVar('invited_by').replace(/[^a-zA-Z]/g,'');
- message = "You have been invited by " + invited + " for a Play-by-Email game of Freeciv-web. "
- + "You and " + invited + " will play alternating turns, and you will get an e-mail every time "
- + "it is your turn to play. First you can create a new user or log-in, then you will play "
- + "the first turn.";
- } else if ($.getUrlVar('savegame') != null) {
- message = "It is now your turn to play this Play-by-Email game. Please login to play your turn.";
- if (pbem_duplicate_turn_play_check()) return;
-
- } else {
- message = "You are about to start a Play-by-Email game, where you "
- + "can challenge other players, and each player will be notified when "
- + "it is their turn to play through e-mail. If you are a new player, then click the sign up button below. These are the game rules: "
- + "
The game will have between 2 and 4 human players playing alternating turns. Each player will get an e-mail when it is their turn to play.
"
- + "
Standard Freeciv-web rules are used with some changes to map size, research speed, start units and gold to speed up games.
"
- + "
Please complete your turn as soon as possible, and use at no longer than 7 days until you complete your turn.
"
- + "
Results of games with 2 players are stored to rank players.
"
- + "
Please post feedback and arrange new games on the forum.
"
- + "
";
- }
-
- // reset dialog page.
- $("#dialog").remove();
- $("").appendTo("div#game_page");
- $("#dialog").html(message);
- $("#dialog").attr("title", title);
- $("#dialog").dialog({
- bgiframe: true,
- modal: true,
- width: is_small_screen() ? "95%" : "60%",
- buttons:
- {
- "Signup new user": function() {
- show_new_user_account_dialog("pbem");
- },
- "Log In" : function() {
- login_pbem_user();
- },
- "Account...": function() {
- close_pbem_account();
- }
-
-
- }
-
- });
-
- $("#dialog").dialog('open');
-
- $.ajax({
- type: 'POST',
- url: "/user_count" ,
- success: function(data, textStatus, request){
- $("#user_count").html("We are now " + data + " players available for Play-By-Email games.");
- } });
-
- var stored_username = simpleStorage.get("username", "");
- var stored_password = simpleStorage.get("password", "");
- if (stored_username != null && stored_username != false && stored_password != null && stored_password != false) {
- // Not allowed to create a new user account when already logged in.
- $(".ui-dialog-buttonset button").first().button("disable");
- }
-
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function login_pbem_user()
-{
-
- var title = "Log in";
- var message = "Log in to your Freeciv-web user account:
" +
- "";
-
- // reset dialog page.
- $("#dialog").remove();
- $("").appendTo("div#game_page");
- $("#dialog").html(message);
- $("#dialog").attr("title", title);
- $("#dialog").dialog({
- bgiframe: true,
- modal: true,
- width: is_small_screen() ? "80%" : "40%",
- buttons: {"Invite random player": function() {
- $.ajax({
- type: 'POST',
- url: "/random_user" ,
- success: function(data, textStatus, request){
- var playercount = parseFloat($('#playercount').val());
- if (playercount == 2) {
- $("#opponent").val(data)
- } else {
- $("#opponent_1").val(data)
- }
- }
- });
- },
- "Cancel" : function() {
- show_pbem_dialog();
- },
-
- "Start game": function() {
- create_new_pbem_game();
- }
- }
-
- });
-
- var invited = $.getUrlVar('invited_by');
- if (invited != null) {
- $("#opponent").val(invited);
- $(".ui-dialog-buttonset button").first().hide();
- }
- prepare_pbem_opponent_selection();
- $('#playercount').change(prepare_pbem_opponent_selection);
-
- $("#dialog").dialog('open');
-}
-
-/**************************************************************************
- TODO: validate all usernames when game has 3 or more players.
-**************************************************************************/
-function prepare_pbem_opponent_selection() {
- var playercount = parseFloat($('#playercount').val());
- opponents = [];
- if (playercount == 2) {
- var msg = "Enter the username or e-mail address of the other player: "
- + "
Username/E-Mail:
"
- + "
"
- + "If the other player already has an account, then you will play the first turn. If you enter the e-mail "
- + "address of a new player, then we will send an invitation e-mail to that player "
- + "and the other player will play the first turn."
- $("#opponent_box").html(msg);
-
- } else if (playercount == 3 || playercount == 4) {
- var msg = "Enter the usernames of the other human players: "
- + "
"
- + "
You:
" + username + "
"
- + "
Username 2:
"
- + "
Username 3:
";
- if (playercount == 4) {
- msg += "
Username 4:
";
- }
-
- msg += "
"
- + "Before you fill in this form, the other players have to create their account and tell you their usernames. " +
- "You will play the first turn. The other players will get an e-mail when it is their turn to play. " +
- "This type of game can have human players only."
- $("#opponent_box").html(msg);
-
- }
- }
-
-/**************************************************************************
- Determines if the email is valid
-**************************************************************************/
-function validateEmail(email) {
- var checkemail = email;
- if (checkemail != null) checkemail = checkemail.replace("+", ""); // + is allowed.
- var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
- return re.test(checkemail);
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function forceLower(strInput)
-{
- strInput.value=strInput.value.toLowerCase();
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function create_new_pbem_game()
-{
- opponents = [];
- var playercount = parseFloat($('#playercount').val());
-
- if (playercount == 2) {
- var check_opponent = $("#opponent").val();
- if (check_opponent != null) check_opponent = check_opponent.trim();
-
- if (invited_players.indexOf(check_opponent) != -1) {
- swal("You have already invited " + check_opponent + ". There is no "
- + "need to invite multiple times. It can take some time before "
- + "the invitation email arrives to " + check_opponent);
- return;
- }
-
- if (check_opponent == username) {
- swal("No playing with yourself please.");
- return;
- }
-
- invited_players.push(check_opponent);
-
- $.ajax({
- type: 'POST',
- url: "/validate_user?userstring=" + check_opponent + "&invited_by=" + username,
- success: function(data, textStatus, request){
- if (data == "invitation") {
- send_pbem_invitation(check_opponent);
-
- } else if (data == "user_does_not_exist") {
- swal("Opponent does not exist. Please try another username.");
-
- } else if (data != null && data.length > 3) {
- opponents.push(data);
- network_init();
- $("#dialog").dialog('close');
- setTimeout(create_pbem_players, 3500);
- show_dialog_message("Game ready", "Play-By-Email game is now ready to start. " +
- "Click the start game button to play the first turn. You can also configure some " +
- "game settings before the game begins. The default settings are recommended. " +
- "Some settings are not supported in PBEM games, " +
- "such as more than four players, AI players or diplomacy. " +
- "As the first player, you can choose nation for both players. " +
- "Have a fun Play-By-Email game!"
- );
-
-
- } else {
- swal("Problem starting new pbem game.");
- }
- },
- error: function (request, textStatus, errorThrown) {
- swal("Opponent does not exist. Please try another username.");
- }
- });
-
- } else {
- // 3 or 4 players.
- var pbem_ready_players = 1;
- var game_start_ready = true;
- opponents.push($("#opponent_1").val());
- opponents.push($("#opponent_2").val());
- if (playercount == 4) {
- opponents.push($("#opponent_3").val());
- }
-
- for (var i = 0; i < opponents.length; i++) {
- if (opponents[i].length < 1) {
- swal("Please fill inn all players names.");
- return;
- } else if (opponents[i].indexOf("@") != -1) {
- swal("Please specify the username of the other player, not their e-mail address.");
- return;
- } else {
- $.ajax({
- async: false,
- type: 'POST',
- url: "/validate_user?userstring=" + opponents[i] + "&invited_by=" + username,
- success: function(data, textStatus, request) {
- if (data == "user_does_not_exist") {
- swal("Opponent does not exist. Please try another username.");
- game_start_ready = false;
- } else {
- pbem_ready_players = pbem_ready_players + 1;
- }
- }
- });
- }
- }
-
- // start new pbem game
- if (game_start_ready && pbem_ready_players == playercount) {
- network_init();
- $("#dialog").dialog('close');
- setTimeout(create_pbem_players, 3500);
- show_dialog_message("Game ready", "Play-By-Email game is now ready to start. " +
- "Click the start game button to play the first turn. You can also configure some " +
- "game settings before the game begins. The default settings are recommended. " +
- "Some settings are not supported in PBEM games, " +
- "such as more than four players, AI players or diplomacy. " +
- "As the first player, you can choose nation for all players. " +
- "Have a fun Play-By-Email game!"
- );
- }
- }
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function send_pbem_invitation(email)
-{
- $.ajax({
- type: 'POST',
- url: "/mailstatus?action=invite&to=" + email + "&from=" + username,
- success: function(data, textStatus, request){
- swal(email + " has been invited to Freeciv-web. You will "
- + "receive an e-mail when it is your turn to play. Now "
- + "you can wait for the other player.");
- $("#opponent").val("")
- },
- error: function (request, textStatus, errorThrown) {
- swal("Error: Unable to invite the opponent.");
- }
- });
-
-
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function create_pbem_players()
-{
- if (ws != null && ws.readyState === 1) {
- if (opponents != null && opponents.length > 0) {
- for (var i = 0; i < opponents.length; i++) {
- send_message("/create " + opponents[i]);
- }
- setTimeout(pbem_init_game, 1200);
-
- } else {
- swal("Error: invalid opponent selected.");
- }
- } else {
- setTimeout(create_pbem_players, 500);
- }
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function set_human_pbem_players()
-{
- for (var player_id in players) {
- var pplayer = players[player_id];
- if (pplayer['flags'].isSet(PLRF_AI) == true
- && pplayer['name'].toUpperCase() != username.toUpperCase()) {
- send_message("/aitoggle " + pplayer['name']);
- }
- }
-}
-
-/**************************************************************************
- Is this a Play-By-Email game?
-**************************************************************************/
-function is_pbem()
-{
- return ($.getUrlVar('action') == "pbem");
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function pbem_end_phase()
-{
- if (pbem_phase_ended) return;
- pbem_phase_ended = true;
- send_message("/save");
-
- show_dialog_message("Play By Email turn over",
- "Your turn is now over in this Play By Email game. Now the next player " +
- "will get an email with information about how to complete their turn. " +
- "You will also get an email about when it is your turn to play again. " +
- "See you again soon!" );
- if ($.getUrlVar('savegame') != null) {
- simpleStorage.set("pbem_" + $.getUrlVar('savegame'), "true");
- }
- $(window).unbind('beforeunload');
- setTimeout("window.location.href ='/';", 5000);
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function handle_pbem_load()
-{
- network_init();
- var savegame = $.getUrlVar('savegame');
- $("#dialog").dialog('close');
- $.blockUI();
-
- wait_for_text("You are logged in as", function () {
- load_game_real(savegame);
- });
- wait_for_text("Load complete", activate_pbem_player);
-
-}
-
-/**************************************************************************
- called when starting a new PBEM game.
-**************************************************************************/
-function pbem_init_game()
-{
- set_human_pbem_players();
- $.post("/freeciv_time_played_stats?type=pbem").fail(function() {});
-}
-
-
-/**************************************************************************
- called when loading a PBEM game.
-**************************************************************************/
-function activate_pbem_player()
-{
- send_message("/take " + username);
- send_message_delayed("/start", 50);
-}
-
-
-/**************************************************************************
- Dialog for the user to close their user accounts.
-**************************************************************************/
-function close_pbem_account()
-{
-
- var title = "Close account";
- var message = "To deactivate your account, please enter your username and password:
"
- + "
Username:
"
- + "
Password:
"
- + "
";
-
- // reset dialog page.
- $("#dialog").remove();
- $("").appendTo("div#game_page");
-
- $("#dialog").html(message);
- $("#dialog").attr("title", title);
- $("#dialog").dialog({
- bgiframe: true,
- modal: true,
- width: is_small_screen() ? "80%" : "50%",
- buttons:
- {
- "Cancel" : function() {
- show_pbem_dialog();
- },
- "Unsubscribe" : function() {
- request_deactivate_account();
- }
- }
- });
-
- $("#dialog").dialog('open');
-}
-
-
-
-/**************************************************************************
- Will request the user to be deactivated (activated='0' in DB).
-**************************************************************************/
-function request_deactivate_account()
-{
- var usr = $("#username").val().trim();
- var password = $("#password").val().trim();
-
- var shaObj = new jsSHA("SHA-512", "TEXT");
- shaObj.update(password);
- var sha_password = encodeURIComponent(shaObj.getHash("HEX"));
-
- $.ajax({
- type: 'POST',
- url: "/deactivate_user?username=" + usr + "&sha_password=" + sha_password,
- success: function(data, textStatus, request){
- swal("User account has been deactivated!");
-
- },
- error: function (request, textStatus, errorThrown) {
- swal("deactivate user failed.");
- }
- });
-
-}
-
-/**************************************************************************
- Checks for user playing same turn twice.
-**************************************************************************/
-function pbem_duplicate_turn_play_check()
-{
- var pbem_savegame = $.getUrlVar('savegame');
- if (pbem_savegame != null) {
- var previously_played = simpleStorage.get("pbem_" + pbem_savegame, "");
- if (previously_played != null) {
- swal("This Play-By-Email turn has already been played and can't be played again. Sorry!");
- return true;
- } else {
- return false;
- }
-
- }
-
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-function get_pbem_game_key()
-{
- return "pbem_tech_" + client.conn.username + players[0]['name'] + players[1]['name'];
-}
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2015 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+var opponents = [];
+var pbem_phase_ended = false;
+
+var invited_players = [];
+
+/**************************************************************************
+ Shows the Freeciv play-by-email dialog.
+**************************************************************************/
+function show_pbem_dialog()
+{
+ var title = "Welcome to Freeciv-web";
+ var message = "";
+
+ if ($.getUrlVar('invited_by') != null) {
+ var invited = $.getUrlVar('invited_by').replace(/[^a-zA-Z]/g,'');
+ message = "You have been invited by " + invited + " for a Play-by-Email game of Freeciv-web. "
+ + "You and " + invited + " will play alternating turns, and you will get an e-mail every time "
+ + "it is your turn to play. First you can create a new user or log-in, then you will play "
+ + "the first turn.";
+ } else if ($.getUrlVar('savegame') != null) {
+ message = "It is now your turn to play this Play-by-Email game. Please login to play your turn.";
+ if (pbem_duplicate_turn_play_check()) return;
+
+ } else {
+ message = "You are about to start a Play-by-Email game, where you "
+ + "can challenge other players, and each player will be notified when "
+ + "it is their turn to play through e-mail. If you are a new player, then click the sign up button below. These are the game rules: "
+ + "
The game will have between 2 and 4 human players playing alternating turns. Each player will get an e-mail when it is their turn to play.
"
+ + "
Standard Freeciv-web rules are used with some changes to map size, research speed, start units and gold to speed up games.
"
+ + "
Please complete your turn as soon as possible, and use at no longer than 7 days until you complete your turn.
"
+ + "
Results of games with 2 players are stored to rank players.
"
+ + "
Please post feedback and arrange new games on the forum.
"
+ + "
";
+ }
+
+ // reset dialog page.
+ $("#dialog").remove();
+ $("").appendTo("div#game_page");
+ $("#dialog").html(message);
+ $("#dialog").attr("title", title);
+ $("#dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: is_small_screen() ? "95%" : "60%",
+ buttons:
+ {
+ "Signup new user": function() {
+ show_new_user_account_dialog("pbem");
+ },
+ "Log In" : function() {
+ login_pbem_user();
+ },
+ "Account...": function() {
+ close_pbem_account();
+ }
+
+
+ }
+
+ });
+
+ $("#dialog").dialog('open');
+
+ $.ajax({
+ type: 'POST',
+ url: "/user_count" ,
+ success: function(data, textStatus, request){
+ $("#user_count").html("We are now " + data + " players available for Play-By-Email games.");
+ } });
+
+ var stored_username = simpleStorage.get("username", "");
+ var stored_password = simpleStorage.get("password", "");
+ if (stored_username != null && stored_username != false && stored_password != null && stored_password != false) {
+ // Not allowed to create a new user account when already logged in.
+ $(".ui-dialog-buttonset button").first().button("disable");
+ }
+
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function login_pbem_user()
+{
+
+ var title = "Log in";
+ var message = "Log in to your Freeciv-web user account:
" +
+ "";
+
+ // reset dialog page.
+ $("#dialog").remove();
+ $("").appendTo("div#game_page");
+ $("#dialog").html(message);
+ $("#dialog").attr("title", title);
+ $("#dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: is_small_screen() ? "80%" : "40%",
+ buttons: {"Invite random player": function() {
+ $.ajax({
+ type: 'POST',
+ url: "/random_user" ,
+ success: function(data, textStatus, request){
+ var playercount = parseFloat($('#playercount').val());
+ if (playercount == 2) {
+ $("#opponent").val(data)
+ } else {
+ $("#opponent_1").val(data)
+ }
+ }
+ });
+ },
+ "Cancel" : function() {
+ show_pbem_dialog();
+ },
+
+ "Start game": function() {
+ create_new_pbem_game();
+ }
+ }
+
+ });
+
+ var invited = $.getUrlVar('invited_by');
+ if (invited != null) {
+ $("#opponent").val(invited);
+ $(".ui-dialog-buttonset button").first().hide();
+ }
+ prepare_pbem_opponent_selection();
+ $('#playercount').change(prepare_pbem_opponent_selection);
+
+ $("#dialog").dialog('open');
+}
+
+/**************************************************************************
+ TODO: validate all usernames when game has 3 or more players.
+**************************************************************************/
+function prepare_pbem_opponent_selection() {
+ var playercount = parseFloat($('#playercount').val());
+ opponents = [];
+ if (playercount == 2) {
+ var msg = "Enter the username or e-mail address of the other player: "
+ + "
Username/E-Mail:
"
+ + "
"
+ + "If the other player already has an account, then you will play the first turn. If you enter the e-mail "
+ + "address of a new player, then we will send an invitation e-mail to that player "
+ + "and the other player will play the first turn."
+ $("#opponent_box").html(msg);
+
+ } else if (playercount == 3 || playercount == 4) {
+ var msg = "Enter the usernames of the other human players: "
+ + "
"
+ + "
You:
" + username + "
"
+ + "
Username 2:
"
+ + "
Username 3:
";
+ if (playercount == 4) {
+ msg += "
Username 4:
";
+ }
+
+ msg += "
"
+ + "Before you fill in this form, the other players have to create their account and tell you their usernames. " +
+ "You will play the first turn. The other players will get an e-mail when it is their turn to play. " +
+ "This type of game can have human players only."
+ $("#opponent_box").html(msg);
+
+ }
+ }
+
+/**************************************************************************
+ Determines if the email is valid
+**************************************************************************/
+function validateEmail(email) {
+ var checkemail = email;
+ if (checkemail != null) checkemail = checkemail.replace("+", ""); // + is allowed.
+ var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
+ return re.test(checkemail);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function forceLower(strInput)
+{
+ strInput.value=strInput.value.toLowerCase();
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function create_new_pbem_game()
+{
+ opponents = [];
+ var playercount = parseFloat($('#playercount').val());
+
+ if (playercount == 2) {
+ var check_opponent = $("#opponent").val();
+ if (check_opponent != null) check_opponent = check_opponent.trim();
+
+ if (invited_players.indexOf(check_opponent) != -1) {
+ swal("You have already invited " + check_opponent + ". There is no "
+ + "need to invite multiple times. It can take some time before "
+ + "the invitation email arrives to " + check_opponent);
+ return;
+ }
+
+ if (check_opponent == username) {
+ swal("No playing with yourself please.");
+ return;
+ }
+
+ invited_players.push(check_opponent);
+
+ $.ajax({
+ type: 'POST',
+ url: "/validate_user?userstring=" + check_opponent + "&invited_by=" + username,
+ success: function(data, textStatus, request){
+ if (data == "invitation") {
+ send_pbem_invitation(check_opponent);
+
+ } else if (data == "user_does_not_exist") {
+ swal("Opponent does not exist. Please try another username.");
+
+ } else if (data != null && data.length > 3) {
+ opponents.push(data);
+ network_init();
+ $("#dialog").dialog('close');
+ setTimeout(create_pbem_players, 3500);
+ show_dialog_message("Game ready", "Play-By-Email game is now ready to start. " +
+ "Click the start game button to play the first turn. You can also configure some " +
+ "game settings before the game begins. The default settings are recommended. " +
+ "Some settings are not supported in PBEM games, " +
+ "such as more than four players, AI players or diplomacy. " +
+ "As the first player, you can choose nation for both players. " +
+ "Have a fun Play-By-Email game!"
+ );
+
+
+ } else {
+ swal("Problem starting new pbem game.");
+ }
+ },
+ error: function (request, textStatus, errorThrown) {
+ swal("Opponent does not exist. Please try another username.");
+ }
+ });
+
+ } else {
+ // 3 or 4 players.
+ var pbem_ready_players = 1;
+ var game_start_ready = true;
+ opponents.push($("#opponent_1").val());
+ opponents.push($("#opponent_2").val());
+ if (playercount == 4) {
+ opponents.push($("#opponent_3").val());
+ }
+
+ for (var i = 0; i < opponents.length; i++) {
+ if (opponents[i].length < 1) {
+ swal("Please fill inn all players names.");
+ return;
+ } else if (opponents[i].indexOf("@") != -1) {
+ swal("Please specify the username of the other player, not their e-mail address.");
+ return;
+ } else {
+ $.ajax({
+ async: false,
+ type: 'POST',
+ url: "/validate_user?userstring=" + opponents[i] + "&invited_by=" + username,
+ success: function(data, textStatus, request) {
+ if (data == "user_does_not_exist") {
+ swal("Opponent does not exist. Please try another username.");
+ game_start_ready = false;
+ } else {
+ pbem_ready_players = pbem_ready_players + 1;
+ }
+ }
+ });
+ }
+ }
+
+ // start new pbem game
+ if (game_start_ready && pbem_ready_players == playercount) {
+ network_init();
+ $("#dialog").dialog('close');
+ setTimeout(create_pbem_players, 3500);
+ show_dialog_message("Game ready", "Play-By-Email game is now ready to start. " +
+ "Click the start game button to play the first turn. You can also configure some " +
+ "game settings before the game begins. The default settings are recommended. " +
+ "Some settings are not supported in PBEM games, " +
+ "such as more than four players, AI players or diplomacy. " +
+ "As the first player, you can choose nation for all players. " +
+ "Have a fun Play-By-Email game!"
+ );
+ }
+ }
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function send_pbem_invitation(email)
+{
+ $.ajax({
+ type: 'POST',
+ url: "/mailstatus?action=invite&to=" + email + "&from=" + username,
+ success: function(data, textStatus, request){
+ swal(email + " has been invited to Freeciv-web. You will "
+ + "receive an e-mail when it is your turn to play. Now "
+ + "you can wait for the other player.");
+ $("#opponent").val("")
+ },
+ error: function (request, textStatus, errorThrown) {
+ swal("Error: Unable to invite the opponent.");
+ }
+ });
+
+
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function create_pbem_players()
+{
+ if (ws != null && ws.readyState === 1) {
+ if (opponents != null && opponents.length > 0) {
+ for (var i = 0; i < opponents.length; i++) {
+ send_message("/create " + opponents[i]);
+ }
+ setTimeout(pbem_init_game, 1200);
+
+ } else {
+ swal("Error: invalid opponent selected.");
+ }
+ } else {
+ setTimeout(create_pbem_players, 500);
+ }
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function set_human_pbem_players()
+{
+ for (var player_id in players) {
+ var pplayer = players[player_id];
+ if (pplayer['flags'].isSet(PLRF_AI) == true
+ && pplayer['name'].toUpperCase() != username.toUpperCase()) {
+ send_message("/aitoggle " + pplayer['name']);
+ }
+ }
+}
+
+/**************************************************************************
+ Is this a Play-By-Email game?
+**************************************************************************/
+function is_pbem()
+{
+ return ($.getUrlVar('action') == "pbem");
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function pbem_end_phase()
+{
+ if (pbem_phase_ended) return;
+ pbem_phase_ended = true;
+ send_message("/save");
+
+ show_dialog_message("Play By Email turn over",
+ "Your turn is now over in this Play By Email game. Now the next player " +
+ "will get an email with information about how to complete their turn. " +
+ "You will also get an email about when it is your turn to play again. " +
+ "See you again soon!" );
+ if ($.getUrlVar('savegame') != null) {
+ simpleStorage.set("pbem_" + $.getUrlVar('savegame'), "true");
+ }
+ $(window).unbind('beforeunload');
+ setTimeout("window.location.href ='/';", 5000);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function handle_pbem_load()
+{
+ network_init();
+ var savegame = $.getUrlVar('savegame');
+ $("#dialog").dialog('close');
+ $.blockUI();
+
+ wait_for_text("You are logged in as", function () {
+ load_game_real(savegame);
+ });
+ wait_for_text("Load complete", activate_pbem_player);
+
+}
+
+/**************************************************************************
+ called when starting a new PBEM game.
+**************************************************************************/
+function pbem_init_game()
+{
+ set_human_pbem_players();
+ $.post("/freeciv_time_played_stats?type=pbem").fail(function() {});
+}
+
+
+/**************************************************************************
+ called when loading a PBEM game.
+**************************************************************************/
+function activate_pbem_player()
+{
+ send_message("/take " + username);
+ send_message_delayed("/start", 50);
+}
+
+
+/**************************************************************************
+ Dialog for the user to close their user accounts.
+**************************************************************************/
+function close_pbem_account()
+{
+
+ var title = "Close account";
+ var message = "To deactivate your account, please enter your username and password:
"
+ + "
Username:
"
+ + "
Password:
"
+ + "
";
+
+ // reset dialog page.
+ $("#dialog").remove();
+ $("").appendTo("div#game_page");
+
+ $("#dialog").html(message);
+ $("#dialog").attr("title", title);
+ $("#dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: is_small_screen() ? "80%" : "50%",
+ buttons:
+ {
+ "Cancel" : function() {
+ show_pbem_dialog();
+ },
+ "Unsubscribe" : function() {
+ request_deactivate_account();
+ }
+ }
+ });
+
+ $("#dialog").dialog('open');
+}
+
+
+
+/**************************************************************************
+ Will request the user to be deactivated (activated='0' in DB).
+**************************************************************************/
+function request_deactivate_account()
+{
+ var usr = $("#username").val().trim();
+ var password = $("#password").val().trim();
+
+ var shaObj = new jsSHA("SHA-512", "TEXT");
+ shaObj.update(password);
+ var sha_password = encodeURIComponent(shaObj.getHash("HEX"));
+
+ $.ajax({
+ type: 'POST',
+ url: "/deactivate_user?username=" + usr + "&sha_password=" + sha_password,
+ success: function(data, textStatus, request){
+ swal("User account has been deactivated!");
+
+ },
+ error: function (request, textStatus, errorThrown) {
+ swal("deactivate user failed.");
+ }
+ });
+
+}
+
+/**************************************************************************
+ Checks for user playing same turn twice.
+**************************************************************************/
+function pbem_duplicate_turn_play_check()
+{
+ var pbem_savegame = $.getUrlVar('savegame');
+ if (pbem_savegame != null) {
+ var previously_played = simpleStorage.get("pbem_" + pbem_savegame, "");
+ if (previously_played != null) {
+ swal("This Play-By-Email turn has already been played and can't be played again. Sorry!");
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+function get_pbem_game_key()
+{
+ return "pbem_tech_" + client.conn.username + players[0]['name'] + players[1]['name'];
+}
diff --git a/freeciv-web/src/main/webapp/javascript/scorelog.js b/freeciv-web/src/main/webapp/javascript/scorelog.js
index bf0dcc556..c00d57a22 100644
--- a/freeciv-web/src/main/webapp/javascript/scorelog.js
+++ b/freeciv-web/src/main/webapp/javascript/scorelog.js
@@ -1,187 +1,187 @@
-/**********************************************************************
- Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
- Copyright (C) 2009-2015 The Freeciv-web project
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-***********************************************************************/
-
-
-/****************************************************************************
- Shows the game scores dialog.
-****************************************************************************/
-function view_game_scores() {
- set_default_mapview_active();
- $("#scores_dialog").remove();
- $("").appendTo("div#game_page");
-
-
- var dialog_html = "
Please wait while generating score graphs...
"
- +"
";
-
- $("#scores_dialog").html(dialog_html);
- $("#scores_dialog").attr("title", "Game Scores");
- $("#scores_dialog").dialog({
- bgiframe: true,
- modal: true,
- width: is_small_screen() ? "95%" : "80%",
- height: is_small_screen() ? 560 : 710,
- buttons: {
- Ok: function() {
- $("#scores_dialog").dialog('close');
- $("#scores_tabs").remove();
- $("#scores_dialog").remove();
- }
- }
- });
-
- $("#scores_dialog").dialog('open');
-
-
- $.ajax({
- url: "/data/scorelogs/score-" + civserverport + ".log",
- dataType: "html",
- cache: false,
- async: true
- }).fail(function() {
- $("#scores_wait").html("Score graphs not shown, because server 'scorelog' variable is disabled,"
- + " or because of problem loading the score file.");
- $("#game_scores_button").button( "option", "disabled", true);
- }).done(function( data ) {
- handle_scorelog(data);
- });
-
-
-}
-
-/****************************************************************************
- Handles the scorelog file
-****************************************************************************/
-function handle_scorelog(scorelog) {
- var start_turn = 0;
- var scoreitems = scorelog.split("\n");
- var scoreplayers = {};
- var playerslist = [];
- var playernames = [];
- var scoretags = {};
- var resultdata = {};
- var scorecolors = [];
- for (var i = 0; i < scoreitems.length; i++) {
- var scoreitem = scoreitems[i];
- var scoredata = scoreitem.split(" ");
- if (scoredata.length >= 3) {
- if (scoredata[0] == "addplayer") {
- var pname = scoredata[3];
- for (var s = 4; s < scoredata.length; s++) {
- pname += " " + scoredata[s];
- }
- scoreplayers[scoredata[2]] = pname;
- playerslist.push(scoredata[2]);
- playernames.push(pname);
- var pplayer = player_by_name(pname);
- if (pplayer == null) {
- scorecolors.push("#ff0000");
- } else {
- scorecolors.push(nations[pplayer['nation']]['color']);
- }
-
- } else if (scoredata[0] == "turn") {
- if (start_turn==0) start_turn = scoredata[1];
-
- } else if (scoredata[0] == "tag") {
- scoretags[scoredata[1]] = scoredata[2];
-
- } else if (scoredata[0] == "data") {
- var turn = scoredata[1];
- var tag = scoredata[2];
- var player = scoredata[3];
- var value = scoredata[4];
- if (resultdata[tag] == null) {
- var s = {};
- s["turn"] = turn;
- s[player] = parseInt(value);
- resultdata[tag] = [];
- resultdata[tag][turn - start_turn] = s;
- } else if (resultdata[tag] != null && resultdata[tag][turn - start_turn] == null) {
- var s = {};
- s["turn"] = turn;
- s[player] = parseInt(value);
- resultdata[tag][turn - start_turn] = s;
- } else if (resultdata[tag][turn - start_turn] != null) {
- resultdata[tag][turn - start_turn][player] = parseInt(value);
- }
- }
- }
- }
- if (is_small_screen()) scoretags = {"0" : "score"};
-
- for (var key in scoretags) {
- var tagname = scoretags[key];
- $("#scores_ul").append("
");
- }
-
- var ps = 4;
- if (scoreitems.length >1000) ps = 0;
-
- for (var key in scoretags) {
- try {
- Morris.Line({
- element: 'scoreschart-' + key,
- data: resultdata[key],
- xkey: 'turn',
- ykeys: playerslist,
- labels: playernames,
- parseTime: false,
- lineColors : scorecolors,
- pointSize: ps
- });
- } catch(err) {
- console.log("Problem showing score log graph: " + err);
- }
- }
-
- $("#scores_tabs").tabs();
- $(".scores_tabber").css("padding", "1px");
- $("#scores_wait").hide();
- if (is_small_screen()) {
- $(".scorechart").height($("#scores_dialog").height() - 10);
- }
-}
-
-/****************************************************************************
-...
-****************************************************************************/
-function get_scorelog_name(tag) {
- var names = {
- "score" : "Score",
- "pop" : "Population",
- "bnp" : "Economics",
- "mfg" : "Production",
- "cities" : "Cities",
- "techs" : "Techs",
- "munits" : "Military units",
- "wonders" : "Wonders",
- "techout" : "Tech output",
- "landarea" : "Land area",
- "settledarea" : "Settled area",
- "gold" : "Gold",
- "unitsbuilt" : "Units built",
- "unitskilled" : "Units killed",
- "unitslost" : "Units lost"
- };
- return names[tag];
-}
-
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2015 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+
+/****************************************************************************
+ Shows the game scores dialog.
+****************************************************************************/
+function view_game_scores() {
+ set_default_mapview_active();
+ $("#scores_dialog").remove();
+ $("").appendTo("div#game_page");
+
+
+ var dialog_html = "
Please wait while generating score graphs...
"
+ +"
";
+
+ $("#scores_dialog").html(dialog_html);
+ $("#scores_dialog").attr("title", "Game Scores");
+ $("#scores_dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: is_small_screen() ? "95%" : "80%",
+ height: is_small_screen() ? 560 : 710,
+ buttons: {
+ Ok: function() {
+ $("#scores_dialog").dialog('close');
+ $("#scores_tabs").remove();
+ $("#scores_dialog").remove();
+ }
+ }
+ });
+
+ $("#scores_dialog").dialog('open');
+
+
+ $.ajax({
+ url: "/data/scorelogs/score-" + civserverport + ".log",
+ dataType: "html",
+ cache: false,
+ async: true
+ }).fail(function() {
+ $("#scores_wait").html("Score graphs not shown, because server 'scorelog' variable is disabled,"
+ + " or because of problem loading the score file.");
+ $("#game_scores_button").button( "option", "disabled", true);
+ }).done(function( data ) {
+ handle_scorelog(data);
+ });
+
+
+}
+
+/****************************************************************************
+ Handles the scorelog file
+****************************************************************************/
+function handle_scorelog(scorelog) {
+ var start_turn = 0;
+ var scoreitems = scorelog.split("\n");
+ var scoreplayers = {};
+ var playerslist = [];
+ var playernames = [];
+ var scoretags = {};
+ var resultdata = {};
+ var scorecolors = [];
+ for (var i = 0; i < scoreitems.length; i++) {
+ var scoreitem = scoreitems[i];
+ var scoredata = scoreitem.split(" ");
+ if (scoredata.length >= 3) {
+ if (scoredata[0] == "addplayer") {
+ var pname = scoredata[3];
+ for (var s = 4; s < scoredata.length; s++) {
+ pname += " " + scoredata[s];
+ }
+ scoreplayers[scoredata[2]] = pname;
+ playerslist.push(scoredata[2]);
+ playernames.push(pname);
+ var pplayer = player_by_name(pname);
+ if (pplayer == null) {
+ scorecolors.push("#ff0000");
+ } else {
+ scorecolors.push(nations[pplayer['nation']]['color']);
+ }
+
+ } else if (scoredata[0] == "turn") {
+ if (start_turn==0) start_turn = scoredata[1];
+
+ } else if (scoredata[0] == "tag") {
+ scoretags[scoredata[1]] = scoredata[2];
+
+ } else if (scoredata[0] == "data") {
+ var turn = scoredata[1];
+ var tag = scoredata[2];
+ var player = scoredata[3];
+ var value = scoredata[4];
+ if (resultdata[tag] == null) {
+ var s = {};
+ s["turn"] = turn;
+ s[player] = parseInt(value);
+ resultdata[tag] = [];
+ resultdata[tag][turn - start_turn] = s;
+ } else if (resultdata[tag] != null && resultdata[tag][turn - start_turn] == null) {
+ var s = {};
+ s["turn"] = turn;
+ s[player] = parseInt(value);
+ resultdata[tag][turn - start_turn] = s;
+ } else if (resultdata[tag][turn - start_turn] != null) {
+ resultdata[tag][turn - start_turn][player] = parseInt(value);
+ }
+ }
+ }
+ }
+ if (is_small_screen()) scoretags = {"0" : "score"};
+
+ for (var key in scoretags) {
+ var tagname = scoretags[key];
+ $("#scores_ul").append("