Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Side by Side Diff: chrome_frame/tools/test/reference_build/chrome/resources/inspector/utilities.js

Issue 218019: Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 Object.proxyType = function(objectProxy)
30 {
31 if (objectProxy === null)
32 return "null";
33
34 var type = typeof objectProxy;
35 if (type !== "object" && type !== "function")
36 return type;
37
38 return objectProxy.type;
39 }
40
41 Object.properties = function(obj)
42 {
43 var properties = [];
44 for (var prop in obj)
45 properties.push(prop);
46 return properties;
47 }
48
49 Object.sortedProperties = function(obj, sortFunc)
50 {
51 return Object.properties(obj).sort(sortFunc);
52 }
53
54 Function.prototype.bind = function(thisObject)
55 {
56 var func = this;
57 var args = Array.prototype.slice.call(arguments, 1);
58 return function() { return func.apply(thisObject, args.concat(Array.prototyp e.slice.call(arguments, 0))) };
59 }
60
61 Node.prototype.rangeOfWord = function(offset, stopCharacters, stayWithinNode, di rection)
62 {
63 var startNode;
64 var startOffset = 0;
65 var endNode;
66 var endOffset = 0;
67
68 if (!stayWithinNode)
69 stayWithinNode = this;
70
71 if (!direction || direction === "backward" || direction === "both") {
72 var node = this;
73 while (node) {
74 if (node === stayWithinNode) {
75 if (!startNode)
76 startNode = stayWithinNode;
77 break;
78 }
79
80 if (node.nodeType === Node.TEXT_NODE) {
81 var start = (node === this ? (offset - 1) : (node.nodeValue.leng th - 1));
82 for (var i = start; i >= 0; --i) {
83 if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
84 startNode = node;
85 startOffset = i + 1;
86 break;
87 }
88 }
89 }
90
91 if (startNode)
92 break;
93
94 node = node.traversePreviousNode(false, stayWithinNode);
95 }
96
97 if (!startNode) {
98 startNode = stayWithinNode;
99 startOffset = 0;
100 }
101 } else {
102 startNode = this;
103 startOffset = offset;
104 }
105
106 if (!direction || direction === "forward" || direction === "both") {
107 node = this;
108 while (node) {
109 if (node === stayWithinNode) {
110 if (!endNode)
111 endNode = stayWithinNode;
112 break;
113 }
114
115 if (node.nodeType === Node.TEXT_NODE) {
116 var start = (node === this ? offset : 0);
117 for (var i = start; i < node.nodeValue.length; ++i) {
118 if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
119 endNode = node;
120 endOffset = i;
121 break;
122 }
123 }
124 }
125
126 if (endNode)
127 break;
128
129 node = node.traverseNextNode(false, stayWithinNode);
130 }
131
132 if (!endNode) {
133 endNode = stayWithinNode;
134 endOffset = stayWithinNode.nodeType === Node.TEXT_NODE ? stayWithinN ode.nodeValue.length : stayWithinNode.childNodes.length;
135 }
136 } else {
137 endNode = this;
138 endOffset = offset;
139 }
140
141 var result = this.ownerDocument.createRange();
142 result.setStart(startNode, startOffset);
143 result.setEnd(endNode, endOffset);
144
145 return result;
146 }
147
148 Element.prototype.removeStyleClass = function(className)
149 {
150 // Test for the simple case before using a RegExp.
151 if (this.className === className) {
152 this.className = "";
153 return;
154 }
155
156 this.removeMatchingStyleClasses(className.escapeForRegExp());
157 }
158
159 Element.prototype.removeMatchingStyleClasses = function(classNameRegex)
160 {
161 var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
162 if (regex.test(this.className))
163 this.className = this.className.replace(regex, " ");
164 }
165
166 Element.prototype.addStyleClass = function(className)
167 {
168 if (className && !this.hasStyleClass(className))
169 this.className += (this.className.length ? " " + className : className);
170 }
171
172 Element.prototype.hasStyleClass = function(className)
173 {
174 if (!className)
175 return false;
176 // Test for the simple case before using a RegExp.
177 if (this.className === className)
178 return true;
179 var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)");
180 return regex.test(this.className);
181 }
182
183 Element.prototype.positionAt = function(x, y)
184 {
185 this.style.left = x + "px";
186 this.style.top = y + "px";
187 }
188
189 Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray)
190 {
191 for (var node = this; node && node !== this.ownerDocument; node = node.paren tNode)
192 for (var i = 0; i < nameArray.length; ++i)
193 if (node.nodeName.toLowerCase() === nameArray[i].toLowerCase())
194 return node;
195 return null;
196 }
197
198 Node.prototype.enclosingNodeOrSelfWithNodeName = function(nodeName)
199 {
200 return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
201 }
202
203 Node.prototype.enclosingNodeOrSelfWithClass = function(className)
204 {
205 for (var node = this; node && node !== this.ownerDocument; node = node.paren tNode)
206 if (node.nodeType === Node.ELEMENT_NODE && node.hasStyleClass(className) )
207 return node;
208 return null;
209 }
210
211 Node.prototype.enclosingNodeWithClass = function(className)
212 {
213 if (!this.parentNode)
214 return null;
215 return this.parentNode.enclosingNodeOrSelfWithClass(className);
216 }
217
218 Element.prototype.query = function(query)
219 {
220 return this.ownerDocument.evaluate(query, this, null, XPathResult.FIRST_ORDE RED_NODE_TYPE, null).singleNodeValue;
221 }
222
223 Element.prototype.removeChildren = function()
224 {
225 while (this.firstChild)
226 this.removeChild(this.firstChild);
227 }
228
229 Element.prototype.isInsertionCaretInside = function()
230 {
231 var selection = window.getSelection();
232 if (!selection.rangeCount || !selection.isCollapsed)
233 return false;
234 var selectionRange = selection.getRangeAt(0);
235 return selectionRange.startContainer === this || selectionRange.startContain er.isDescendant(this);
236 }
237
238 Element.prototype.__defineGetter__("totalOffsetLeft", function()
239 {
240 var total = 0;
241 for (var element = this; element; element = element.offsetParent)
242 total += element.offsetLeft;
243 return total;
244 });
245
246 Element.prototype.__defineGetter__("totalOffsetTop", function()
247 {
248 var total = 0;
249 for (var element = this; element; element = element.offsetParent)
250 total += element.offsetTop;
251 return total;
252 });
253
254 Element.prototype.offsetRelativeToWindow = function(targetWindow)
255 {
256 var elementOffset = {x: 0, y: 0};
257 var curElement = this;
258 var curWindow = this.ownerDocument.defaultView;
259 while (curWindow && curElement) {
260 elementOffset.x += curElement.totalOffsetLeft;
261 elementOffset.y += curElement.totalOffsetTop;
262 if (curWindow === targetWindow)
263 break;
264
265 curElement = curWindow.frameElement;
266 curWindow = curWindow.parent;
267 }
268
269 return elementOffset;
270 }
271
272 Element.prototype.firstChildSkippingWhitespace = firstChildSkippingWhitespace;
273 Element.prototype.lastChildSkippingWhitespace = lastChildSkippingWhitespace;
274
275 Node.prototype.isWhitespace = isNodeWhitespace;
276 Node.prototype.displayName = nodeDisplayName;
277 Node.prototype.isAncestor = function(node)
278 {
279 return isAncestorNode(this, node);
280 };
281 Node.prototype.isDescendant = isDescendantNode;
282 Node.prototype.nextSiblingSkippingWhitespace = nextSiblingSkippingWhitespace;
283 Node.prototype.previousSiblingSkippingWhitespace = previousSiblingSkippingWhites pace;
284 Node.prototype.traverseNextNode = traverseNextNode;
285 Node.prototype.traversePreviousNode = traversePreviousNode;
286 Node.prototype.onlyTextChild = onlyTextChild;
287
288 String.prototype.hasSubstring = function(string, caseInsensitive)
289 {
290 if (!caseInsensitive)
291 return this.indexOf(string) !== -1;
292 return this.match(new RegExp(string.escapeForRegExp(), "i"));
293 }
294
295 String.prototype.escapeCharacters = function(chars)
296 {
297 var foundChar = false;
298 for (var i = 0; i < chars.length; ++i) {
299 if (this.indexOf(chars.charAt(i)) !== -1) {
300 foundChar = true;
301 break;
302 }
303 }
304
305 if (!foundChar)
306 return this;
307
308 var result = "";
309 for (var i = 0; i < this.length; ++i) {
310 if (chars.indexOf(this.charAt(i)) !== -1)
311 result += "\\";
312 result += this.charAt(i);
313 }
314
315 return result;
316 }
317
318 String.prototype.escapeForRegExp = function()
319 {
320 return this.escapeCharacters("^[]{}()\\.$*+?|");
321 }
322
323 String.prototype.escapeHTML = function()
324 {
325 return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt; ");
326 }
327
328 String.prototype.collapseWhitespace = function()
329 {
330 return this.replace(/[\s\xA0]+/g, " ");
331 }
332
333 String.prototype.trimLeadingWhitespace = function()
334 {
335 return this.replace(/^[\s\xA0]+/g, "");
336 }
337
338 String.prototype.trimTrailingWhitespace = function()
339 {
340 return this.replace(/[\s\xA0]+$/g, "");
341 }
342
343 String.prototype.trimWhitespace = function()
344 {
345 return this.replace(/^[\s\xA0]+|[\s\xA0]+$/g, "");
346 }
347
348 String.prototype.trimURL = function(baseURLDomain)
349 {
350 var result = this.replace(new RegExp("^http[s]?:\/\/", "i"), "");
351 if (baseURLDomain)
352 result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp() , "i"), "");
353 return result;
354 }
355
356 function isNodeWhitespace()
357 {
358 if (!this || this.nodeType !== Node.TEXT_NODE)
359 return false;
360 if (!this.nodeValue.length)
361 return true;
362 return this.nodeValue.match(/^[\s\xA0]+$/);
363 }
364
365 function nodeDisplayName()
366 {
367 if (!this)
368 return "";
369
370 switch (this.nodeType) {
371 case Node.DOCUMENT_NODE:
372 return "Document";
373
374 case Node.ELEMENT_NODE:
375 var name = "<" + this.nodeName.toLowerCase();
376
377 if (this.hasAttributes()) {
378 var value = this.getAttribute("id");
379 if (value)
380 name += " id=\"" + value + "\"";
381 value = this.getAttribute("class");
382 if (value)
383 name += " class=\"" + value + "\"";
384 if (this.nodeName.toLowerCase() === "a") {
385 value = this.getAttribute("name");
386 if (value)
387 name += " name=\"" + value + "\"";
388 value = this.getAttribute("href");
389 if (value)
390 name += " href=\"" + value + "\"";
391 } else if (this.nodeName.toLowerCase() === "img") {
392 value = this.getAttribute("src");
393 if (value)
394 name += " src=\"" + value + "\"";
395 } else if (this.nodeName.toLowerCase() === "iframe") {
396 value = this.getAttribute("src");
397 if (value)
398 name += " src=\"" + value + "\"";
399 } else if (this.nodeName.toLowerCase() === "input") {
400 value = this.getAttribute("name");
401 if (value)
402 name += " name=\"" + value + "\"";
403 value = this.getAttribute("type");
404 if (value)
405 name += " type=\"" + value + "\"";
406 } else if (this.nodeName.toLowerCase() === "form") {
407 value = this.getAttribute("action");
408 if (value)
409 name += " action=\"" + value + "\"";
410 }
411 }
412
413 return name + ">";
414
415 case Node.TEXT_NODE:
416 if (isNodeWhitespace.call(this))
417 return "(whitespace)";
418 return "\"" + this.nodeValue + "\"";
419
420 case Node.COMMENT_NODE:
421 return "<!--" + this.nodeValue + "-->";
422
423 case Node.DOCUMENT_TYPE_NODE:
424 var docType = "<!DOCTYPE " + this.nodeName;
425 if (this.publicId) {
426 docType += " PUBLIC \"" + this.publicId + "\"";
427 if (this.systemId)
428 docType += " \"" + this.systemId + "\"";
429 } else if (this.systemId)
430 docType += " SYSTEM \"" + this.systemId + "\"";
431 if (this.internalSubset)
432 docType += " [" + this.internalSubset + "]";
433 return docType + ">";
434 }
435
436 return this.nodeName.toLowerCase().collapseWhitespace();
437 }
438
439 function isAncestorNode(ancestor, node)
440 {
441 if (!node || !ancestor)
442 return false;
443
444 var currentNode = node.parentNode;
445 while (currentNode) {
446 if (ancestor === currentNode)
447 return true;
448 currentNode = currentNode.parentNode;
449 }
450 return false;
451 }
452
453 function isDescendantNode(descendant)
454 {
455 return isAncestorNode(descendant, this);
456 }
457
458 function nextSiblingSkippingWhitespace()
459 {
460 if (!this)
461 return;
462 var node = this.nextSibling;
463 while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(nod e))
464 node = node.nextSibling;
465 return node;
466 }
467
468 function previousSiblingSkippingWhitespace()
469 {
470 if (!this)
471 return;
472 var node = this.previousSibling;
473 while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(nod e))
474 node = node.previousSibling;
475 return node;
476 }
477
478 function firstChildSkippingWhitespace()
479 {
480 if (!this)
481 return;
482 var node = this.firstChild;
483 while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(nod e))
484 node = nextSiblingSkippingWhitespace.call(node);
485 return node;
486 }
487
488 function lastChildSkippingWhitespace()
489 {
490 if (!this)
491 return;
492 var node = this.lastChild;
493 while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(nod e))
494 node = previousSiblingSkippingWhitespace.call(node);
495 return node;
496 }
497
498 function traverseNextNode(skipWhitespace, stayWithin)
499 {
500 if (!this)
501 return;
502
503 var node = skipWhitespace ? firstChildSkippingWhitespace.call(this) : this.f irstChild;
504 if (node)
505 return node;
506
507 if (stayWithin && this === stayWithin)
508 return null;
509
510 node = skipWhitespace ? nextSiblingSkippingWhitespace.call(this) : this.next Sibling;
511 if (node)
512 return node;
513
514 node = this;
515 while (node && !(skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling) && (!stayWithin || !node.parentNode || node.parentNode !== st ayWithin))
516 node = node.parentNode;
517 if (!node)
518 return null;
519
520 return skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.next Sibling;
521 }
522
523 function traversePreviousNode(skipWhitespace, stayWithin)
524 {
525 if (!this)
526 return;
527 if (stayWithin && this === stayWithin)
528 return null;
529 var node = skipWhitespace ? previousSiblingSkippingWhitespace.call(this) : t his.previousSibling;
530 while (node && (skipWhitespace ? lastChildSkippingWhitespace.call(node) : no de.lastChild) )
531 node = skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.la stChild;
532 if (node)
533 return node;
534 return this.parentNode;
535 }
536
537 function onlyTextChild(ignoreWhitespace)
538 {
539 if (!this)
540 return null;
541
542 var firstChild = ignoreWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild;
543 if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE)
544 return null;
545
546 var sibling = ignoreWhitespace ? nextSiblingSkippingWhitespace.call(firstChi ld) : firstChild.nextSibling;
547 return sibling ? null : firstChild;
548 }
549
550 function nodeTitleInfo(hasChildren, linkify)
551 {
552 var info = {title: "", hasChildren: hasChildren};
553
554 switch (this.nodeType) {
555 case Node.DOCUMENT_NODE:
556 info.title = "Document";
557 break;
558
559 case Node.ELEMENT_NODE:
560 info.title = "<span class=\"webkit-html-tag\">&lt;" + this.nodeName. toLowerCase().escapeHTML();
561
562 if (this.hasAttributes()) {
563 for (var i = 0; i < this.attributes.length; ++i) {
564 var attr = this.attributes[i];
565 info.title += " <span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=&#820 3;\"";
566
567 var value = attr.value;
568 if (linkify && (attr.name === "src" || attr.name === "href") ) {
569 var value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B") ;
570 info.title += linkify(attr.value, value, "webkit-html-at tribute-value", this.nodeName.toLowerCase() == "a");
571 } else {
572 var value = value.escapeHTML();
573 value = value.replace(/([\/;:\)\]\}])/g, "$1&#8203;");
574 info.title += "<span class=\"webkit-html-attribute-value \">" + value + "</span>";
575 }
576 info.title += "\"</span>";
577 }
578 }
579 info.title += "&gt;</span>&#8203;";
580
581 // If this element only has a single child that is a text node,
582 // just show that text and the closing tag inline rather than
583 // create a subtree for them
584
585 var textChild = onlyTextChild.call(this, Preferences.ignoreWhitespac e);
586 var showInlineText = textChild && textChild.textContent.length < Pre ferences.maxInlineTextChildLength;
587
588 if (showInlineText) {
589 info.title += "<span class=\"webkit-html-text-node\">" + textChi ld.nodeValue.escapeHTML() + "</span>&#8203;<span class=\"webkit-html-tag\">&lt;/ " + this.nodeName.toLowerCase().escapeHTML() + "&gt;</span>";
590 info.hasChildren = false;
591 }
592 break;
593
594 case Node.TEXT_NODE:
595 if (isNodeWhitespace.call(this))
596 info.title = "(whitespace)";
597 else
598 info.title = "\"<span class=\"webkit-html-text-node\">" + this.n odeValue.escapeHTML() + "</span>\"";
599 break
600
601 case Node.COMMENT_NODE:
602 info.title = "<span class=\"webkit-html-comment\">&lt;!--" + this.no deValue.escapeHTML() + "--&gt;</span>";
603 break;
604
605 case Node.DOCUMENT_TYPE_NODE:
606 info.title = "<span class=\"webkit-html-doctype\">&lt;!DOCTYPE " + t his.nodeName;
607 if (this.publicId) {
608 info.title += " PUBLIC \"" + this.publicId + "\"";
609 if (this.systemId)
610 info.title += " \"" + this.systemId + "\"";
611 } else if (this.systemId)
612 info.title += " SYSTEM \"" + this.systemId + "\"";
613 if (this.internalSubset)
614 info.title += " [" + this.internalSubset + "]";
615 info.title += "&gt;</span>";
616 break;
617 default:
618 info.title = this.nodeName.toLowerCase().collapseWhitespace().escape HTML();
619 }
620
621 return info;
622 }
623
624 function getDocumentForNode(node) {
625 return node.nodeType == Node.DOCUMENT_NODE ? node : node.ownerDocument;
626 }
627
628 function parentNode(node) {
629 return node.parentNode;
630 }
631
632 Number.secondsToString = function(seconds, formatterFunction, higherResolution)
633 {
634 if (!formatterFunction)
635 formatterFunction = String.sprintf;
636
637 var ms = seconds * 1000;
638 if (higherResolution && ms < 1000)
639 return formatterFunction("%.3fms", ms);
640 else if (ms < 1000)
641 return formatterFunction("%.0fms", ms);
642
643 if (seconds < 60)
644 return formatterFunction("%.2fs", seconds);
645
646 var minutes = seconds / 60;
647 if (minutes < 60)
648 return formatterFunction("%.1fmin", minutes);
649
650 var hours = minutes / 60;
651 if (hours < 24)
652 return formatterFunction("%.1fhrs", hours);
653
654 var days = hours / 24;
655 return formatterFunction("%.1f days", days);
656 }
657
658 Number.bytesToString = function(bytes, formatterFunction, higherResolution)
659 {
660 if (!formatterFunction)
661 formatterFunction = String.sprintf;
662 if (typeof higherResolution === "undefined")
663 higherResolution = true;
664
665 if (bytes < 1024)
666 return formatterFunction("%.0fB", bytes);
667
668 var kilobytes = bytes / 1024;
669 if (higherResolution && kilobytes < 1024)
670 return formatterFunction("%.2fKB", kilobytes);
671 else if (kilobytes < 1024)
672 return formatterFunction("%.0fKB", kilobytes);
673
674 var megabytes = kilobytes / 1024;
675 if (higherResolution)
676 return formatterFunction("%.3fMB", megabytes);
677 else
678 return formatterFunction("%.0fMB", megabytes);
679 }
680
681 Number.constrain = function(num, min, max)
682 {
683 if (num < min)
684 num = min;
685 else if (num > max)
686 num = max;
687 return num;
688 }
689
690 HTMLTextAreaElement.prototype.moveCursorToEnd = function()
691 {
692 var length = this.value.length;
693 this.setSelectionRange(length, length);
694 }
695
696 Array.prototype.remove = function(value, onlyFirst)
697 {
698 if (onlyFirst) {
699 var index = this.indexOf(value);
700 if (index !== -1)
701 this.splice(index, 1);
702 return;
703 }
704
705 var length = this.length;
706 for (var i = 0; i < length; ++i) {
707 if (this[i] === value)
708 this.splice(i, 1);
709 }
710 }
711
712 function insertionIndexForObjectInListSortedByFunction(anObject, aList, aFunctio n)
713 {
714 // indexOf returns (-lowerBound - 1). Taking (-result - 1) works out to lowe rBound.
715 return (-indexOfObjectInListSortedByFunction(anObject, aList, aFunction) - 1 );
716 }
717
718 function indexOfObjectInListSortedByFunction(anObject, aList, aFunction)
719 {
720 var first = 0;
721 var last = aList.length - 1;
722 var floor = Math.floor;
723 var mid, c;
724
725 while (first <= last) {
726 mid = floor((first + last) / 2);
727 c = aFunction(anObject, aList[mid]);
728
729 if (c > 0)
730 first = mid + 1;
731 else if (c < 0)
732 last = mid - 1;
733 else {
734 // Return the first occurance of an item in the list.
735 while (mid > 0 && aFunction(anObject, aList[mid - 1]) === 0)
736 mid--;
737 first = mid;
738 break;
739 }
740 }
741
742 // By returning 1 less than the negative lower search bound, we can reuse th is function
743 // for both indexOf and insertionIndexFor, with some simple arithmetic.
744 return (-first - 1);
745 }
746
747 String.sprintf = function(format)
748 {
749 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
750 }
751
752 String.tokenizeFormatString = function(format)
753 {
754 var tokens = [];
755 var substitutionIndex = 0;
756
757 function addStringToken(str)
758 {
759 tokens.push({ type: "string", value: str });
760 }
761
762 function addSpecifierToken(specifier, precision, substitutionIndex)
763 {
764 tokens.push({ type: "specifier", specifier: specifier, precision: precis ion, substitutionIndex: substitutionIndex });
765 }
766
767 var index = 0;
768 for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; pre centIndex = format.indexOf("%", index)) {
769 addStringToken(format.substring(index, precentIndex));
770 index = precentIndex + 1;
771
772 if (format[index] === "%") {
773 addStringToken("%");
774 ++index;
775 continue;
776 }
777
778 if (!isNaN(format[index])) {
779 // The first character is a number, it might be a substitution index .
780 var number = parseInt(format.substring(index));
781 while (!isNaN(format[index]))
782 ++index;
783 // If the number is greater than zero and ends with a "$",
784 // then this is a substitution index.
785 if (number > 0 && format[index] === "$") {
786 substitutionIndex = (number - 1);
787 ++index;
788 }
789 }
790
791 var precision = -1;
792 if (format[index] === ".") {
793 // This is a precision specifier. If no digit follows the ".",
794 // then the precision should be zero.
795 ++index;
796 precision = parseInt(format.substring(index));
797 if (isNaN(precision))
798 precision = 0;
799 while (!isNaN(format[index]))
800 ++index;
801 }
802
803 addSpecifierToken(format[index], precision, substitutionIndex);
804
805 ++substitutionIndex;
806 ++index;
807 }
808
809 addStringToken(format.substring(index));
810
811 return tokens;
812 }
813
814 String.standardFormatters = {
815 d: function(substitution)
816 {
817 substitution = parseInt(substitution);
818 return !isNaN(substitution) ? substitution : 0;
819 },
820
821 f: function(substitution, token)
822 {
823 substitution = parseFloat(substitution);
824 if (substitution && token.precision > -1)
825 substitution = substitution.toFixed(token.precision);
826 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Num ber(0).toFixed(token.precision) : 0);
827 },
828
829 s: function(substitution)
830 {
831 return substitution;
832 },
833 };
834
835 String.vsprintf = function(format, substitutions)
836 {
837 return String.format(format, substitutions, String.standardFormatters, "", f unction(a, b) { return a + b; }).formattedResult;
838 }
839
840 String.format = function(format, substitutions, formatters, initialValue, append )
841 {
842 if (!format || !substitutions || !substitutions.length)
843 return { formattedResult: append(initialValue, format), unusedSubstituti ons: substitutions };
844
845 function prettyFunctionName()
846 {
847 return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
848 }
849
850 function warn(msg)
851 {
852 console.warn(prettyFunctionName() + ": " + msg);
853 }
854
855 function error(msg)
856 {
857 console.error(prettyFunctionName() + ": " + msg);
858 }
859
860 var result = initialValue;
861 var tokens = String.tokenizeFormatString(format);
862 var usedSubstitutionIndexes = {};
863
864 for (var i = 0; i < tokens.length; ++i) {
865 var token = tokens[i];
866
867 if (token.type === "string") {
868 result = append(result, token.value);
869 continue;
870 }
871
872 if (token.type !== "specifier") {
873 error("Unknown token type \"" + token.type + "\" found.");
874 continue;
875 }
876
877 if (token.substitutionIndex >= substitutions.length) {
878 // If there are not enough substitutions for the current substitutio nIndex
879 // just output the format specifier literally and move on.
880 error("not enough substitution arguments. Had " + substitutions.leng th + " but needed " + (token.substitutionIndex + 1) + ", so substitution was ski pped.");
881 result = append(result, "%" + (token.precision > -1 ? token.precisio n : "") + token.specifier);
882 continue;
883 }
884
885 usedSubstitutionIndexes[token.substitutionIndex] = true;
886
887 if (!(token.specifier in formatters)) {
888 // Encountered an unsupported format character, treat as a string.
889 warn("unsupported format character \u201C" + token.specifier + "\u20 1D. Treating as a string.");
890 result = append(result, substitutions[token.substitutionIndex]);
891 continue;
892 }
893
894 result = append(result, formatters[token.specifier](substitutions[token. substitutionIndex], token));
895 }
896
897 var unusedSubstitutions = [];
898 for (var i = 0; i < substitutions.length; ++i) {
899 if (i in usedSubstitutionIndexes)
900 continue;
901 unusedSubstitutions.push(substitutions[i]);
902 }
903
904 return { formattedResult: result, unusedSubstitutions: unusedSubstitutions } ;
905 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698