By Richard Gibson
Has 6 other scripts.
// ==UserScript==
// @name User Script Updates
// @namespace http://userscripts.org/people/336
// @description Checks for updated versions of user scripts that request it, and notifies the user of their existence.
// @source http://userscripts.org/scripts/show/2296
// @identifier http://userscripts.org/scripts/source/2296.user.js
// @version 0.3
// @date 2006-06-20
// @creator Richard Gibson <@gmail.com>
// @include *
// ==/UserScript==
//
// **COPYRIGHT NOTICE**
//
// I, Richard Gibson, hereby establish my original authorship of this
// work, and announce its release into the public domain. I claim no
// exclusive copyrights to it, and will neither pursue myself (nor
// condone pursuit by others of) punishment, retribution, or forced
// compensation for its reproduction in any form.
//
// That being said, I would like to receive credit for this work
// whenever it, or any part thereof, is reproduced or incorporated into
// another creation; and would also like compensation whenever income is
// derived from such reproduction or inclusion. At the very least,
// please let me know if you find this work useful or enjoyable, and
// contact me with any comments or criticisms regarding it.
//
// 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.
//
// **END COPYRIGHT NOTICE**
//
//
// Changelog
// 0.3 (2006-06-20)
// Improved: notification shows "above" framesets
// 0.2 (2005-12-07)
// Fixed: removed testing code
// 0.1 (2005-12-07)
// original release
//
// To do:
// * allow users to customize the update frequency
// * allow users to manage their check list
//
// HOW TO USE USER SCRIPT UPDATES
// ------------------------------
// User Script Updates works through script objects, which are JavaScript
// objects with the following required properties:
// name: script name
// namespace: script namespace
// identifier: location of the script (.user.js)
// version: script version, in "a.b.c..." format
// date: date the script was last updated (JavaScript timestamp, i.e.
// (new Date()).getTime())
// and the following optional properties:
// description: script description
// source: script homepage
//
// Technically, only one of version and date is required, but including both
// makes version checking easier for me. If these properties seem familiar,
// that's good: name, namespace, and description are ==UserScript== metadata,
// and identifier, date, and source are defined in the Dublin Core.
//
// Note that all data in the script object should be duplicated exactly in the
// ==UserScript== metadata, with date being an exception (it should be ISO
// yyyy-mm-dd format in the metadata and a timestamp in the object). If in
// doubt, refer to this user script for an example.
//
// Once you have the script object, wait for the window to load and then pass
// it as the only argument to window.UserScriptUpdates.requestAutomaticUpdates,
// where window is the unwrapped/unsafe JavaScript-accessible window. User
// Script Updates will confirm that the user wants to check for automatic
// updates. Then, when you update your script, update the metadata and the
// script object, and place a copy of the new script at the URL pointed to by
// "identifier". User Script Updates will find it and inform the user. That's
// it! Again, refer to this user script if you are unsure of what to do.
//
// Happy coding!
//
// -----------------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts, select this script,
// and click Uninstall.
//
// -----------------------------------------------------------------------------
(function () {
// bit masks
var CHECK_START = 1;
var CHECK_NOT_FOUND = 2;
var CHECK_ERROR = 4;
var CHECK_NOT_NEWER = 8;
var CHECK_NEWER = 16;
// configurable constants
var SCRIPT = {
name: "User Script Updates",
namespace: "http://userscripts.org/people/336",
description: "Checks for updated versions of user scripts that request"
+ " it, and notifies the user of their existence.",
source: "http://userscripts.org" // script homepage/description URL
+ "/scripts/show/2296",
identifier: "http://userscripts.org" // script URL
+ "/scripts/source/2296.user.js",
version: "0.3", // version
date: (new Date(2006, 6 - 1, 20)) // update date
.valueOf()
};
var CMD_CHECK_LABEL = SCRIPT.name + ": Check Now";
var CMD_CHECK_SHOW_MASK =
CHECK_START | CHECK_NOT_FOUND | CHECK_ERROR | CHECK_NEWER;
var ADD_PROMPT_BEFORE =
"A user script has requested automatic update checks:\n\n";
var ADD_PROMPT_AFTER = "\n\nAllow?";
var CHANGE_PROMPT_BEFORE = "Change update location of user script\n\n";
var CHANGE_PROMPT_AFTER = "\n\n to ";
var CHECK_START_TEXT = "Checking for user script updates...";
var CHECK_FINISHED_TEXT =
"%newer% update(s) found, %notFound% not found, %metaError% error(s)";
var CHECK_LINK_TEXT = {};
CHECK_LINK_TEXT[CHECK_NOT_FOUND] = "NOT\xA0FOUND";
CHECK_LINK_TEXT[CHECK_ERROR] = "ERROR";
CHECK_LINK_TEXT[CHECK_NOT_NEWER] = "NOT\xA0NEWER";
CHECK_LINK_TEXT[CHECK_NEWER] = "link";
var DESCRIPTIONS_MAP = [
["name"],
["namespace"],
["description"],
["source", "homepage"],
["identifier", "location"],
["version"],
["date"]
];
var UPDATE_FREQUENCY_MS = 7 * 86400 * 1000; // 1 week
var ALERT_LINE_LENGTH = 80;
var MIN_HOVER_MS = 500;
var API_OBJECT_NAME = "UserScriptUpdates"; // object exposing update APIs
var UPDATE_NOTIFICATION_CLASS = "UserScriptUpdatesNotification";
var FRAME_MARGIN = "16px";
// non-configurable constants
var ID_RE = /[^0-9a-z_$]/ig;
var ID_RE_FN = function (ch) { return "\\u" + intToHex(ch.charCodeAt(0), 4); };
var STRING_SAVE_RE = /./g;
var STRING_SAVE_RE_FN = function (ch) { var code = ch.charCodeAt(0);
if (code < 32 || code > 126) { return ("\\u" + intToHex(code, 4)); }
else { return (BACKSLASH_ESCAPE_RE.test(ch) ? "\\" : "") + ch; } };
var BACKSLASH_ESCAPE_RE = /["'\\]/;
var KEYWORD_LITERALS = [null, undefined, true, false, NaN, Infinity];
var LINE_TERMINATORS = "\r\n\u2028\u2029";
// global variables
var scripts = {};
var updates = {
check: {pending: 0, notFound: 0, metaError: 0, notNewer: 0, newer: 0},
list: document.createElement("ol"),
label: document.createTextNode(""),
labelContainer: document.createElement("a"),
hoverTimer: null,
listMutex: 1
};
var win = unsafeWindow || window.wrappedJSObject || window;
// initialization
setupGlobals();
win[API_OBJECT_NAME] = {requestAutomaticUpdates: requestAutomaticUpdates};
try {
if (GM_registerMenuCommand
&& GM_registerMenuCommand !== win.GM_registerMenuCommand) {
GM_registerMenuCommand(CMD_CHECK_LABEL, function () {
checkAll(CMD_CHECK_SHOW_MASK);
});
}
} catch (ex) {}
try {
window.addEventListener("load", windowOnLoad, false);
} catch (ex) {}
function setupGlobals () {
loadScripts();
setStoredValue("lastGlobalUpdate",
getStoredValue("lastGlobalUpdate") || "-1");
try {
updates.labelContainer.appendChild(updates.label);
updates.labelContainer.className = UPDATE_NOTIFICATION_CLASS;
updates.labelContainer.setAttribute("href", "javascript:void(0);");
updates.list.className = UPDATE_NOTIFICATION_CLASS;
function hover (evt) {
evt = evt || window.event;
try { window.clearTimeout(updates.hoverTimer); } catch (ex) {}
addClass(updates.list, "hover");
if (evt.type == "mouseout") {
updates.hoverTimer = window.setTimeout(function () {
removeClass(updates.list, "hover");
}, MIN_HOVER_MS);
}
};
updates.labelContainer.addEventListener("click", hover, false);
updates.labelContainer.addEventListener("mouseover", hover, false);
updates.labelContainer.addEventListener("mouseout", hover, false);
updates.list.addEventListener("mouseover", hover, false);
updates.list.addEventListener("mouseout", hover, false);
} catch (ex) {}
};
function windowOnLoad () {
win[API_OBJECT_NAME].requestAutomaticUpdates(SCRIPT);
if (setupPage() && (parseInt(getStoredValue("lastGlobalUpdate"), 10) || Infinity)
< ((new Date()).valueOf() - UPDATE_FREQUENCY_MS)) {
checkAll(CHECK_NEWER);
}
};
function setupPage () {
var sel_label = "a." + UPDATE_NOTIFICATION_CLASS;
var sel_list = "ol." + UPDATE_NOTIFICATION_CLASS;
var elTop = window.top.document;
var isFrame = false;
try {
if (elTop.documentElement.nodeName.search(/(^|:)html$/i) == -1) {
throw new Error();
}
isFrame = (!elTop.body || elTop.body.nodeName.search(/(^|:)body$/i) == -1);
addGlobalStyle(""
+ sel_label + ", " + sel_list + " { display: none;"
+ " position: absolute !important; position: fixed !important;"
+ " bottom: 0 !important;"
+ " right: " + (isFrame ? FRAME_MARGIN : 0) + " !important;"
+ " margin: 0 !important;"
+ " font-family: sans-serif; cursor: default; }\n"
+ sel_label + " {"
+ " border: 2px solid white; padding: 3px;"
+ " font: bold x-small sans-serif; line-height: 150%;"
+ " text-decoration: none !important;"
+ " color: white; background-color: red; }\n"
+ sel_list + " { list-style-type: none !important;"
+ " max-width: 90% !important; max-height: 90% !important;"
+ " overflow: auto;"
+ " border: 0 !important; padding: 0 !important; }\n"
+ sel_list + " li" + " { display: table-row !important;"
+ " margin: 0 !important; border: 0px solid silver !important;"
+ " border-top-width: 1px !important; padding: 0 !important;"
+ " color: silver; background-color: maroon; }\n"
+ sel_list + " li a" + " { display: table-cell !important;"
+ " white-space: nowrap !important; padding: 2px;"
+ " color: inherit !important;"
+ " background-color: transparent !important; }\n"
+ sel_list + " li a:first-child" + " { padding-right: .3em !important;"
+ " text-decoration: none !important; }\n"
+ sel_list + ".hover" + "{ display: block; border-collapse: collapse;"
+ " }\n"
+ sel_list + " li:hover" + " {"
+ " color: white; background-color: red; }",
false,
elTop
);
elTop = (isFrame ? elTop.documentElement : elTop.body);
elTop.appendChild(updates.labelContainer);
elTop.appendChild(updates.list);
return true;
} catch (ex) { return false; }
};
function requestAutomaticUpdates (objScript) {
if (!isValidScript(objScript)) { return; }
// handle old versions of the script
try {
var oldScript = getScript(objScript.namespace, objScript.name);
if (oldScript && isNewerScript(objScript, oldScript)
&& !updateRejected(oldScript, objScript.identifier)) {
if (oldScript.identifier == objScript.identifier
|| window.confirm(CHANGE_PROMPT_BEFORE
+ scriptToAlertString(oldScript)
+ CHANGE_PROMPT_AFTER + objScript.identifier)) {
acceptUpdate(oldScript, objScript);
}
else {
rejectUpdate(oldScript, objScript.identifier);
}
}
if (oldScript) { return; } // update has been handled
} catch (ex) {}
// prompt to add this new script
if (window.confirm(ADD_PROMPT_BEFORE + scriptToAlertString(objScript)
+ ADD_PROMPT_AFTER)) {
acceptUpdate(null, objScript);
}
};
function updateRejected (objScript, url) {
var rejected = false;
try {
for (var i = objScript.rejected.length - 1; !rejected && i >= 0; i--) {
rejected = (objScript.rejected[i] == url);
}
} catch (ex) {}
return rejected;
};
function acceptUpdate (oldScript, objScript) {
try { delete scripts[oldScript.namespace][oldScript.name]; } catch (ex) {}
scripts[objScript.namespace] = scripts[objScript.namespace] || {};
scripts[objScript.namespace][objScript.name] = objScript;
saveScripts();
};
function rejectUpdate (objScript, url) {
objScript.rejected = objScript.rejected || [];
objScript.rejected.push(url);
saveScripts();
};
function checkAll (intShow) {
var temp, container = updates.labelContainer, list = updates.list;
setStoredValue("lastGlobalUpdate", "" + (new Date()).valueOf());
while (list.firstChild) { list.removeChild(list.lastChild); }
updates.label.nodeValue = CHECK_START_TEXT;
if (intShow & CHECK_START != 0) { container.style.display = "block"; }
for (var count in updates.check) { updates.check[count] = 0; }
for (var namespace in scripts) {
for (var name in scripts[namespace]) {
updates.check.pending++;
temp = wrapCall(check, this, [scripts[namespace][name], intShow]);
try { window.setTimeout(temp, 0); } catch (ex) { temp(); }
}
}
};
function check (objScript, intShow) {
//xxx if (rejected) { return; }
var request = {method: "GET", url: objScript.identifier,
onerror: function (objResponse) {
updates.check.pending--;
updates.check.notFound++;
updateList(objScript, intShow, CHECK_NOT_FOUND);
updateLabel(intShow, CHECK_NOT_FOUND);
},
onload: function (objResponse) {
var updateType, script =
textToScript(objResponse.responseText, objScript.identifier);
updates.check.pending--;
if (!isValidScript(script)) {
script = null;
updates.check.metaError++;
updateType = CHECK_ERROR;
}
else if (!isNewerScript(script, objScript)) {
updates.check.notNewer++;
updateType = CHECK_NOT_NEWER;
}
else {
updates.check.newer++;
updateType = CHECK_NEWER;
}
updateList(script || objScript, intShow, updateType);
updateLabel(intShow, updateType);
}
};
try { HTTPRequest(request); } catch (ex) { request.onerror(); }
};
function updateList (objScript, intShow, intUpdateType) {
if ((intShow & intUpdateType) == 0) { return; }
while (--updates.listMutex < 0) { ++updates.listMutex; }
try { // add the item to the list
var item = updates.list.appendChild(document.createElement("li")), temp;
item.setAttribute("title", objScript.description);
// details
temp = item.appendChild(document.createElement("a"));
temp.setAttribute("href", objScript.source || objScript.identifier);
temp.appendChild(document.createElement("strong"))
.appendChild(document.createTextNode(objScript.name));
temp.appendChild(document.createTextNode(" " + objScript.version));
if (objScript.date) { try {
temp.appendChild(document.createElement("em"))
.appendChild(document.createTextNode(""
+ (new Date(objScript.date)).getFullYear() + "-"
+ padString((new Date(objScript.date)).getMonth() + 1, 2, 0)
+ "-" + padString((new Date(objScript.date)).getDate(), 2, 0)
));
temp.insertBefore(document.createElement("br"), temp.lastChild);
} catch (ex) {} }
// script link/error message
temp = item.appendChild(document.createElement("a"));
if ((intUpdateType & CHECK_NEWER) == 0) {
temp.style.fontWeight = "bold";
}
temp.setAttribute("href", objScript.identifier);
temp.appendChild(document.createTextNode(CHECK_LINK_TEXT[intUpdateType]));
// alphabetize
temp = (item.previousSibling
&& item.previousSibling.firstChild.firstChild.firstChild.nodeValue);
while (temp !== null && stringCompare(temp, objScript.name) > 0) {
item.parentNode.insertBefore(item, item.previousSibling);
temp = (item.previousSibling
&& item.previousSibling.firstChild.firstChild.firstChild
.nodeValue);
}
} catch (ex) {}
updates.listMutex++;
};
function updateLabel (intShow, intUpdateType, blnIsFinalCall) {
var text = (updates.check.pending > 0 ? CHECK_START_TEXT + " " : "")
+ CHECK_FINISHED_TEXT;
for (var count in updates.check) {
text = text.replace("%" + count + "%", updates.check[count]);
}
updates.label.nodeValue = text;
if ((intShow & intUpdateType) != 0) {
updates.labelContainer.style.display = "block";
}
if (updates.check.pending == 0 && !blnIsFinalCall) {
try { window.setTimeout(function () {
updateLabel(intShow, intUpdateType, true);
}, 50); } catch (ex) {}
}
};
function loadScripts () {
var json = ("" + getStoredValue("scripts", "")).split("\t");
for (var script, i = 0; i < json.length; i++) {
script = fromSafeJSON(json[i]);
if (isValidScript(script)) {
scripts[script.namespace] = scripts[script.namespace] || {};
scripts[script.namespace][script.name] = script;
}
}
};
function saveScripts (arrScripts) {
if (!arrScripts) { arrScripts = scripts; }
var json = "";
for (var namespace in arrScripts) {
for (var name in arrScripts[namespace]) {
json += toSafeJSON(arrScripts[namespace][name]) + "\t";
}
}
setStoredValue("scripts", json.slice(0, -1));
};
function isValidScript (objScript) {
return (objScript
&& ("namespace" in objScript) && ("name" in objScript)
&& ("identifier" in objScript)
&& (("version" in objScript) || ("date" in objScript))
);
};
function isNewerScript (objNew, objOld) {
try {
return (
(objNew.date > objOld.date)
|| isNewerVersion(objNew.version, objOld.version)
? true
: false
);
}
catch (ex) {
return null;
}
};
function isNewerVersion (strNew, strOld) {
var newer = false, arrNew = [], arrOld = [], tmpNew, tmpOld, cmp;
try {
arrNew[0] = (strNew + "").split("-");
arrOld[0] = (strOld + "").split("-");
for (var i = 0; !newer && i < arrNew[0].length; i++) {
arrNew[1] = arrNew[0][i].split(".");
arrOld[1] = (arrOld[0][i] || "").split(".");
for (var j = 0; !newer && j < arrNew[1].length; j++) {
tmpNew = parseInt(arrNew[1][j], 10);
tmpOld = parseInt(arrOld[1][j], 10);
cmp = (isNaN(tmpNew)
// new piece does not start with a number
? (isNaN(tmpOld)
? stringCompare(arrNew[1][j], arrOld[1][j] || "") : -1)
// new piece starts with a number
: (isNaN(tmpOld) ? 1 : numberCompare(tmpNew, tmpOld))
);
// go down to string compare if number compare matched
if (!isNaN(tmpNew) && !isNaN(tmpOld) && cmp == 0) {
cmp = stringCompare(arrNew[1][j], arrOld[1][j]);
}
// error if tmpNew < tmpOlder, otherwise update newer
if (cmp < 0) { throw {}; }
newer = (cmp > 0);
}
}
}
catch (ex) {
newer = null;
}
return newer;
};
function getScript (strNamespace, strName) {
try {
return (scripts[strNamespace][strName] || null);
}
catch (ex) {
return null;
}
};
function textToScript (str, url) {
var script = {}, lineIndex = 0, line, foundMeta = false, done = false, match;
lines = str.replace("\r", "\n").split(/\n+/g);
do {
line = lines[lineIndex++];
foundMeta = foundMeta || (line.indexOf("// ==UserScript==") == 0);
done = foundMeta && (line.indexOf("// ==/UserScript==") == 0);
if (foundMeta) {
match = line.match(/\/\/ \@(\S+)\s+(.*)/);
if (match != null) {
if (match[1] == "date") {
match[2] = match[2].split("-"); // yyyy-mm-dd to array
match[2] = (new Date(match[2][0] || 0,
(parseInt(match[2][1]) || 1) - 1, match[2][2] || 1))
.valueOf();
}
if (match[1] in SCRIPT) { script[match[1]] = match[2]; }
}
}
} while (!done && lineIndex < lines.length);
if (!script.namespace) {
match = url.match(/.*?\:([\/]*)([^\/\\]*)/);
if (match != null && match[2]) { script.namespace = match[2]; }
}
if (!script.name) {
script.name = url;
script.name = script.name.substring(0, script.name.indexOf(".user.js"));
script.name = script.name.substring(script.name.lastIndexOf("/") + 1);
}
return script;
};
function scriptToAlertString (objScript) {
var arrText = [], maxLabel = 0, indent = "\xA0\xA0\xA0\xA0\xA0\xA0"; // nbsp
for (var i = DESCRIPTIONS_MAP.length - 1; i >= 0; i--) {
try {
arrText.unshift([
(DESCRIPTIONS_MAP[i][1] || DESCRIPTIONS_MAP[i][0]) + ": ",
objScript[DESCRIPTIONS_MAP[i][0]] || ""
]);
if (DESCRIPTIONS_MAP[i][0] == "date") { try {
arrText[0][1] = (new Date(arrText[0][1])).toDateString();
} catch (ex) {} }
maxLabel = Math.max(maxLabel, arrText[0][0].length);
} catch (ex) {}
}
for (var text, line, lastSpace, i = arrText.length - 1; i >= 0; i--) {
text = arrText[i];
text = [text[0] + ("" + text[1]).replace(/\s+/, " ")];
while (text[text.length - 1].length > ALERT_LINE_LENGTH) {
line = text[text.length - 1];
if (text.length < 5) {
lastSpace = line.lastIndexOf(" ", ALERT_LINE_LENGTH);
if (lastSpace < (maxLabel + 1)) { lastSpace = -1; }
text[text.length - 1] = line.substring(0,
(lastSpace == -1 ? ALERT_LINE_LENGTH : lastSpace));
text[text.length] =
indent + line.substring((lastSpace + 1) || ALERT_LINE_LENGTH);
}
else {
text[text.length - 1] = line.substring(0, ALERT_LINE_LENGTH - 3)
+ "...";
}
}
arrText[i] = text.join("\n");
}
return arrText.join("\n");
};
function getStoredValue (strName, varDefault) {
try {
return (GM_getValue && GM_getValue !== win.GM_getValue
? GM_getValue(strName, varDefault)
: varDefault
);
}
catch (ex) {
return varDefault;
}
};
function setStoredValue (strName, varValue) {
try {
if (GM_setValue && GM_setValue !== win.GM_setValue) {
GM_setValue(strName, varValue);
}
} catch (ex) {}
return varValue;
};
function addGlobalStyle (strCSS, blnAtFront, objDoc) {
objDoc = objDoc || document;
try {
var head = objDoc.getElementsByTagName("head")[0];
var style = head.insertBefore(objDoc.createElement("style"),
blnAtFront ? head.firstChild : null);
style.setAttribute("type", "text/css");
style.appendChild(document.createTextNode(strCSS));
} catch (ex) {}
};
function addClass (el, strClass) {
removeClass(el, strClass);
el.className += " " + strClass;
};
function removeClass (el, strClass) {
try {
var re = new RegExp("(^|\\s)" + strClass.replace(ID_RE, ID_RE_FN)
+ "(\\s|$)", "g");
el.className = el.className.replace(re, " ");
} catch (ex) {
try {
var classes = el.className.split(/\s+/g), newClasses = "";
for (var i = 0; i < classes.length; i++) {
if (classes[i] != strClass) { newClasses += classes[i] + " "; }
}
el.className = newClasses.slice(0, -1);
} catch (ex) {}
}
};
function HTTPRequest (objDetails) { // Greasemonkey-compatible XMLHttpRequest
if (GM_xmlhttpRequest && GM_xmlhttpRequest !== win.GM_xmlhttpRequest) {
return GM_xmlhttpRequest(objDetails);
}
var request = null;
/*@cc_on @*/ // JScript (Internet Explorer) conditional compilation
/*@if (@_jscript_version >= 5)
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (ex) {
try { request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (ex) {}
}
@end @*/
try { request = request || new XMLHttpRequest(); } catch (ex) {}
try {
request.open(objDetails.method, objDetails.url, true);
try { for (var h in objDetails.headers) {
try { request.setRequestHeader(h, objDetails.headers[h]); }catch(ex){}
} } catch (ex) {}
request.onreadystatechange = function () {
var temp, ready = (request.readyState == 4), response = {
readyState: request.readyState,
responseText: request.responseText,
status: (ready ? request.status : 0),
statusText: (ready ? request.statusText : ''),
responseHeaders: (ready ? request.getAllResponseHeaders() : '')
};
temp = function () { try {
objDetails.onreadystatechange.call(request, response);
} catch (ex) {} };
try { window.setTimeout(temp, 0); } catch (ex) { temp(); }
if (ready) {
temp = (request.status == 200
? function () { try { objDetails.onload.call(request,
response); } catch (ex) {} }
: function () { try { objDetails.onerror.call(request,
response); } catch (ex) {} }
);
try { window.setTimeout(temp, 0); } catch (ex) { temp(); }
}
};
request.send(objDetails.data || null);
} catch (ex) {}
};
// "safe" JSON conversion (preserves only primitives, basic Objects, and Arrays)
function toSafeJSON (val) {
var json = "";
if (val && (val instanceof Boolean || val instanceof Number
|| val instanceof String)) {
json = toSafeJSON(val.valueOf());
}
else {
switch (typeof(val)) {
case "undefined":
case "boolean":
case "number":
json = "" + val;
break;
case "string":
json = '"' + val.replace(STRING_SAVE_RE, STRING_SAVE_RE_FN) + '"';
break;
default: // function or object
if (!val) {
json = "null";
}
else if (val instanceof Array) {
for (var i = 0; i < val.length; i++) {
json += toSafeJSON(val[i]) + ",";
}
json = "[" + json.slice(0, -1) + "]";
}
else {
for (var p in val) {
json += p.replace(ID_RE, ID_RE_FN) + ":" + toSafeJSON(val[p])
+ ",";
}
json = "{" + json.slice(0, -1) + "}";
}
}
}
return json;
};
function fromSafeJSON (strJSON) {
var progress = {index: 0, isError: false},
obj = fromSafeJSON_getValue(strJSON, progress);
return (progress.isError ? null : obj);
};
function fromSafeJSON_getValue (strJSON, objProgress) {
var getValue = fromSafeJSON_getValue, getProperty = fromSafeJSON_getProperty;
var obj = null, currentChar = strJSON.charAt(objProgress.index++), temp;
if (currentChar == "{") { // object
obj = {};
if (strJSON.charAt(objProgress.index) == "}") objProgress.index += 1;
else {
temp = getProperty(strJSON, objProgress);
while (!objProgress.isError && !temp.isRightBrace) {
try { obj[temp.name] = temp.value; } catch (ex) {}
switch (strJSON.charAt(objProgress.index++)) {
case "}": temp.isRightBrace = true; break;
case ",": temp = getProperty(strJSON, objProgress); break;
default: objProgress.isError = true;
}
}
}
}
else if (currentChar == "[") { // array
obj = [];
if (strJSON.charAt(objProgress.index) == "]") objProgress.index += 1;
else {
temp = {value: getValue(strJSON, objProgress)};
while (!objProgress.isError && !temp.isRightBracket) {
try { obj.push(temp.value); } catch (ex) {}
switch (strJSON.charAt(objProgress.index++)) {
case "]": temp.isRightBracket = true; break;
case ",": temp.value = getValue(strJSON, objProgress); break;
default: objProgress.isError = true;
}
}
}
}
else if (currentChar == "'" || currentChar == '"') { // string
obj = "";
temp = {value: strJSON.charAt(objProgress.index++)};
temp.isClosingQuote = (temp.value == currentChar);
if (LINE_TERMINATORS.indexOf(temp.value) >= 0) objProgress.isError = true;
while (!objProgress.isError && !temp.isClosingQuote) {
if (temp.value == "\\") {
temp.value = strJSON.charAt(objProgress.index++);
if (temp.value == "b") { temp.value = "\b"; }
else if (temp.value == "t") { temp.value = "\t"; }
else if (temp.value == "n") { temp.value = "\n"; }
else if (temp.value == "v") { temp.value = "\v"; }
else if (temp.value == "f") { temp.value = "\f"; }
else if (temp.value == "r") { temp.value = "\r"; }
else if (temp.value == "x" || temp.value == "u") {
temp.digits = (temp.value == "x" ? 2 : 4);
temp.value = strJSON.substring(objProgress.index,
objProgress.index + temp.digits);
objProgress.index += temp.digits;
if (temp.value.length != temp.digits
|| temp.value.search(/^[0-9a-f]+$/i) == -1) {
objProgress.isError = true;
}
else {
temp.value = String.fromCharCode(eval("0x" + temp.value));
}
}
else if (LINE_TERMINATORS.indexOf(temp.value) >= 0) {
objProgress.isError = true;
}
}
obj += temp.value;
temp = {value: strJSON.charAt(objProgress.index++)};
temp.isClosingQuote = (temp.value == currentChar);
if (LINE_TERMINATORS.indexOf(temp.value) >= 0) {
objProgress.isError = true;
}
}
}
else if (currentChar.search(/[0-9.+-]/) != -1) { // NaN, Infinity, <number>
objProgress.isError = true;
temp = {index: objProgress.index - (currentChar.search(/[+-]/) != -1 ? 0 : 1)};
for (var literals = [NaN, Infinity], str, i = 0; i < literals.length; i++) {
str = "" + literals[i];
if (strJSON.substring(temp.index, temp.index + str.length) == str) {
obj = literals[i] * (currentChar == "-" ? -1 : 1);
objProgress.index = temp.index + str.length;
objProgress.isError = false;
}
}
if (objProgress.isError) { // must read a non-exponent decimal <number>
obj = currentChar;
temp.dotFound = (currentChar == ".");
temp.value = strJSON.charAt(objProgress.index);
while (temp.value.search(temp.dotFound ? /[0-9]/ : /[0-9.]/) != -1) {
obj += temp.value;
temp.dotFound = temp.dotFound || (temp.value == ".");
temp.value = strJSON.charAt(++objProgress.index);
}
obj = parseFloat(obj);
if (!isNaN(obj)) objProgress.isError = false;
}
}
else {
objProgress.isError = true;
for (var str, i = KEYWORD_LITERALS.length - 1; i >= 0; i--) {
str = "" + KEYWORD_LITERALS[i];
if (strJSON.substring(objProgress.index - 1, objProgress.index
+ str.length - 1) == str) {
obj = KEYWORD_LITERALS[i];
objProgress.isError = false;
}
}
}
return obj;
};
function fromSafeJSON_getProperty (strJSON, objProgress) {
var getValue = fromSafeJSON_getValue,
property = {name: "", value: undefined},
currentChar = strJSON.charAt(objProgress.index++);
if (currentChar == ":") objProgress.isError = true;
while (!objProgress.isError && currentChar != ":") {
if (currentChar == "\\") {
objProgress.isError = true;
if (strJSON.substring(objProgress.index, objProgress.index + 5)
.search(/^u[0-9a-fA-F]{4}$/) != -1) {
currentChar = String.fromCharCode(eval("0x"
+ strJSON.substring(objProgress.index + 1,
objProgress.index + 5)));
objProgress.index += 5;
objProgress.isError = false;
}
}
property.name += currentChar;
currentChar = strJSON.charAt(objProgress.index++);
if (currentChar == "") objProgress.isError = true;
}
if (!objProgress.isError) property.value = getValue(strJSON, objProgress);
return property;
};
function wrapCall (fn, objThis, arrArgs) {
return (function () { fn.apply(objThis, arrArgs); });
};
function intToHex (intNonNegative, intMinLength) {
var strHex = "", place = 0, placeValue = 16, digit;
while (intNonNegative >= placeValue) {
place += 1;
placeValue *= 16;
}
while (place >= 0) {
placeValue /= 16;
digit = Math.floor(intNonNegative / placeValue);
intNonNegative -= placeValue * digit;
if (digit > 9) { digit = String.fromCharCode(55 + digit); } // A-F
strHex += digit;
place -= 1;
}
while (strHex.length < intMinLength) { strHex = "0" + strHex; }
return strHex;
};
function padString (str, intWidth, chPad, blnAtRight) {
var rv = "" + str, ch = (arguments.length > 2 ? chPad + " " : " ").charAt(0),
add = "";
for (var i = intWidth - rv.length; i > 0; i--) add += ch;
return (blnAtRight ? rv + add : add + rv);
};
function stringCompare (A, B) {
var result = 0, a, b;
try {
try { a = A.toLocaleLowerCase(); } catch (ex) { a = A.toLowerCase(); };
try { b = B.toLocaleLowerCase(); } catch (ex) { b = B.toLowerCase(); };
try {
result = a.localeCompare(b);
if (result == 0) result = A.localeCompare(B);
}
catch (ex) {
result = (a < b ? -1 : (a > b ? 1 : (A < B ? -1 : (A > B ? 1 : 0))));
}
} catch (ex) {}
return result;
};
function numberCompare (a, b) {
return (a < b ? -1 : (a > b ? 1 : 0));
};
})();