12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193 |
- /*
- * Copyright (C) 2013 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
- */
- if (!("bind" in Function.prototype)) {
- Object.defineProperty(Function.prototype, "bind",
- {
- value: function(thisObject)
- {
- var func = this;
- var args = Array.prototype.slice.call(arguments, 1);
- return function bound()
- {
- return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0)));
- };
- }
- });
- }
- Object.defineProperty(Object, "shallowCopy",
- {
- value: function(object)
- {
- // Make a new object and copy all the key/values. The values are not copied.
- var copy = {};
- var keys = Object.keys(object);
- for (var i = 0; i < keys.length; ++i)
- copy[keys[i]] = object[keys[i]];
- return copy;
- }
- });
- Object.defineProperty(Object, "shallowEqual",
- {
- value: function(a, b)
- {
- // Checks if two objects have the same top-level properties.
- // Check for strict equality in case they are the same object.
- if (a === b)
- return true;
- // Only objects can proceed. null is an object, but Object.keys throws for null.
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
- return false;
- var aKeys = Object.keys(a);
- var bKeys = Object.keys(b);
- // Check that each object has the same number of keys.
- if (aKeys.length !== bKeys.length)
- return false;
- // Check if all the keys and their values are equal.
- for (var i = 0; i < aKeys.length; ++i) {
- // Check that b has the same key as a.
- if (!(aKeys[i] in b))
- return false;
- // Check that the values are strict equal since this is only
- // a shallow check, not a recursive one.
- if (a[aKeys[i]] !== b[aKeys[i]])
- return false;
- }
- return true;
- }
- });
- Object.defineProperty(Object.prototype, "valueForCaseInsensitiveKey",
- {
- value: function(key)
- {
- if (this.hasOwnProperty(key))
- return this[key];
- var lowerCaseKey = key.toLowerCase();
- for (var currentKey in this) {
- if (currentKey.toLowerCase() === lowerCaseKey)
- return this[currentKey];
- }
- return undefined;
- }
- });
- Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithClass",
- {
- value: function(className)
- {
- for (var node = this; node && node !== this.ownerDocument; node = node.parentNode)
- if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains(className))
- return node;
- return null;
- }
- });
- Object.defineProperty(Node.prototype, "enclosingNodeWithClass",
- {
- value: function(className)
- {
- if (!this.parentNode)
- return null;
- return this.parentNode.enclosingNodeOrSelfWithClass(className);
- }
- });
- Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithNodeNameInArray",
- {
- value: function(nameArray)
- {
- var lowerCaseNameArray = nameArray.map(function(name) { return name.toLowerCase() });
- for (var node = this; node && node !== this.ownerDocument; node = node.parentNode) {
- for (var i = 0; i < nameArray.length; ++i) {
- if (node.nodeName.toLowerCase() === lowerCaseNameArray[i])
- return node;
- }
- }
- return null;
- }
- });
- Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithNodeName",
- {
- value: function(nodeName)
- {
- return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
- }
- });
- Object.defineProperty(Node.prototype, "isAncestor",
- {
- value: function(node)
- {
- if (!node)
- return false;
- var currentNode = node.parentNode;
- while (currentNode) {
- if (this === currentNode)
- return true;
- currentNode = currentNode.parentNode;
- }
- return false;
- }
- });
- Object.defineProperty(Node.prototype, "isDescendant",
- {
- value: function(descendant)
- {
- return !!descendant && descendant.isAncestor(this);
- }
- });
- Object.defineProperty(Node.prototype, "isSelfOrAncestor",
- {
- value: function(node)
- {
- return !!node && (node === this || this.isAncestor(node));
- }
- });
- Object.defineProperty(Node.prototype, "isSelfOrDescendant",
- {
- value: function(node)
- {
- return !!node && (node === this || this.isDescendant(node));
- }
- });
- Object.defineProperty(Node.prototype, "traverseNextNode",
- {
- value: function(stayWithin)
- {
- var node = this.firstChild;
- if (node)
- return node;
- if (stayWithin && this === stayWithin)
- return null;
- node = this.nextSibling;
- if (node)
- return node;
- node = this;
- while (node && !node.nextSibling && (!stayWithin || !node.parentNode || node.parentNode !== stayWithin))
- node = node.parentNode;
- if (!node)
- return null;
- return node.nextSibling;
- }
- });
- Object.defineProperty(Node.prototype, "traversePreviousNode",
- {
- value: function(stayWithin)
- {
- if (stayWithin && this === stayWithin)
- return null;
- var node = this.previousSibling;
- while (node && node.lastChild)
- node = node.lastChild;
- if (node)
- return node;
- return this.parentNode;
- }
- });
- Object.defineProperty(Node.prototype, "traverseNextTextNode",
- {
- value: function(stayWithin)
- {
- var node = this.traverseNextNode(stayWithin);
- if (!node)
- return;
- while (node && node.nodeType !== Node.TEXT_NODE)
- node = node.traverseNextNode(stayWithin);
- return node;
- }
- });
- Object.defineProperty(Node.prototype, "rangeBoundaryForOffset",
- {
- value: function(offset)
- {
- var textNode = this.traverseNextTextNode(this);
- while (textNode && offset > textNode.data.length) {
- offset -= textNode.data.length;
- textNode = textNode.traverseNextTextNode(this);
- }
- if (!textNode)
- return {container: this, offset: 0};
- return {container: textNode, offset: offset};
- }
- });
- Object.defineProperty(Node.prototype, "rangeOfWord",
- {
- value: 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(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(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;
- }
- });
- if (!("remove" in Element.prototype)) {
- Object.defineProperty(Element.prototype, "remove",
- {
- value: function()
- {
- if (this.parentNode)
- this.parentNode.removeChild(this);
- }
- });
- }
- Object.defineProperty(Element.prototype, "totalOffsetLeft",
- {
- get: function()
- {
- return this.getBoundingClientRect().left;
- }
- });
- Object.defineProperty(Element.prototype, "totalOffsetTop",
- {
- get: function()
- {
- return this.getBoundingClientRect().top;
- }
- });
- Object.defineProperty(Element.prototype, "removeChildren",
- {
- value: function()
- {
- // This has been tested to be the fastest removal method.
- if (this.firstChild)
- this.textContent = "";
- }
- });
- Object.defineProperty(Element.prototype, "isInsertionCaretInside",
- {
- value: 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);
- }
- });
- Object.defineProperty(Element.prototype, "removeMatchingStyleClasses",
- {
- value: function(classNameRegex)
- {
- var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
- if (regex.test(this.className))
- this.className = this.className.replace(regex, " ");
- }
- });
- Object.defineProperty(Element.prototype, "createChild",
- {
- value: function(elementName, className)
- {
- var element = this.ownerDocument.createElement(elementName);
- if (className)
- element.className = className;
- this.appendChild(element);
- return element;
- }
- });
- function AnchorBox(x, y, width, height)
- {
- this.x = x || 0;
- this.y = y || 0;
- this.width = width || 0;
- this.height = height || 0;
- }
- Object.defineProperty(Element.prototype, "boxInWindow",
- {
- value: function()
- {
- var anchorBox = new AnchorBox;
- var clientRect = this.getBoundingClientRect();
- console.assert(clientRect);
- anchorBox.x = clientRect.left;
- anchorBox.y = clientRect.top;
- anchorBox.width = clientRect.width;
- anchorBox.height = clientRect.height;
- return anchorBox;
- }
- });
- Object.defineProperty(Element.prototype, "positionAt",
- {
- value: function(x, y)
- {
- this.style.left = x + "px";
- this.style.top = y + "px";
- }
- });
- Object.defineProperty(Element.prototype, "pruneEmptyTextNodes",
- {
- value: function()
- {
- var sibling = this.firstChild;
- while (sibling) {
- var nextSibling = sibling.nextSibling;
- if (sibling.nodeType === this.TEXT_NODE && sibling.nodeValue === "")
- this.removeChild(sibling);
- sibling = nextSibling;
- }
- }
- });
- Object.defineProperty(Element.prototype, "isScrolledToBottom",
- {
- value: function()
- {
- // This code works only for 0-width border
- return this.scrollTop + this.clientHeight === this.scrollHeight;
- }
- });
- Object.defineProperty(DocumentFragment.prototype, "createChild",
- {
- value: Element.prototype.createChild
- });
- Object.defineProperty(Array.prototype, "contains",
- {
- value: function(value)
- {
- return this.indexOf(value) !== -1;
- }
- });
- Object.defineProperty(Array.prototype, "lastValue",
- {
- get: function()
- {
- if (!this.length)
- return undefined;
- return this[this.length - 1];
- }
- });
- Object.defineProperty(Array.prototype, "remove",
- {
- value: function(value, onlyFirst)
- {
- for (var i = this.length - 1; i >= 0; --i) {
- if (this[i] === value) {
- this.splice(i, 1);
- if (onlyFirst)
- return;
- }
- }
- }
- });
- Object.defineProperty(Array.prototype, "keySet",
- {
- value: function()
- {
- var keys = {};
- for (var i = 0; i < this.length; ++i)
- keys[this[i]] = true;
- return keys;
- }
- });
- Object.defineProperty(Array.prototype, "upperBound",
- {
- value: function(value)
- {
- var first = 0;
- var count = this.length;
- while (count > 0) {
- var step = count >> 1;
- var middle = first + step;
- if (value >= this[middle]) {
- first = middle + 1;
- count -= step + 1;
- } else
- count = step;
- }
- return first;
- }
- });
- Object.defineProperty(Array, "convert",
- {
- value: function(list, startIndex, endIndex)
- {
- return Array.prototype.slice.call(list, startIndex, endIndex);
- }
- });
- Object.defineProperty(String.prototype, "trimMiddle",
- {
- value: function(maxLength)
- {
- if (this.length <= maxLength)
- return this;
- var leftHalf = maxLength >> 1;
- var rightHalf = maxLength - leftHalf - 1;
- return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - rightHalf, rightHalf);
- }
- });
- Object.defineProperty(String.prototype, "trimEnd",
- {
- value: function(maxLength)
- {
- if (this.length <= maxLength)
- return this;
- return this.substr(0, maxLength - 1) + "\u2026";
- }
- });
- Object.defineProperty(String.prototype, "collapseWhitespace",
- {
- value: function()
- {
- return this.replace(/[\s\xA0]+/g, " ");
- }
- });
- Object.defineProperty(String.prototype, "escapeCharacters",
- {
- value: 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;
- }
- });
- Object.defineProperty(String.prototype, "escapeForRegExp",
- {
- value: function()
- {
- return this.escapeCharacters("^[]{}()\\.$*+?|");
- }
- });
- Object.defineProperty(String.prototype, "capitalize",
- {
- value: function()
- {
- return this.charAt(0).toUpperCase() + this.slice(1);
- }
- });
- Object.defineProperty(String, "tokenizeFormatString",
- {
- value: 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), 10);
- 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), 10);
- if (isNaN(precision))
- precision = 0;
- while (!isNaN(format[index]))
- ++index;
- }
- addSpecifierToken(format[index], precision, substitutionIndex);
- ++substitutionIndex;
- ++index;
- }
- addStringToken(format.substring(index));
- return tokens;
- }
- });
- Object.defineProperty(String.prototype, "startsWith",
- {
- value: function(string)
- {
- return this.lastIndexOf(string, 0) === 0;
- }
- });
- Object.defineProperty(String.prototype, "hash",
- {
- get: function()
- {
- // Matches the wtf/StringHasher.h (SuperFastHash) algorithm.
- // Arbitrary start value to avoid mapping all 0's to all 0's.
- const stringHashingStartValue = 0x9e3779b9;
- var result = stringHashingStartValue;
- var pendingCharacter = null;
- for (var i = 0; i < this.length; ++i) {
- var currentCharacter = this[i].charCodeAt(0);
- if (pendingCharacter === null) {
- pendingCharacter = currentCharacter
- continue;
- }
- result += pendingCharacter;
- result = (result << 16) ^ ((currentCharacter << 11) ^ result);
- result += result >> 11;
- pendingCharacter = null;
- }
- // Handle the last character in odd length strings.
- if (pendingCharacter !== null) {
- result += pendingCharacter;
- result ^= result << 11;
- result += result >> 17;
- }
- // Force "avalanching" of final 31 bits.
- result ^= result << 3;
- result += result >> 5;
- result ^= result << 2;
- result += result >> 15;
- result ^= result << 10;
- // Prevent 0 and negative results.
- return (0xffffffff + result + 1).toString(36);
- }
- });
- Object.defineProperty(String, "standardFormatters",
- {
- value: {
- d: function(substitution)
- {
- return !isNaN(substitution) ? substitution : 0;
- },
- f: function(substitution, token)
- {
- 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;
- }
- }
- });
- Object.defineProperty(String, "format",
- {
- value: 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};
- }
- });
- Object.defineProperty(String.prototype, "format",
- {
- value: function()
- {
- return String.format(this, arguments, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
- }
- });
- Object.defineProperty(String.prototype, "insertWordBreakCharacters",
- {
- value: function()
- {
- // Add zero width spaces after characters that are good to break after.
- // Otherwise a string with no spaces will not break and overflow its container.
- // This is mainly used on URL strings, so the characters are tailored for URLs.
- return this.replace(/([\/;:\)\]\}&?])/g, "$1\u200b");
- }
- });
- Object.defineProperty(String.prototype, "removeWordBreakCharacters",
- {
- value: function()
- {
- // Undoes what insertWordBreakCharacters did.
- return this.replace(/\u200b/g, "");
- }
- });
- Object.defineProperty(Number, "constrain",
- {
- value: function(num, min, max)
- {
- if (num < min)
- num = min;
- else if (num > max)
- num = max;
- return num;
- }
- });
- Object.defineProperty(Number, "secondsToString",
- {
- value: function(seconds, higherResolution)
- {
- var ms = seconds * 1000;
- if (higherResolution && ms < 100)
- return WebInspector.UIString("%.2fms").format(ms);
- else if (ms < 100)
- return WebInspector.UIString("%.1fms").format(ms);
- if (higherResolution && ms < 1000)
- return WebInspector.UIString("%.1fms").format(ms);
- else if (ms < 1000)
- return WebInspector.UIString("%.0fms").format(ms);
- if (seconds < 60)
- return WebInspector.UIString("%.2fs").format(seconds);
- var minutes = seconds / 60;
- if (minutes < 60)
- return WebInspector.UIString("%.1fmin").format(minutes);
- var hours = minutes / 60;
- if (hours < 24)
- return WebInspector.UIString("%.1fhrs").format(hours);
- var days = hours / 24;
- return WebInspector.UIString("%.1f days").format(days);
- }
- });
- Object.defineProperty(Number, "bytesToString",
- {
- value: function(bytes, higherResolution)
- {
- if (higherResolution === undefined)
- higherResolution = true;
- if (bytes < 1024)
- return WebInspector.UIString("%.0f B").format(bytes);
- var kilobytes = bytes / 1024;
- if (higherResolution && kilobytes < 1024)
- return WebInspector.UIString("%.2f KB").format(kilobytes);
- else if (kilobytes < 1024)
- return WebInspector.UIString("%.0f KB").format(kilobytes);
- var megabytes = kilobytes / 1024;
- if (higherResolution)
- return WebInspector.UIString("%.2f MB").format(megabytes);
- else
- return WebInspector.UIString("%.0f MB").format(megabytes);
- }
- });
- Object.defineProperty(Uint32Array, "isLittleEndian",
- {
- value: function()
- {
- if ("_isLittleEndian" in this)
- return this._isLittleEndian;
- var buffer = new ArrayBuffer(4);
- var longData = new Uint32Array(buffer);
- var data = new Uint8Array(buffer);
- longData[0] = 0x0a0b0c0d;
- this._isLittleEndian = data[0] === 0x0d && data[1] === 0x0c && data[2] === 0x0b && data[3] === 0x0a;
- return this._isLittleEndian;
- }
- });
- function isEmptyObject(object)
- {
- for (var property in object)
- return false;
- return true;
- }
- function isEnterKey(event)
- {
- // Check if this is an IME event.
- return event.keyCode !== 229 && event.keyIdentifier === "Enter";
- }
- function removeURLFragment(url)
- {
- var hashIndex = url.indexOf("#");
- if (hashIndex >= 0)
- return url.substring(0, hashIndex);
- return url;
- }
- function relativePath(path, basePath)
- {
- console.assert(path.charAt(0) === "/");
- console.assert(basePath.charAt(0) === "/");
-
- var pathComponents = path.split("/");
- var baseComponents = basePath.replace(/\/$/, "").split("/");
- var finalComponents = [];
- var index = 1;
- for (; index < pathComponents.length && index < baseComponents.length; ++index) {
- if (pathComponents[index] !== baseComponents[index])
- break;
- }
- for (var i = index; i < baseComponents.length; ++i)
- finalComponents.push("..");
- for (var i = index; i < pathComponents.length; ++i)
- finalComponents.push(pathComponents[i]);
- return finalComponents.join("/");
- }
- function resolveDotsInPath(path)
- {
- if (!path)
- return path;
- if (path.indexOf("./") === -1)
- return path;
- console.assert(path.charAt(0) === "/");
- var result = [];
- var components = path.split("/");
- for (var i = 0; i < components.length; ++i) {
- var component = components[i];
- // Skip over "./".
- if (component === ".")
- continue;
- // Rewind one component for "../".
- if (component === "..") {
- if (result.length === 1)
- continue;
- result.pop();
- continue;
- }
- result.push(component);
- }
- return result.join("/");
- }
- function parseURL(url)
- {
- url = url ? url.trim() : "";
- var match = url.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i);
- if (!match)
- return {scheme: null, host: null, port: null, path: null, queryString: null, fragment: null, lastPathComponent: null};
- var scheme = match[1].toLowerCase();
- var host = match[2].toLowerCase();
- var port = Number(match[3]) || null;
- var wholePath = match[4] || null;
- var fragment = match[5] || null;
- var path = wholePath;
- var queryString = null;
- // Split the path and the query string.
- if (wholePath) {
- var indexOfQuery = wholePath.indexOf("?");
- if (indexOfQuery !== -1) {
- path = wholePath.substring(0, indexOfQuery);
- queryString = wholePath.substring(indexOfQuery + 1);
- }
- path = resolveDotsInPath(path);
- }
- // Find last path component.
- var lastPathComponent = null;
- if (path && path !== "/") {
- // Skip the trailing slash if there is one.
- var endOffset = path[path.length - 1] === "/" ? 1 : 0;
- var lastSlashIndex = path.lastIndexOf("/", path.length - 1 - endOffset);
- if (lastSlashIndex !== -1)
- lastPathComponent = path.substring(lastSlashIndex + 1, path.length - endOffset);
- }
- return {scheme: scheme, host: host, port: port, path: path, queryString: queryString, fragment: fragment, lastPathComponent: lastPathComponent};
- }
- function absoluteURL(partialURL, baseURL)
- {
- partialURL = partialURL ? partialURL.trim() : "";
- // Return data and javascript URLs as-is.
- if (partialURL.startsWith("data:") || partialURL.startsWith("javascript:") || partialURL.startsWith("mailto:"))
- return partialURL;
- // If the URL has a scheme it is already a full URL, so return it.
- if (parseURL(partialURL).scheme)
- return partialURL;
- // If there is no partial URL, just return the base URL.
- if (!partialURL)
- return baseURL || null;
- var baseURLComponents = parseURL(baseURL);
- // The base URL needs to be an absolute URL. Return null if it isn't.
- if (!baseURLComponents.scheme)
- return null;
- // A URL that starts with "//" is a full URL without the scheme. Use the base URL scheme.
- if (partialURL[0] === "/" && partialURL[1] === "/")
- return baseURLComponents.scheme + ":" + partialURL;
- // The path can be null for URLs that have just a scheme and host (like "http://apple.com"). So make the path be "/".
- if (!baseURLComponents.path)
- baseURLComponents.path = "/";
- // Generate the base URL prefix that is used in the rest of the cases.
- var baseURLPrefix = baseURLComponents.scheme + "://" + baseURLComponents.host + (baseURLComponents.port ? (":" + baseURLComponents.port) : "");
- // A URL that starts with "?" is just a query string that gets applied to the base URL (replacing the base URL query string and fragment).
- if (partialURL[0] === "?")
- return baseURLPrefix + baseURLComponents.path + partialURL;
- // A URL that starts with "/" is an absolute path that gets applied to the base URL (replacing the base URL path, query string and fragment).
- if (partialURL[0] === "/")
- return baseURLPrefix + resolveDotsInPath(partialURL);
- // Generate the base path that is used in the final case by removing everything after the last "/" from the base URL's path.
- var basePath = baseURLComponents.path.substring(0, baseURLComponents.path.lastIndexOf("/")) + "/";
- return baseURLPrefix + resolveDotsInPath(basePath + partialURL);
- }
- function simpleGlobStringToRegExp(globString, regExpFlags)
- {
- // Only supports "*" globs.
- if (!globString)
- return null;
- // Escape everything from String.prototype.escapeForRegExp except "*".
- var regexString = globString.escapeCharacters("^[]{}()\\.$+?|");
- // Unescape all doubly escaped backslashes in front of escaped asterisks.
- // So "\\*" will become "\*" again, undoing escapeCharacters escaping of "\".
- // This makes "\*" match a literal "*" instead of using the "*" for globbing.
- regexString = regexString.replace(/\\\\\*/g, "\\*");
- // The following regex doesn't match an asterisk that has a backslash in front.
- // It also catches consecutive asterisks so they collapse down when replaced.
- var unescapedAsteriskRegex = /(^|[^\\])\*+/g;
- if (unescapedAsteriskRegex.test(globString)) {
- // Replace all unescaped asterisks with ".*".
- regexString = regexString.replace(unescapedAsteriskRegex, "$1.*");
- // Match edge boundaries when there is an asterisk to better meet the expectations
- // of the user. When someone types "*.js" they don't expect "foo.json" to match. They
- // would only expect that if they type "*.js*". We use \b (instead of ^ and $) to allow
- // matches inside paths or URLs, so "ba*.js" will match "foo/bar.js" but not "boo/bbar.js".
- // When there isn't an asterisk the regexString is just a substring search.
- regexString = "\\b" + regexString + "\\b";
- }
- return new RegExp(regexString, regExpFlags);
- }
- function parseLocationQueryParameters(arrayResult)
- {
- // The first character is always the "?".
- return parseQueryString(window.location.search.substring(1), arrayResult);
- }
- function parseQueryString(queryString, arrayResult)
- {
- if (!queryString)
- return arrayResult ? [] : {};
- function decode(string)
- {
- try {
- // Replace "+" with " " then decode precent encoded values.
- return decodeURIComponent(string.replace(/\+/g, " "));
- } catch (e) {
- return string;
- }
- }
- var parameters = arrayResult ? [] : {};
- var parameterStrings = queryString.split("&");
- for (var i = 0; i < parameterStrings.length; ++i) {
- var pair = parameterStrings[i].split("=").map(decode);
- if (arrayResult)
- parameters.push({name: pair[0], value: pair[1]});
- else
- parameters[pair[0]] = pair[1];
- }
- return parameters;
- }
|