| Index: chrome_frame/tools/test/reference_build/chrome/resources/inspector/utilities.js
|
| ===================================================================
|
| --- chrome_frame/tools/test/reference_build/chrome/resources/inspector/utilities.js (revision 0)
|
| +++ chrome_frame/tools/test/reference_build/chrome/resources/inspector/utilities.js (revision 0)
|
| @@ -0,0 +1,905 @@
|
| +/*
|
| + * Copyright (C) 2007 Apple Inc. All rights reserved.
|
| + *
|
| + * Redistribution and use in source and binary forms, with or without
|
| + * modification, are permitted provided that the following conditions
|
| + * are met:
|
| + *
|
| + * 1. Redistributions of source code must retain the above copyright
|
| + * notice, this list of conditions and the following disclaimer.
|
| + * 2. Redistributions in binary form must reproduce the above copyright
|
| + * notice, this list of conditions and the following disclaimer in the
|
| + * documentation and/or other materials provided with the distribution.
|
| + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
| + * its contributors may be used to endorse or promote products derived
|
| + * from this software without specific prior written permission.
|
| + *
|
| + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
| + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| + */
|
| +
|
| +Object.proxyType = function(objectProxy)
|
| +{
|
| + if (objectProxy === null)
|
| + return "null";
|
| +
|
| + var type = typeof objectProxy;
|
| + if (type !== "object" && type !== "function")
|
| + return type;
|
| +
|
| + return objectProxy.type;
|
| +}
|
| +
|
| +Object.properties = function(obj)
|
| +{
|
| + var properties = [];
|
| + for (var prop in obj)
|
| + properties.push(prop);
|
| + return properties;
|
| +}
|
| +
|
| +Object.sortedProperties = function(obj, sortFunc)
|
| +{
|
| + return Object.properties(obj).sort(sortFunc);
|
| +}
|
| +
|
| +Function.prototype.bind = function(thisObject)
|
| +{
|
| + var func = this;
|
| + var args = Array.prototype.slice.call(arguments, 1);
|
| + return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))) };
|
| +}
|
| +
|
| +Node.prototype.rangeOfWord = function(offset, stopCharacters, stayWithinNode, direction)
|
| +{
|
| + var startNode;
|
| + var startOffset = 0;
|
| + var endNode;
|
| + var endOffset = 0;
|
| +
|
| + if (!stayWithinNode)
|
| + stayWithinNode = this;
|
| +
|
| + if (!direction || direction === "backward" || direction === "both") {
|
| + var node = this;
|
| + while (node) {
|
| + if (node === stayWithinNode) {
|
| + if (!startNode)
|
| + startNode = stayWithinNode;
|
| + break;
|
| + }
|
| +
|
| + if (node.nodeType === Node.TEXT_NODE) {
|
| + var start = (node === this ? (offset - 1) : (node.nodeValue.length - 1));
|
| + for (var i = start; i >= 0; --i) {
|
| + if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
|
| + startNode = node;
|
| + startOffset = i + 1;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (startNode)
|
| + break;
|
| +
|
| + node = node.traversePreviousNode(false, stayWithinNode);
|
| + }
|
| +
|
| + if (!startNode) {
|
| + startNode = stayWithinNode;
|
| + startOffset = 0;
|
| + }
|
| + } else {
|
| + startNode = this;
|
| + startOffset = offset;
|
| + }
|
| +
|
| + if (!direction || direction === "forward" || direction === "both") {
|
| + node = this;
|
| + while (node) {
|
| + if (node === stayWithinNode) {
|
| + if (!endNode)
|
| + endNode = stayWithinNode;
|
| + break;
|
| + }
|
| +
|
| + if (node.nodeType === Node.TEXT_NODE) {
|
| + var start = (node === this ? offset : 0);
|
| + for (var i = start; i < node.nodeValue.length; ++i) {
|
| + if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
|
| + endNode = node;
|
| + endOffset = i;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (endNode)
|
| + break;
|
| +
|
| + node = node.traverseNextNode(false, stayWithinNode);
|
| + }
|
| +
|
| + if (!endNode) {
|
| + endNode = stayWithinNode;
|
| + endOffset = stayWithinNode.nodeType === Node.TEXT_NODE ? stayWithinNode.nodeValue.length : stayWithinNode.childNodes.length;
|
| + }
|
| + } else {
|
| + endNode = this;
|
| + endOffset = offset;
|
| + }
|
| +
|
| + var result = this.ownerDocument.createRange();
|
| + result.setStart(startNode, startOffset);
|
| + result.setEnd(endNode, endOffset);
|
| +
|
| + return result;
|
| +}
|
| +
|
| +Element.prototype.removeStyleClass = function(className)
|
| +{
|
| + // Test for the simple case before using a RegExp.
|
| + if (this.className === className) {
|
| + this.className = "";
|
| + return;
|
| + }
|
| +
|
| + this.removeMatchingStyleClasses(className.escapeForRegExp());
|
| +}
|
| +
|
| +Element.prototype.removeMatchingStyleClasses = function(classNameRegex)
|
| +{
|
| + var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
|
| + if (regex.test(this.className))
|
| + this.className = this.className.replace(regex, " ");
|
| +}
|
| +
|
| +Element.prototype.addStyleClass = function(className)
|
| +{
|
| + if (className && !this.hasStyleClass(className))
|
| + this.className += (this.className.length ? " " + className : className);
|
| +}
|
| +
|
| +Element.prototype.hasStyleClass = function(className)
|
| +{
|
| + if (!className)
|
| + return false;
|
| + // Test for the simple case before using a RegExp.
|
| + if (this.className === className)
|
| + return true;
|
| + var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)");
|
| + return regex.test(this.className);
|
| +}
|
| +
|
| +Element.prototype.positionAt = function(x, y)
|
| +{
|
| + this.style.left = x + "px";
|
| + this.style.top = y + "px";
|
| +}
|
| +
|
| +Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray)
|
| +{
|
| + for (var node = this; node && node !== this.ownerDocument; node = node.parentNode)
|
| + for (var i = 0; i < nameArray.length; ++i)
|
| + if (node.nodeName.toLowerCase() === nameArray[i].toLowerCase())
|
| + return node;
|
| + return null;
|
| +}
|
| +
|
| +Node.prototype.enclosingNodeOrSelfWithNodeName = function(nodeName)
|
| +{
|
| + return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
|
| +}
|
| +
|
| +Node.prototype.enclosingNodeOrSelfWithClass = function(className)
|
| +{
|
| + for (var node = this; node && node !== this.ownerDocument; node = node.parentNode)
|
| + if (node.nodeType === Node.ELEMENT_NODE && node.hasStyleClass(className))
|
| + return node;
|
| + return null;
|
| +}
|
| +
|
| +Node.prototype.enclosingNodeWithClass = function(className)
|
| +{
|
| + if (!this.parentNode)
|
| + return null;
|
| + return this.parentNode.enclosingNodeOrSelfWithClass(className);
|
| +}
|
| +
|
| +Element.prototype.query = function(query)
|
| +{
|
| + return this.ownerDocument.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
| +}
|
| +
|
| +Element.prototype.removeChildren = function()
|
| +{
|
| + while (this.firstChild)
|
| + this.removeChild(this.firstChild);
|
| +}
|
| +
|
| +Element.prototype.isInsertionCaretInside = function()
|
| +{
|
| + var selection = window.getSelection();
|
| + if (!selection.rangeCount || !selection.isCollapsed)
|
| + return false;
|
| + var selectionRange = selection.getRangeAt(0);
|
| + return selectionRange.startContainer === this || selectionRange.startContainer.isDescendant(this);
|
| +}
|
| +
|
| +Element.prototype.__defineGetter__("totalOffsetLeft", function()
|
| +{
|
| + var total = 0;
|
| + for (var element = this; element; element = element.offsetParent)
|
| + total += element.offsetLeft;
|
| + return total;
|
| +});
|
| +
|
| +Element.prototype.__defineGetter__("totalOffsetTop", function()
|
| +{
|
| + var total = 0;
|
| + for (var element = this; element; element = element.offsetParent)
|
| + total += element.offsetTop;
|
| + return total;
|
| +});
|
| +
|
| +Element.prototype.offsetRelativeToWindow = function(targetWindow)
|
| +{
|
| + var elementOffset = {x: 0, y: 0};
|
| + var curElement = this;
|
| + var curWindow = this.ownerDocument.defaultView;
|
| + while (curWindow && curElement) {
|
| + elementOffset.x += curElement.totalOffsetLeft;
|
| + elementOffset.y += curElement.totalOffsetTop;
|
| + if (curWindow === targetWindow)
|
| + break;
|
| +
|
| + curElement = curWindow.frameElement;
|
| + curWindow = curWindow.parent;
|
| + }
|
| +
|
| + return elementOffset;
|
| +}
|
| +
|
| +Element.prototype.firstChildSkippingWhitespace = firstChildSkippingWhitespace;
|
| +Element.prototype.lastChildSkippingWhitespace = lastChildSkippingWhitespace;
|
| +
|
| +Node.prototype.isWhitespace = isNodeWhitespace;
|
| +Node.prototype.displayName = nodeDisplayName;
|
| +Node.prototype.isAncestor = function(node)
|
| +{
|
| + return isAncestorNode(this, node);
|
| +};
|
| +Node.prototype.isDescendant = isDescendantNode;
|
| +Node.prototype.nextSiblingSkippingWhitespace = nextSiblingSkippingWhitespace;
|
| +Node.prototype.previousSiblingSkippingWhitespace = previousSiblingSkippingWhitespace;
|
| +Node.prototype.traverseNextNode = traverseNextNode;
|
| +Node.prototype.traversePreviousNode = traversePreviousNode;
|
| +Node.prototype.onlyTextChild = onlyTextChild;
|
| +
|
| +String.prototype.hasSubstring = function(string, caseInsensitive)
|
| +{
|
| + if (!caseInsensitive)
|
| + return this.indexOf(string) !== -1;
|
| + return this.match(new RegExp(string.escapeForRegExp(), "i"));
|
| +}
|
| +
|
| +String.prototype.escapeCharacters = function(chars)
|
| +{
|
| + var foundChar = false;
|
| + for (var i = 0; i < chars.length; ++i) {
|
| + if (this.indexOf(chars.charAt(i)) !== -1) {
|
| + foundChar = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (!foundChar)
|
| + return this;
|
| +
|
| + var result = "";
|
| + for (var i = 0; i < this.length; ++i) {
|
| + if (chars.indexOf(this.charAt(i)) !== -1)
|
| + result += "\\";
|
| + result += this.charAt(i);
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +String.prototype.escapeForRegExp = function()
|
| +{
|
| + return this.escapeCharacters("^[]{}()\\.$*+?|");
|
| +}
|
| +
|
| +String.prototype.escapeHTML = function()
|
| +{
|
| + return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
| +}
|
| +
|
| +String.prototype.collapseWhitespace = function()
|
| +{
|
| + return this.replace(/[\s\xA0]+/g, " ");
|
| +}
|
| +
|
| +String.prototype.trimLeadingWhitespace = function()
|
| +{
|
| + return this.replace(/^[\s\xA0]+/g, "");
|
| +}
|
| +
|
| +String.prototype.trimTrailingWhitespace = function()
|
| +{
|
| + return this.replace(/[\s\xA0]+$/g, "");
|
| +}
|
| +
|
| +String.prototype.trimWhitespace = function()
|
| +{
|
| + return this.replace(/^[\s\xA0]+|[\s\xA0]+$/g, "");
|
| +}
|
| +
|
| +String.prototype.trimURL = function(baseURLDomain)
|
| +{
|
| + var result = this.replace(new RegExp("^http[s]?:\/\/", "i"), "");
|
| + if (baseURLDomain)
|
| + result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
|
| + return result;
|
| +}
|
| +
|
| +function isNodeWhitespace()
|
| +{
|
| + if (!this || this.nodeType !== Node.TEXT_NODE)
|
| + return false;
|
| + if (!this.nodeValue.length)
|
| + return true;
|
| + return this.nodeValue.match(/^[\s\xA0]+$/);
|
| +}
|
| +
|
| +function nodeDisplayName()
|
| +{
|
| + if (!this)
|
| + return "";
|
| +
|
| + switch (this.nodeType) {
|
| + case Node.DOCUMENT_NODE:
|
| + return "Document";
|
| +
|
| + case Node.ELEMENT_NODE:
|
| + var name = "<" + this.nodeName.toLowerCase();
|
| +
|
| + if (this.hasAttributes()) {
|
| + var value = this.getAttribute("id");
|
| + if (value)
|
| + name += " id=\"" + value + "\"";
|
| + value = this.getAttribute("class");
|
| + if (value)
|
| + name += " class=\"" + value + "\"";
|
| + if (this.nodeName.toLowerCase() === "a") {
|
| + value = this.getAttribute("name");
|
| + if (value)
|
| + name += " name=\"" + value + "\"";
|
| + value = this.getAttribute("href");
|
| + if (value)
|
| + name += " href=\"" + value + "\"";
|
| + } else if (this.nodeName.toLowerCase() === "img") {
|
| + value = this.getAttribute("src");
|
| + if (value)
|
| + name += " src=\"" + value + "\"";
|
| + } else if (this.nodeName.toLowerCase() === "iframe") {
|
| + value = this.getAttribute("src");
|
| + if (value)
|
| + name += " src=\"" + value + "\"";
|
| + } else if (this.nodeName.toLowerCase() === "input") {
|
| + value = this.getAttribute("name");
|
| + if (value)
|
| + name += " name=\"" + value + "\"";
|
| + value = this.getAttribute("type");
|
| + if (value)
|
| + name += " type=\"" + value + "\"";
|
| + } else if (this.nodeName.toLowerCase() === "form") {
|
| + value = this.getAttribute("action");
|
| + if (value)
|
| + name += " action=\"" + value + "\"";
|
| + }
|
| + }
|
| +
|
| + return name + ">";
|
| +
|
| + case Node.TEXT_NODE:
|
| + if (isNodeWhitespace.call(this))
|
| + return "(whitespace)";
|
| + return "\"" + this.nodeValue + "\"";
|
| +
|
| + case Node.COMMENT_NODE:
|
| + return "<!--" + this.nodeValue + "-->";
|
| +
|
| + case Node.DOCUMENT_TYPE_NODE:
|
| + var docType = "<!DOCTYPE " + this.nodeName;
|
| + if (this.publicId) {
|
| + docType += " PUBLIC \"" + this.publicId + "\"";
|
| + if (this.systemId)
|
| + docType += " \"" + this.systemId + "\"";
|
| + } else if (this.systemId)
|
| + docType += " SYSTEM \"" + this.systemId + "\"";
|
| + if (this.internalSubset)
|
| + docType += " [" + this.internalSubset + "]";
|
| + return docType + ">";
|
| + }
|
| +
|
| + return this.nodeName.toLowerCase().collapseWhitespace();
|
| +}
|
| +
|
| +function isAncestorNode(ancestor, node)
|
| +{
|
| + if (!node || !ancestor)
|
| + return false;
|
| +
|
| + var currentNode = node.parentNode;
|
| + while (currentNode) {
|
| + if (ancestor === currentNode)
|
| + return true;
|
| + currentNode = currentNode.parentNode;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +function isDescendantNode(descendant)
|
| +{
|
| + return isAncestorNode(descendant, this);
|
| +}
|
| +
|
| +function nextSiblingSkippingWhitespace()
|
| +{
|
| + if (!this)
|
| + return;
|
| + var node = this.nextSibling;
|
| + while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
|
| + node = node.nextSibling;
|
| + return node;
|
| +}
|
| +
|
| +function previousSiblingSkippingWhitespace()
|
| +{
|
| + if (!this)
|
| + return;
|
| + var node = this.previousSibling;
|
| + while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
|
| + node = node.previousSibling;
|
| + return node;
|
| +}
|
| +
|
| +function firstChildSkippingWhitespace()
|
| +{
|
| + if (!this)
|
| + return;
|
| + var node = this.firstChild;
|
| + while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
|
| + node = nextSiblingSkippingWhitespace.call(node);
|
| + return node;
|
| +}
|
| +
|
| +function lastChildSkippingWhitespace()
|
| +{
|
| + if (!this)
|
| + return;
|
| + var node = this.lastChild;
|
| + while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
|
| + node = previousSiblingSkippingWhitespace.call(node);
|
| + return node;
|
| +}
|
| +
|
| +function traverseNextNode(skipWhitespace, stayWithin)
|
| +{
|
| + if (!this)
|
| + return;
|
| +
|
| + var node = skipWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild;
|
| + if (node)
|
| + return node;
|
| +
|
| + if (stayWithin && this === stayWithin)
|
| + return null;
|
| +
|
| + node = skipWhitespace ? nextSiblingSkippingWhitespace.call(this) : this.nextSibling;
|
| + if (node)
|
| + return node;
|
| +
|
| + node = this;
|
| + while (node && !(skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling) && (!stayWithin || !node.parentNode || node.parentNode !== stayWithin))
|
| + node = node.parentNode;
|
| + if (!node)
|
| + return null;
|
| +
|
| + return skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling;
|
| +}
|
| +
|
| +function traversePreviousNode(skipWhitespace, stayWithin)
|
| +{
|
| + if (!this)
|
| + return;
|
| + if (stayWithin && this === stayWithin)
|
| + return null;
|
| + var node = skipWhitespace ? previousSiblingSkippingWhitespace.call(this) : this.previousSibling;
|
| + while (node && (skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild) )
|
| + node = skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild;
|
| + if (node)
|
| + return node;
|
| + return this.parentNode;
|
| +}
|
| +
|
| +function onlyTextChild(ignoreWhitespace)
|
| +{
|
| + if (!this)
|
| + return null;
|
| +
|
| + var firstChild = ignoreWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild;
|
| + if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE)
|
| + return null;
|
| +
|
| + var sibling = ignoreWhitespace ? nextSiblingSkippingWhitespace.call(firstChild) : firstChild.nextSibling;
|
| + return sibling ? null : firstChild;
|
| +}
|
| +
|
| +function nodeTitleInfo(hasChildren, linkify)
|
| +{
|
| + var info = {title: "", hasChildren: hasChildren};
|
| +
|
| + switch (this.nodeType) {
|
| + case Node.DOCUMENT_NODE:
|
| + info.title = "Document";
|
| + break;
|
| +
|
| + case Node.ELEMENT_NODE:
|
| + info.title = "<span class=\"webkit-html-tag\"><" + this.nodeName.toLowerCase().escapeHTML();
|
| +
|
| + if (this.hasAttributes()) {
|
| + for (var i = 0; i < this.attributes.length; ++i) {
|
| + var attr = this.attributes[i];
|
| + info.title += " <span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=​\"";
|
| +
|
| + var value = attr.value;
|
| + if (linkify && (attr.name === "src" || attr.name === "href")) {
|
| + var value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B");
|
| + info.title += linkify(attr.value, value, "webkit-html-attribute-value", this.nodeName.toLowerCase() == "a");
|
| + } else {
|
| + var value = value.escapeHTML();
|
| + value = value.replace(/([\/;:\)\]\}])/g, "$1​");
|
| + info.title += "<span class=\"webkit-html-attribute-value\">" + value + "</span>";
|
| + }
|
| + info.title += "\"</span>";
|
| + }
|
| + }
|
| + info.title += "></span>​";
|
| +
|
| + // If this element only has a single child that is a text node,
|
| + // just show that text and the closing tag inline rather than
|
| + // create a subtree for them
|
| +
|
| + var textChild = onlyTextChild.call(this, Preferences.ignoreWhitespace);
|
| + var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength;
|
| +
|
| + if (showInlineText) {
|
| + info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​<span class=\"webkit-html-tag\"></" + this.nodeName.toLowerCase().escapeHTML() + "></span>";
|
| + info.hasChildren = false;
|
| + }
|
| + break;
|
| +
|
| + case Node.TEXT_NODE:
|
| + if (isNodeWhitespace.call(this))
|
| + info.title = "(whitespace)";
|
| + else
|
| + info.title = "\"<span class=\"webkit-html-text-node\">" + this.nodeValue.escapeHTML() + "</span>\"";
|
| + break
|
| +
|
| + case Node.COMMENT_NODE:
|
| + info.title = "<span class=\"webkit-html-comment\"><!--" + this.nodeValue.escapeHTML() + "--></span>";
|
| + break;
|
| +
|
| + case Node.DOCUMENT_TYPE_NODE:
|
| + info.title = "<span class=\"webkit-html-doctype\"><!DOCTYPE " + this.nodeName;
|
| + if (this.publicId) {
|
| + info.title += " PUBLIC \"" + this.publicId + "\"";
|
| + if (this.systemId)
|
| + info.title += " \"" + this.systemId + "\"";
|
| + } else if (this.systemId)
|
| + info.title += " SYSTEM \"" + this.systemId + "\"";
|
| + if (this.internalSubset)
|
| + info.title += " [" + this.internalSubset + "]";
|
| + info.title += "></span>";
|
| + break;
|
| + default:
|
| + info.title = this.nodeName.toLowerCase().collapseWhitespace().escapeHTML();
|
| + }
|
| +
|
| + return info;
|
| +}
|
| +
|
| +function getDocumentForNode(node) {
|
| + return node.nodeType == Node.DOCUMENT_NODE ? node : node.ownerDocument;
|
| +}
|
| +
|
| +function parentNode(node) {
|
| + return node.parentNode;
|
| +}
|
| +
|
| +Number.secondsToString = function(seconds, formatterFunction, higherResolution)
|
| +{
|
| + if (!formatterFunction)
|
| + formatterFunction = String.sprintf;
|
| +
|
| + var ms = seconds * 1000;
|
| + if (higherResolution && ms < 1000)
|
| + return formatterFunction("%.3fms", ms);
|
| + else if (ms < 1000)
|
| + return formatterFunction("%.0fms", ms);
|
| +
|
| + if (seconds < 60)
|
| + return formatterFunction("%.2fs", seconds);
|
| +
|
| + var minutes = seconds / 60;
|
| + if (minutes < 60)
|
| + return formatterFunction("%.1fmin", minutes);
|
| +
|
| + var hours = minutes / 60;
|
| + if (hours < 24)
|
| + return formatterFunction("%.1fhrs", hours);
|
| +
|
| + var days = hours / 24;
|
| + return formatterFunction("%.1f days", days);
|
| +}
|
| +
|
| +Number.bytesToString = function(bytes, formatterFunction, higherResolution)
|
| +{
|
| + if (!formatterFunction)
|
| + formatterFunction = String.sprintf;
|
| + if (typeof higherResolution === "undefined")
|
| + higherResolution = true;
|
| +
|
| + if (bytes < 1024)
|
| + return formatterFunction("%.0fB", bytes);
|
| +
|
| + var kilobytes = bytes / 1024;
|
| + if (higherResolution && kilobytes < 1024)
|
| + return formatterFunction("%.2fKB", kilobytes);
|
| + else if (kilobytes < 1024)
|
| + return formatterFunction("%.0fKB", kilobytes);
|
| +
|
| + var megabytes = kilobytes / 1024;
|
| + if (higherResolution)
|
| + return formatterFunction("%.3fMB", megabytes);
|
| + else
|
| + return formatterFunction("%.0fMB", megabytes);
|
| +}
|
| +
|
| +Number.constrain = function(num, min, max)
|
| +{
|
| + if (num < min)
|
| + num = min;
|
| + else if (num > max)
|
| + num = max;
|
| + return num;
|
| +}
|
| +
|
| +HTMLTextAreaElement.prototype.moveCursorToEnd = function()
|
| +{
|
| + var length = this.value.length;
|
| + this.setSelectionRange(length, length);
|
| +}
|
| +
|
| +Array.prototype.remove = function(value, onlyFirst)
|
| +{
|
| + if (onlyFirst) {
|
| + var index = this.indexOf(value);
|
| + if (index !== -1)
|
| + this.splice(index, 1);
|
| + return;
|
| + }
|
| +
|
| + var length = this.length;
|
| + for (var i = 0; i < length; ++i) {
|
| + if (this[i] === value)
|
| + this.splice(i, 1);
|
| + }
|
| +}
|
| +
|
| +function insertionIndexForObjectInListSortedByFunction(anObject, aList, aFunction)
|
| +{
|
| + // indexOf returns (-lowerBound - 1). Taking (-result - 1) works out to lowerBound.
|
| + return (-indexOfObjectInListSortedByFunction(anObject, aList, aFunction) - 1);
|
| +}
|
| +
|
| +function indexOfObjectInListSortedByFunction(anObject, aList, aFunction)
|
| +{
|
| + var first = 0;
|
| + var last = aList.length - 1;
|
| + var floor = Math.floor;
|
| + var mid, c;
|
| +
|
| + while (first <= last) {
|
| + mid = floor((first + last) / 2);
|
| + c = aFunction(anObject, aList[mid]);
|
| +
|
| + if (c > 0)
|
| + first = mid + 1;
|
| + else if (c < 0)
|
| + last = mid - 1;
|
| + else {
|
| + // Return the first occurance of an item in the list.
|
| + while (mid > 0 && aFunction(anObject, aList[mid - 1]) === 0)
|
| + mid--;
|
| + first = mid;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // By returning 1 less than the negative lower search bound, we can reuse this function
|
| + // for both indexOf and insertionIndexFor, with some simple arithmetic.
|
| + return (-first - 1);
|
| +}
|
| +
|
| +String.sprintf = function(format)
|
| +{
|
| + return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
|
| +}
|
| +
|
| +String.tokenizeFormatString = function(format)
|
| +{
|
| + var tokens = [];
|
| + var substitutionIndex = 0;
|
| +
|
| + function addStringToken(str)
|
| + {
|
| + tokens.push({ type: "string", value: str });
|
| + }
|
| +
|
| + function addSpecifierToken(specifier, precision, substitutionIndex)
|
| + {
|
| + tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
|
| + }
|
| +
|
| + var index = 0;
|
| + for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
|
| + addStringToken(format.substring(index, precentIndex));
|
| + index = precentIndex + 1;
|
| +
|
| + if (format[index] === "%") {
|
| + addStringToken("%");
|
| + ++index;
|
| + continue;
|
| + }
|
| +
|
| + if (!isNaN(format[index])) {
|
| + // The first character is a number, it might be a substitution index.
|
| + var number = parseInt(format.substring(index));
|
| + while (!isNaN(format[index]))
|
| + ++index;
|
| + // If the number is greater than zero and ends with a "$",
|
| + // then this is a substitution index.
|
| + if (number > 0 && format[index] === "$") {
|
| + substitutionIndex = (number - 1);
|
| + ++index;
|
| + }
|
| + }
|
| +
|
| + var precision = -1;
|
| + if (format[index] === ".") {
|
| + // This is a precision specifier. If no digit follows the ".",
|
| + // then the precision should be zero.
|
| + ++index;
|
| + precision = parseInt(format.substring(index));
|
| + if (isNaN(precision))
|
| + precision = 0;
|
| + while (!isNaN(format[index]))
|
| + ++index;
|
| + }
|
| +
|
| + addSpecifierToken(format[index], precision, substitutionIndex);
|
| +
|
| + ++substitutionIndex;
|
| + ++index;
|
| + }
|
| +
|
| + addStringToken(format.substring(index));
|
| +
|
| + return tokens;
|
| +}
|
| +
|
| +String.standardFormatters = {
|
| + d: function(substitution)
|
| + {
|
| + substitution = parseInt(substitution);
|
| + return !isNaN(substitution) ? substitution : 0;
|
| + },
|
| +
|
| + f: function(substitution, token)
|
| + {
|
| + substitution = parseFloat(substitution);
|
| + if (substitution && token.precision > -1)
|
| + substitution = substitution.toFixed(token.precision);
|
| + return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
|
| + },
|
| +
|
| + s: function(substitution)
|
| + {
|
| + return substitution;
|
| + },
|
| +};
|
| +
|
| +String.vsprintf = function(format, substitutions)
|
| +{
|
| + return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
|
| +}
|
| +
|
| +String.format = function(format, substitutions, formatters, initialValue, append)
|
| +{
|
| + if (!format || !substitutions || !substitutions.length)
|
| + return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
|
| +
|
| + function prettyFunctionName()
|
| + {
|
| + return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
|
| + }
|
| +
|
| + function warn(msg)
|
| + {
|
| + console.warn(prettyFunctionName() + ": " + msg);
|
| + }
|
| +
|
| + function error(msg)
|
| + {
|
| + console.error(prettyFunctionName() + ": " + msg);
|
| + }
|
| +
|
| + var result = initialValue;
|
| + var tokens = String.tokenizeFormatString(format);
|
| + var usedSubstitutionIndexes = {};
|
| +
|
| + for (var i = 0; i < tokens.length; ++i) {
|
| + var token = tokens[i];
|
| +
|
| + if (token.type === "string") {
|
| + result = append(result, token.value);
|
| + continue;
|
| + }
|
| +
|
| + if (token.type !== "specifier") {
|
| + error("Unknown token type \"" + token.type + "\" found.");
|
| + continue;
|
| + }
|
| +
|
| + if (token.substitutionIndex >= substitutions.length) {
|
| + // If there are not enough substitutions for the current substitutionIndex
|
| + // just output the format specifier literally and move on.
|
| + error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
|
| + result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
|
| + continue;
|
| + }
|
| +
|
| + usedSubstitutionIndexes[token.substitutionIndex] = true;
|
| +
|
| + if (!(token.specifier in formatters)) {
|
| + // Encountered an unsupported format character, treat as a string.
|
| + warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
|
| + result = append(result, substitutions[token.substitutionIndex]);
|
| + continue;
|
| + }
|
| +
|
| + result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
|
| + }
|
| +
|
| + var unusedSubstitutions = [];
|
| + for (var i = 0; i < substitutions.length; ++i) {
|
| + if (i in usedSubstitutionIndexes)
|
| + continue;
|
| + unusedSubstitutions.push(substitutions[i]);
|
| + }
|
| +
|
| + return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
|
| +}
|
|
|