forked from 0ad/0ad
Yves
4b1297b328
Each GUI Page gets its own compartment and all ScriptInterfaces in the same thread should now use the same JS Runtime. This is required for the SpiderMonkey upgrade. Check the ticket for details. Closes #2241 Refs #1886 Refs #1966 This was SVN commit r14496.
314 lines
7.7 KiB
JavaScript
314 lines
7.7 KiB
JavaScript
/*
|
|
DESCRIPTION : Generic utility functions.
|
|
NOTES :
|
|
*/
|
|
|
|
// ====================================================================
|
|
|
|
function getRandom(randomMin, randomMax)
|
|
{
|
|
// Returns a random whole number in a min..max range.
|
|
// NOTE: There should probably be an engine function for this,
|
|
// since we'd need to keep track of random seeds for replays.
|
|
|
|
var randomNum = randomMin + (randomMax-randomMin)*Math.random(); // num is random, from A to B
|
|
return Math.round(randomNum);
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Get list of XML files in pathname with recursion, excepting those starting with _
|
|
function getXMLFileList(pathname)
|
|
{
|
|
var files = Engine.BuildDirEntList(pathname, "*.xml", true);
|
|
|
|
var result = [];
|
|
|
|
// Get only subpath from filename and discard extension
|
|
for (var i = 0; i < files.length; ++i)
|
|
{
|
|
var file = files[i];
|
|
file = file.substring(pathname.length, file.length-4);
|
|
|
|
// Split path into directories so we can check for beginning _ character
|
|
var tokens = file.split("/");
|
|
|
|
if (tokens[tokens.length-1][0] != "_")
|
|
result.push(file);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Get list of JSON files in pathname
|
|
function getJSONFileList(pathname)
|
|
{
|
|
var files = Engine.BuildDirEntList(pathname, "*.json", false);
|
|
|
|
// Remove the path and extension from each name, since we just want the filename
|
|
files = [ n.substring(pathname.length, n.length-5) for each (n in files) ];
|
|
|
|
return files;
|
|
}
|
|
|
|
|
|
// ====================================================================
|
|
|
|
// Parse JSON data
|
|
function parseJSONData(pathname)
|
|
{
|
|
var data = {};
|
|
|
|
var rawData = Engine.ReadFile(pathname);
|
|
if (!rawData)
|
|
{
|
|
error("Failed to read file: "+pathname);
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{ // Catch nasty errors from JSON parsing
|
|
// TODO: Need more info from the parser on why it failed: line number, position, etc!
|
|
data = JSON.parse(rawData);
|
|
if (!data)
|
|
error("Failed to parse JSON data in: "+pathname+" (check for valid JSON data)");
|
|
|
|
|
|
}
|
|
catch(err)
|
|
{
|
|
error(err.toString()+": parsing JSON data in "+pathname);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// A sorting function for arrays of objects with 'name' properties, ignoring case
|
|
function sortNameIgnoreCase(x, y)
|
|
{
|
|
var lowerX = x.name.toLowerCase();
|
|
var lowerY = y.name.toLowerCase();
|
|
|
|
if (lowerX < lowerY)
|
|
return -1;
|
|
else if (lowerX > lowerY)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Escape text tags and whitespace, so users can't use special formatting in their chats
|
|
// Limit string length to 256 characters
|
|
function escapeText(text)
|
|
{
|
|
if (!text)
|
|
return text;
|
|
|
|
var out = text.replace(/[\[\]]+/g,"");
|
|
out = out.replace(/\s+/g, " ");
|
|
|
|
return out.substr(0, 255);
|
|
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
function toTitleCase (string)
|
|
{
|
|
if (!string)
|
|
return string;
|
|
|
|
// Returns the title-case version of a given string.
|
|
string = string.toString();
|
|
string = string[0].toUpperCase() + string.substring(1).toLowerCase();
|
|
|
|
return string;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Parse and return JSON data from file in simulation/data/*
|
|
// returns valid object or undefined on error
|
|
function parseJSONFromDataFile(filename)
|
|
{
|
|
var path = "simulation/data/"+filename;
|
|
var rawData = Engine.ReadFile(path);
|
|
if (!rawData)
|
|
error("Failed to read file: "+path);
|
|
|
|
try
|
|
{
|
|
// Catch nasty errors from JSON parsing
|
|
// TODO: Need more info from the parser on why it failed: line number, position, etc!
|
|
var data = JSON.parse(rawData);
|
|
return data;
|
|
}
|
|
catch(err)
|
|
{
|
|
error(err.toString()+": parsing JSON data in "+path);
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Load default player data, for when it's not otherwise specified
|
|
function initPlayerDefaults()
|
|
{
|
|
var defaults = [];
|
|
|
|
var data = parseJSONFromDataFile("player_defaults.json");
|
|
if (!data || !data.PlayerData)
|
|
error("Failed to parse player defaults in player_defaults.json (check for valid JSON data)");
|
|
else
|
|
defaults = data.PlayerData;
|
|
|
|
return defaults;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Load map size data
|
|
function initMapSizes()
|
|
{
|
|
var sizes = {
|
|
"shortNames":[],
|
|
"names":[],
|
|
"tiles": [],
|
|
"default": 0
|
|
};
|
|
|
|
var data = parseJSONFromDataFile("map_sizes.json");
|
|
if (!data || !data.Sizes)
|
|
error("Failed to parse map sizes in map_sizes.json (check for valid JSON data)");
|
|
else
|
|
{
|
|
for (var i = 0; i < data.Sizes.length; ++i)
|
|
{
|
|
sizes.shortNames.push(data.Sizes[i].Name);
|
|
sizes.names.push(data.Sizes[i].LongName);
|
|
sizes.tiles.push(data.Sizes[i].Tiles);
|
|
|
|
if (data.Sizes[i].Default)
|
|
sizes["default"] = i;
|
|
}
|
|
}
|
|
|
|
return sizes;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
// Load game speed data
|
|
function initGameSpeeds()
|
|
{
|
|
var gameSpeeds = {
|
|
"names": [],
|
|
"speeds": [],
|
|
"default": 0
|
|
};
|
|
|
|
var data = parseJSONFromDataFile("game_speeds.json");
|
|
if (!data || !data.Speeds)
|
|
error("Failed to parse game speeds in game_speeds.json (check for valid JSON data)");
|
|
else
|
|
{
|
|
for (var i = 0; i < data.Speeds.length; ++i)
|
|
{
|
|
gameSpeeds.names.push(data.Speeds[i].Name);
|
|
gameSpeeds.speeds.push(data.Speeds[i].Speed);
|
|
|
|
if (data.Speeds[i].Default)
|
|
gameSpeeds["default"] = i;
|
|
}
|
|
}
|
|
|
|
return gameSpeeds;
|
|
}
|
|
|
|
|
|
// ====================================================================
|
|
|
|
// Convert integer color values to string (for use in GUI objects)
|
|
function iColorToString(color)
|
|
{
|
|
var string = "0 0 0";
|
|
if (color && ("r" in color) && ("g" in color) && ("b" in color))
|
|
string = color.r + " " + color.g + " " + color.b;
|
|
|
|
return string;
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
/**
|
|
* Convert time in milliseconds to hh:mm:ss string representation.
|
|
* @param time Time period in milliseconds (integer)
|
|
* @return String representing time period
|
|
*/
|
|
function timeToString(time)
|
|
{
|
|
var hours = Math.floor(time / 1000 / 60 / 60);
|
|
var minutes = Math.floor(time / 1000 / 60) % 60;
|
|
var seconds = Math.floor(time / 1000) % 60;
|
|
return hours + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
|
|
}
|
|
|
|
// ====================================================================
|
|
|
|
function removeDupes(array)
|
|
{
|
|
for (var i = 0; i < array.length; i++)
|
|
{
|
|
if (array.indexOf(array[i]) < i)
|
|
{
|
|
array.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ====================================================================
|
|
// "Inside-out" implementation of Fisher-Yates shuffle
|
|
function shuffleArray(source)
|
|
{
|
|
if (!source.length)
|
|
return [];
|
|
|
|
var result = [source[0]];
|
|
for (var i = 1; i < source.length; ++i)
|
|
{
|
|
var j = Math.floor(Math.random() * i);
|
|
result[i] = result[j];
|
|
result[j] = source[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ====================================================================
|
|
// Filter out conflicting characters and limit the length of a given name.
|
|
// @param name Name to be filtered.
|
|
// @param stripUnicode Whether or not to remove unicode characters.
|
|
// @param stripSpaces Whether or not to remove whitespace.
|
|
function sanitizePlayerName(name, stripUnicode, stripSpaces)
|
|
{
|
|
// We delete the '[', ']' characters (GUI tags) and delete the ',' characters (player name separators) by default.
|
|
var sanitizedName = name.replace(/[\[\],]/g, "");
|
|
// Optionally strip unicode
|
|
if (stripUnicode)
|
|
sanitizedName = sanitizedName.replace(/[^\x20-\x7f]/g, "");
|
|
// Optionally strip whitespace
|
|
if (stripSpaces)
|
|
sanitizedName = sanitizedName.replace(/\s/g, "");
|
|
// Limit the length to 20 characters
|
|
return sanitizedName.substr(0,20);
|
|
}
|