diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /editor/libeditor/tests/browserscope/lib/richtext | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editor/libeditor/tests/browserscope/lib/richtext')
9 files changed, 2498 insertions, 0 deletions
diff --git a/editor/libeditor/tests/browserscope/lib/richtext/LICENSE b/editor/libeditor/tests/browserscope/lib/richtext/LICENSE new file mode 100644 index 0000000000..57bc88a15a --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/editor/libeditor/tests/browserscope/lib/richtext/README b/editor/libeditor/tests/browserscope/lib/richtext/README new file mode 100644 index 0000000000..a3bc3110f4 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/README @@ -0,0 +1,58 @@ +README FOR BROWSERSCOPE +----------------------- + +Hey there - thanks for downloading the code. This file has instructions +for getting setup so that you can run the codebase locally. + +This project is built on Google App Engine using the +Django web application framework and written in Python. + +To get started, you'll need to first download the App Engine SDK at: +http://code.google.com/appengine/downloads.html + +For local development, just startup the server: +./pathto/google_appengine/dev_appserver.py --port=8080 browserscope + +You should then be able to access the local application at: +http://localhost:8080/ + +Note: the first time you hit the homepage it may take a little +while - that's because it's trying to read out median times for all +of the tests from a nonexistent datastore and write to memcache. +Just be a lil patient. + +You can run the unit tests at: + http://localhost:8080/test + + +CONTRIBUTING +------------------ + +Most likely you are interested in adding new tests or creating +a new test category. If you are interested in adding tests to an existing +"category" you may want to get in touch with the maintainer for that +branch of the tree. We are really looking forward to receiving your +code in patch format. Currently the category maintainers are: +Network: Steve Souders <souders@gmail.com> +Reflow: Lindsey Simon <elsigh@gmail.com> +Security: Adam Barth <adam@adambarth.com> and Collin Jackson <collin@collinjackson.com> + + +To create a completely new test category: + * Copy one of the existing directories in categories/ + * Edit your test_set.py, handlers.py + * Add your files in templates/ and static/ + * Update urls.py and settings.CATEGORIES + * Follow the examples of other tests re: + * beaconing using/testdriver_base + * your GetScoreAndDisplayValue method + * your GetRowScoreAndDisplayValue method + +References: + * App Engine Docs - http://code.google.com/appengine/docs/python/overview.html + * App Engine Group - http://groups.google.com/group/google-appengine + * Python Docs - http://www.python.org/doc/ + * Django - http://www.djangoproject.com/ + + + diff --git a/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla b/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla new file mode 100644 index 0000000000..5d304943f7 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla @@ -0,0 +1,17 @@ +The BrowserScope project provides a set of cross-browser HTML editor tests, +which we import in our test suite in order to run them as part of our +continuous integration system. + +We pull tests occasionally from their Subversion repository using the pull +script which can be found in this directory. We also record the revision ID +which we've used in the current_revision file inside this directory. + +Using the pull script is quite easy, just switch to this directory, and say: + +sh update_from_upstream + +There are tests which we're currently failing on, and there will probably be +more of those in the future. We should maintain a list of the failing tests +manually in currentStatus.js (which can also be found in this directory), to +make sure that the suite passes entirely, with failing tests marked as todo +items. diff --git a/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js b/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js new file mode 100644 index 0000000000..4b645d9db4 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js @@ -0,0 +1,43 @@ +/** + * This file lists the tests in the BrowserScope suite which we are currently + * failing. We mark them as todo items to keep track of them. + */ + +var knownFailures = { + // Dummy result items. There is one for each category. + 'apply' : { + '0-undefined' : true + }, + 'unapply' : { + '0-undefined' : true + }, + 'change' : { + '0-undefined' : true + }, + 'query' : { + '0-undefined' : true + }, + 'a' : { + 'createbookmark-0' : true, + 'decreasefontsize-0' : true, + 'fontsize-1' : true, + 'subscript-1' : true, + 'superscript-1' : true, + }, + 'u': { + 'removeformat-1' : true, + 'removeformat-2' : true, + 'strikethrough-2' : true, + 'subscript-1' : true, + 'superscript-1' : true, + 'unbookmark-0' : true, + }, + 'q': { + 'fontsize-1' : true, + 'fontsize-2' : true, + }, +}; + +function isKnownFailure(type, test, param) { + return (type in knownFailures) && knownFailures[type][test + "-" + param]; +} diff --git a/editor/libeditor/tests/browserscope/lib/richtext/current_revision b/editor/libeditor/tests/browserscope/lib/richtext/current_revision new file mode 100644 index 0000000000..1e25699145 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/current_revision @@ -0,0 +1 @@ +775 diff --git a/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html b/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html new file mode 100644 index 0000000000..a294f0b56b --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html @@ -0,0 +1,11 @@ +<html>
+<head>
+ <script>
+ function load(){
+ window.document.designMode = "On";
+ }
+ </script>
+</head>
+<body contentEditable="true" onload="load()">
+</body>
+</html>
\ No newline at end of file diff --git a/editor/libeditor/tests/browserscope/lib/richtext/richtext/js/range.js b/editor/libeditor/tests/browserscope/lib/richtext/richtext/js/range.js new file mode 100644 index 0000000000..edd23f86b9 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/richtext/js/range.js @@ -0,0 +1,1069 @@ +var goog$global = this, goog$isString = function(val) { + return typeof val == "string" +}; +Math.floor(Math.random() * 2147483648).toString(36); +var goog$now = Date.now || function() { + return(new Date).getTime() +}, goog$inherits = function(childCtor, parentCtor) { + function tempCtor() { + } + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor +};var goog$array$peek = function(array) { + return array[array.length - 1] +}, goog$array$indexOf = function(arr, obj, opt_fromIndex) { + if(arr.indexOf)return arr.indexOf(obj, opt_fromIndex); + if(Array.indexOf)return Array.indexOf(arr, obj, opt_fromIndex); + for(var fromIndex = opt_fromIndex == null ? 0 : opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex, i = fromIndex;i < arr.length;i++)if(i in arr && arr[i] === obj)return i; + return-1 +}, goog$array$map = function(arr, f, opt_obj) { + if(arr.map)return arr.map(f, opt_obj); + if(Array.map)return Array.map(arr, f, opt_obj); + for(var l = arr.length, res = [], resLength = 0, arr2 = goog$isString(arr) ? arr.split("") : arr, i = 0;i < l;i++)if(i in arr2)res[resLength++] = f.call(opt_obj, arr2[i], i, arr); + return res +}, goog$array$some = function(arr, f, opt_obj) { + if(arr.some)return arr.some(f, opt_obj); + if(Array.some)return Array.some(arr, f, opt_obj); + for(var l = arr.length, arr2 = goog$isString(arr) ? arr.split("") : arr, i = 0;i < l;i++)if(i in arr2 && f.call(opt_obj, arr2[i], i, arr))return true; + return false +}, goog$array$every = function(arr, f, opt_obj) { + if(arr.every)return arr.every(f, opt_obj); + if(Array.every)return Array.every(arr, f, opt_obj); + for(var l = arr.length, arr2 = goog$isString(arr) ? arr.split("") : arr, i = 0;i < l;i++)if(i in arr2 && !f.call(opt_obj, arr2[i], i, arr))return false; + return true +}, goog$array$find = function(arr, f, opt_obj) { + var i; + JSCompiler_inline_label_goog$array$findIndex_12: { + for(var JSCompiler_inline_l = arr.length, JSCompiler_inline_arr2 = goog$isString(arr) ? arr.split("") : arr, JSCompiler_inline_i = 0;JSCompiler_inline_i < JSCompiler_inline_l;JSCompiler_inline_i++)if(JSCompiler_inline_i in JSCompiler_inline_arr2 && f.call(opt_obj, JSCompiler_inline_arr2[JSCompiler_inline_i], JSCompiler_inline_i, arr)) { + i = JSCompiler_inline_i; + break JSCompiler_inline_label_goog$array$findIndex_12 + }i = -1 + }return i < 0 ? null : goog$isString(arr) ? arr.charAt(i) : arr[i] +};var goog$string$trim = function(str) { + return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, "") +}, goog$string$htmlEscape = function(str, opt_isLikelyToContainHtmlChars) { + if(opt_isLikelyToContainHtmlChars)return str.replace(goog$string$amperRe_, "&").replace(goog$string$ltRe_, "<").replace(goog$string$gtRe_, ">").replace(goog$string$quotRe_, """); + else { + if(!goog$string$allRe_.test(str))return str; + if(str.includes("&"))str = str.replace(goog$string$amperRe_, "&"); + if(str.includes("<"))str = str.replace(goog$string$ltRe_, "<"); + if(str.includes(">"))str = str.replace(goog$string$gtRe_, ">"); + if(str.includes('"'))str = str.replace(goog$string$quotRe_, """); + return str + } +}, goog$string$amperRe_ = /&/g, goog$string$ltRe_ = /</g, goog$string$gtRe_ = />/g, goog$string$quotRe_ = /\"/g, goog$string$allRe_ = /[&<>\"]/, goog$string$contains = function(s, ss) { + return s.includes(ss) +}, goog$string$compareVersions = function(version1, version2) { + for(var order = 0, v1Subs = goog$string$trim(String(version1)).split("."), v2Subs = goog$string$trim(String(version2)).split("."), subCount = Math.max(v1Subs.length, v2Subs.length), subIdx = 0;order == 0 && subIdx < subCount;subIdx++) { + var v1Sub = v1Subs[subIdx] || "", v2Sub = v2Subs[subIdx] || "", v1CompParser = new RegExp("(\\d*)(\\D*)", "g"), v2CompParser = new RegExp("(\\d*)(\\D*)", "g"); + do { + var v1Comp = v1CompParser.exec(v1Sub) || ["", "", ""], v2Comp = v2CompParser.exec(v2Sub) || ["", "", ""]; + if(v1Comp[0].length == 0 && v2Comp[0].length == 0)break; + var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10), v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10); + order = goog$string$compareElements_(v1CompNum, v2CompNum) || goog$string$compareElements_(v1Comp[2].length == 0, v2Comp[2].length == 0) || goog$string$compareElements_(v1Comp[2], v2Comp[2]) + }while(order == 0) + }return order +}, goog$string$compareElements_ = function(left, right) { + if(left < right)return-1; + else if(left > right)return 1; + return 0 +}; +goog$now();var goog$userAgent$detectedOpera_, goog$userAgent$detectedIe_, goog$userAgent$detectedWebkit_, goog$userAgent$detectedMobile_, goog$userAgent$detectedGecko_, goog$userAgent$detectedCamino_, goog$userAgent$detectedMac_, goog$userAgent$detectedWindows_, goog$userAgent$detectedLinux_, goog$userAgent$detectedX11_, goog$userAgent$getUserAgentString = function() { + return goog$global.navigator ? goog$global.navigator.userAgent : null +}, goog$userAgent$getNavigator = function() { + return goog$global.navigator +}; +goog$userAgent$detectedCamino_ = goog$userAgent$detectedGecko_ = goog$userAgent$detectedMobile_ = goog$userAgent$detectedWebkit_ = goog$userAgent$detectedIe_ = goog$userAgent$detectedOpera_ = false; +var JSCompiler_inline_ua_15; +if(JSCompiler_inline_ua_15 = goog$userAgent$getUserAgentString()) { + var JSCompiler_inline_navigator$$1_16 = goog$userAgent$getNavigator(); + goog$userAgent$detectedOpera_ = JSCompiler_inline_ua_15.indexOf("Opera") == 0; + goog$userAgent$detectedIe_ = !goog$userAgent$detectedOpera_ && JSCompiler_inline_ua_15.includes("MSIE"); + goog$userAgent$detectedMobile_ = (goog$userAgent$detectedWebkit_ = !goog$userAgent$detectedOpera_ && JSCompiler_inline_ua_15.includes("WebKit")) && JSCompiler_inline_ua_15.includes("Mobile"); + goog$userAgent$detectedCamino_ = (goog$userAgent$detectedGecko_ = !goog$userAgent$detectedOpera_ && !goog$userAgent$detectedWebkit_ && JSCompiler_inline_navigator$$1_16.product == "Gecko") && JSCompiler_inline_navigator$$1_16.vendor == "Camino" +}var goog$userAgent$OPERA = goog$userAgent$detectedOpera_, goog$userAgent$IE = goog$userAgent$detectedIe_, goog$userAgent$GECKO = goog$userAgent$detectedGecko_, goog$userAgent$WEBKIT = goog$userAgent$detectedWebkit_, goog$userAgent$MOBILE = goog$userAgent$detectedMobile_, goog$userAgent$PLATFORM, JSCompiler_inline_navigator$$2_19 = goog$userAgent$getNavigator(); +goog$userAgent$PLATFORM = JSCompiler_inline_navigator$$2_19 && JSCompiler_inline_navigator$$2_19.platform || ""; +goog$userAgent$detectedMac_ = goog$string$contains(goog$userAgent$PLATFORM, "Mac"); +goog$userAgent$detectedWindows_ = goog$string$contains(goog$userAgent$PLATFORM, "Win"); +goog$userAgent$detectedLinux_ = goog$string$contains(goog$userAgent$PLATFORM, "Linux"); +goog$userAgent$detectedX11_ = !!goog$userAgent$getNavigator() && goog$string$contains(goog$userAgent$getNavigator().appVersion || "", "X11"); +var goog$userAgent$VERSION, JSCompiler_inline_version$$6_26 = "", JSCompiler_inline_re$$2_27; +if(goog$userAgent$OPERA && goog$global.opera) { + var JSCompiler_inline_operaVersion_28 = goog$global.opera.version; + JSCompiler_inline_version$$6_26 = typeof JSCompiler_inline_operaVersion_28 == "function" ? JSCompiler_inline_operaVersion_28() : JSCompiler_inline_operaVersion_28 +}else { + if(goog$userAgent$GECKO)JSCompiler_inline_re$$2_27 = /rv\:([^\);]+)(\)|;)/; + else if(goog$userAgent$IE)JSCompiler_inline_re$$2_27 = /MSIE\s+([^\);]+)(\)|;)/; + else if(goog$userAgent$WEBKIT)JSCompiler_inline_re$$2_27 = /WebKit\/(\S+)/; + if(JSCompiler_inline_re$$2_27) { + var JSCompiler_inline_arr$$41_29 = JSCompiler_inline_re$$2_27.exec(goog$userAgent$getUserAgentString()); + JSCompiler_inline_version$$6_26 = JSCompiler_inline_arr$$41_29 ? JSCompiler_inline_arr$$41_29[1] : "" + } +}goog$userAgent$VERSION = JSCompiler_inline_version$$6_26; +var goog$userAgent$isVersionCache_ = {}, goog$userAgent$isVersion = function(version) { + return goog$userAgent$isVersionCache_[version] || (goog$userAgent$isVersionCache_[version] = goog$string$compareVersions(goog$userAgent$VERSION, version) >= 0) +};var goog$dom$getWindow = function(opt_doc) { + return opt_doc ? goog$dom$getWindow_(opt_doc) : window +}, goog$dom$getWindow_ = function(doc) { + if(doc.parentWindow)return doc.parentWindow; + if(goog$userAgent$WEBKIT && !goog$userAgent$isVersion("500") && !goog$userAgent$MOBILE) { + var scriptElement = doc.createElement("script"); + scriptElement.innerHTML = "document.parentWindow=window"; + var parentElement = doc.documentElement; + parentElement.appendChild(scriptElement); + parentElement.removeChild(scriptElement); + return doc.parentWindow + }return doc.defaultView +}, goog$dom$appendChild = function(parent, child) { + parent.appendChild(child) +}, goog$dom$BAD_CONTAINS_WEBKIT_ = goog$userAgent$WEBKIT && goog$userAgent$isVersion("522"), goog$dom$contains = function(parent, descendant) { + if(typeof parent.contains != "undefined" && !goog$dom$BAD_CONTAINS_WEBKIT_ && descendant.nodeType == 1)return parent == descendant || parent.contains(descendant); + if(typeof parent.compareDocumentPosition != "undefined")return parent == descendant || Boolean(parent.compareDocumentPosition(descendant) & 16); + for(;descendant && parent != descendant;)descendant = descendant.parentNode; + return descendant == parent +}, goog$dom$compareNodeOrder = function(node1, node2) { + if(node1 == node2)return 0; + if(node1.compareDocumentPosition)return node1.compareDocumentPosition(node2) & 2 ? 1 : -1; + if("sourceIndex" in node1 || node1.parentNode && "sourceIndex" in node1.parentNode) { + var isElement1 = node1.nodeType == 1, isElement2 = node2.nodeType == 1; + if(isElement1 && isElement2)return node1.sourceIndex - node2.sourceIndex; + else { + var parent1 = node1.parentNode, parent2 = node2.parentNode; + if(parent1 == parent2)return goog$dom$compareSiblingOrder_(node1, node2); + if(!isElement1 && goog$dom$contains(parent1, node2))return-1 * goog$dom$compareParentsDescendantNodeIe_(node1, node2); + if(!isElement2 && goog$dom$contains(parent2, node1))return goog$dom$compareParentsDescendantNodeIe_(node2, node1); + return(isElement1 ? node1.sourceIndex : parent1.sourceIndex) - (isElement2 ? node2.sourceIndex : parent2.sourceIndex) + } + }var doc = goog$dom$getOwnerDocument(node1), range1, range2; + range1 = doc.createRange(); + range1.selectNode(node1); + range1.collapse(true); + range2 = doc.createRange(); + range2.selectNode(node2); + range2.collapse(true); + return range1.compareBoundaryPoints(goog$global.Range.START_TO_END, range2) +}, goog$dom$compareParentsDescendantNodeIe_ = function(textNode, node) { + var parent = textNode.parentNode; + if(parent == node)return-1; + for(var sibling = node;sibling.parentNode != parent;)sibling = sibling.parentNode; + return goog$dom$compareSiblingOrder_(sibling, textNode) +}, goog$dom$compareSiblingOrder_ = function(node1, node2) { + for(var s = node2;s = s.previousSibling;)if(s == node1)return-1; + return 1 +}, goog$dom$findCommonAncestor = function() { + var i, count = arguments.length; + if(count) { + if(count == 1)return arguments[0] + }else return null; + var paths = [], minLength = Infinity; + for(i = 0;i < count;i++) { + for(var ancestors = [], node = arguments[i];node;) { + ancestors.unshift(node); + node = node.parentNode + }paths.push(ancestors); + minLength = Math.min(minLength, ancestors.length) + }var output = null; + for(i = 0;i < minLength;i++) { + for(var first = paths[0][i], j = 1;j < count;j++)if(first != paths[j][i])return output; + output = first + }return output +}, goog$dom$getOwnerDocument = function(node) { + // Added 'editorDoc' as hack for browsers that don't support node.ownerDocument + return node.nodeType == 9 ? node : node.ownerDocument || node.document || editorDoc +}, goog$dom$DomHelper = function(opt_document) { + this.document_ = opt_document || goog$global.document || document +}; +goog$dom$DomHelper.prototype.getDocument = function() { + return this.document_ +}; +goog$dom$DomHelper.prototype.createElement = function(name) { + return this.document_.createElement(name) +}; +goog$dom$DomHelper.prototype.getWindow = function() { + return goog$dom$getWindow_(this.document_) +}; +goog$dom$DomHelper.prototype.appendChild = goog$dom$appendChild; +goog$dom$DomHelper.prototype.contains = goog$dom$contains;var goog$Disposable = function() { +};if("StopIteration" in goog$global)var goog$iter$StopIteration = goog$global.StopIteration; +else goog$iter$StopIteration = Error("StopIteration"); +var goog$iter$Iterator = function() { +}; +goog$iter$Iterator.prototype.next = function() { + throw goog$iter$StopIteration; +}; +goog$iter$Iterator.prototype.__iterator__ = function() { + return this +};var goog$debug$exposeException = function(err, opt_fn) { + try { + var e, JSCompiler_inline_href_34; + JSCompiler_inline_label_goog$getObjectByName_61: { + for(var JSCompiler_inline_parts = "window.location.href".split("."), JSCompiler_inline_cur = goog$global, JSCompiler_inline_part;JSCompiler_inline_part = JSCompiler_inline_parts.shift();)if(JSCompiler_inline_cur[JSCompiler_inline_part])JSCompiler_inline_cur = JSCompiler_inline_cur[JSCompiler_inline_part]; + else { + JSCompiler_inline_href_34 = null; + break JSCompiler_inline_label_goog$getObjectByName_61 + }JSCompiler_inline_href_34 = JSCompiler_inline_cur + }e = typeof err == "string" ? {message:err, name:"Unknown error", lineNumber:"Not available", fileName:JSCompiler_inline_href_34, stack:"Not available"} : !err.lineNumber || !err.fileName || !err.stack ? {message:err.message, name:err.name, lineNumber:err.lineNumber || err.line || "Not available", fileName:err.fileName || err.filename || err.sourceURL || JSCompiler_inline_href_34, stack:err.stack || "Not available"} : err; + var error = "Message: " + goog$string$htmlEscape(e.message) + '\nUrl: <a href="view-source:' + e.fileName + '" target="_new">' + e.fileName + "</a>\nLine: " + e.lineNumber + "\n\nBrowser stack:\n" + goog$string$htmlEscape(e.stack + "-> ") + "[end]\n\nJS stack traversal:\n" + goog$string$htmlEscape(goog$debug$getStacktrace(opt_fn) + "-> "); + return error + }catch(e2) { + return"Exception trying to expose exception! You win, we lose. " + e2 + } +}, goog$debug$getStacktrace = function(opt_fn) { + return goog$debug$getStacktraceHelper_(opt_fn || arguments.callee.caller, []) +}, goog$debug$getStacktraceHelper_ = function(fn, visited) { + var sb = [], JSCompiler_inline_result_36; + JSCompiler_inline_label_goog$array$contains_41:JSCompiler_inline_result_36 = visited.contains ? visited.contains(fn) : goog$array$indexOf(visited, fn) > -1; + if(JSCompiler_inline_result_36)sb.push("[...circular reference...]"); + else if(fn && visited.length < 50) { + sb.push(goog$debug$getFunctionName(fn) + "("); + for(var args = fn.arguments, i = 0;i < args.length;i++) { + i > 0 && sb.push(", "); + var argDesc, arg = args[i]; + switch(typeof arg) { + case "object": + argDesc = arg ? "object" : "null"; + break; + case "string": + argDesc = arg; + break; + case "number": + argDesc = String(arg); + break; + case "boolean": + argDesc = arg ? "true" : "false"; + break; + case "function": + argDesc = (argDesc = goog$debug$getFunctionName(arg)) ? argDesc : "[fn]"; + break; + case "undefined": + ; + default: + argDesc = typeof arg; + break + } + if(argDesc.length > 40)argDesc = argDesc.substr(0, 40) + "..."; + sb.push(argDesc) + }visited.push(fn); + sb.push(")\n"); + try { + sb.push(goog$debug$getStacktraceHelper_(fn.caller, visited)) + }catch(e) { + sb.push("[exception trying to get caller]\n") + } + }else fn ? sb.push("[...long stack...]") : sb.push("[end]"); + return sb.join("") +}, goog$debug$getFunctionName = function(fn) { + var functionSource = String(fn); + if(!goog$debug$fnNameCache_[functionSource]) { + var matches = /function ([^\(]+)/.exec(functionSource); + if(matches) { + var method = matches[1]; + goog$debug$fnNameCache_[functionSource] = method + }else goog$debug$fnNameCache_[functionSource] = "[Anonymous]" + }return goog$debug$fnNameCache_[functionSource] +}, goog$debug$fnNameCache_ = {};var goog$debug$LogRecord = function(level, msg, loggerName, opt_time, opt_sequenceNumber) { + this.sequenceNumber_ = typeof opt_sequenceNumber == "number" ? opt_sequenceNumber : goog$debug$LogRecord$nextSequenceNumber_++; + this.time_ = opt_time || goog$now(); + this.level_ = level; + this.msg_ = msg; + this.loggerName_ = loggerName +}; +goog$debug$LogRecord.prototype.exception_ = null; +goog$debug$LogRecord.prototype.exceptionText_ = null; +var goog$debug$LogRecord$nextSequenceNumber_ = 0; +goog$debug$LogRecord.prototype.setException = function(exception) { + this.exception_ = exception +}; +goog$debug$LogRecord.prototype.setExceptionText = function(text) { + this.exceptionText_ = text +}; +goog$debug$LogRecord.prototype.setLevel = function(level) { + this.level_ = level +};var goog$debug$Logger = function(name) { + this.name_ = name; + this.parent_ = null; + this.children_ = {}; + this.handlers_ = [] +}; +goog$debug$Logger.prototype.level_ = null; +var goog$debug$Logger$Level = function(name, value) { + this.name = name; + this.value = value +}; +goog$debug$Logger$Level.prototype.toString = function() { + return this.name +}; +new goog$debug$Logger$Level("OFF", Infinity); +new goog$debug$Logger$Level("SHOUT", 1200); +var goog$debug$Logger$Level$SEVERE = new goog$debug$Logger$Level("SEVERE", 1000), goog$debug$Logger$Level$WARNING = new goog$debug$Logger$Level("WARNING", 900); +new goog$debug$Logger$Level("INFO", 800); +var goog$debug$Logger$Level$CONFIG = new goog$debug$Logger$Level("CONFIG", 700); +new goog$debug$Logger$Level("FINE", 500); +new goog$debug$Logger$Level("FINER", 400); +new goog$debug$Logger$Level("FINEST", 300); +new goog$debug$Logger$Level("ALL", 0); +goog$debug$Logger.prototype.setLevel = function(level) { + this.level_ = level +}; +goog$debug$Logger.prototype.isLoggable = function(level) { + if(this.level_)return level.value >= this.level_.value; + if(this.parent_)return this.parent_.isLoggable(level); + return false +}; +goog$debug$Logger.prototype.log = function(level, msg, opt_exception) { + this.isLoggable(level) && this.logRecord(this.getLogRecord(level, msg, opt_exception)) +}; +goog$debug$Logger.prototype.getLogRecord = function(level, msg, opt_exception) { + var logRecord = new goog$debug$LogRecord(level, String(msg), this.name_); + if(opt_exception) { + logRecord.setException(opt_exception); + logRecord.setExceptionText(goog$debug$exposeException(opt_exception, arguments.callee.caller)) + }return logRecord +}; +goog$debug$Logger.prototype.severe = function(msg, opt_exception) { + this.log(goog$debug$Logger$Level$SEVERE, msg, opt_exception) +}; +goog$debug$Logger.prototype.warning = function(msg, opt_exception) { + this.log(goog$debug$Logger$Level$WARNING, msg, opt_exception) +}; +goog$debug$Logger.prototype.logRecord = function(logRecord) { + if(this.isLoggable(logRecord.level_))for(var target = this;target;) { + target.callPublish_(logRecord); + target = target.parent_ + } +}; +goog$debug$Logger.prototype.callPublish_ = function(logRecord) { + for(var i = 0;i < this.handlers_.length;i++)this.handlers_[i](logRecord) +}; +goog$debug$Logger.prototype.setParent_ = function(parent) { + this.parent_ = parent +}; +goog$debug$Logger.prototype.addChild_ = function(name, logger) { + this.children_[name] = logger +}; +var goog$debug$LogManager$loggers_ = {}, goog$debug$LogManager$rootLogger_ = null, goog$debug$LogManager$getLogger = function(name) { + if(!goog$debug$LogManager$rootLogger_) { + goog$debug$LogManager$rootLogger_ = new goog$debug$Logger(""); + goog$debug$LogManager$loggers_[""] = goog$debug$LogManager$rootLogger_; + goog$debug$LogManager$rootLogger_.setLevel(goog$debug$Logger$Level$CONFIG) + }return name in goog$debug$LogManager$loggers_ ? goog$debug$LogManager$loggers_[name] : goog$debug$LogManager$createLogger_(name) +}, goog$debug$LogManager$createLogger_ = function(name) { + var logger = new goog$debug$Logger(name), parts = name.split("."), leafName = parts[parts.length - 1]; + parts.length = parts.length - 1; + var parentName = parts.join("."), parentLogger = goog$debug$LogManager$getLogger(parentName); + parentLogger.addChild_(leafName, logger); + logger.setParent_(parentLogger); + return goog$debug$LogManager$loggers_[name] = logger +};var goog$dom$SavedRange = function() { + goog$Disposable.call(this) +}; +goog$inherits(goog$dom$SavedRange, goog$Disposable); +goog$debug$LogManager$getLogger("goog.dom.SavedRange");var goog$dom$TagIterator = function(opt_node, opt_reversed, opt_unconstrained, opt_tagType, opt_depth) { + this.reversed = !!opt_reversed; + opt_node && this.setPosition(opt_node, opt_tagType); + this.depth = opt_depth != undefined ? opt_depth : this.tagType || 0; + if(this.reversed)this.depth *= -1; + this.constrained = !opt_unconstrained +}; +goog$inherits(goog$dom$TagIterator, goog$iter$Iterator); +goog$dom$TagIterator.prototype.node = null; +goog$dom$TagIterator.prototype.tagType = null; +goog$dom$TagIterator.prototype.started_ = false; +goog$dom$TagIterator.prototype.setPosition = function(node, opt_tagType, opt_depth) { + if(this.node = node)this.tagType = typeof opt_tagType == "number" ? opt_tagType : this.node.nodeType != 1 ? 0 : this.reversed ? -1 : 1; + if(typeof opt_depth == "number")this.depth = opt_depth +}; +goog$dom$TagIterator.prototype.next = function() { + var node; + if(this.started_) { + if(!this.node || this.constrained && this.depth == 0)throw goog$iter$StopIteration;node = this.node; + var startType = this.reversed ? -1 : 1; + if(this.tagType == startType) { + var child = this.reversed ? node.lastChild : node.firstChild; + child ? this.setPosition(child) : this.setPosition(node, startType * -1) + }else { + var sibling = this.reversed ? node.previousSibling : node.nextSibling; + sibling ? this.setPosition(sibling) : this.setPosition(node.parentNode, startType * -1) + }this.depth += this.tagType * (this.reversed ? -1 : 1) + }else this.started_ = true; + node = this.node; + if(!this.node)throw goog$iter$StopIteration;return node +}; +goog$dom$TagIterator.prototype.isStartTag = function() { + return this.tagType == 1 +};var goog$dom$AbstractRange = function() { +}; +goog$dom$AbstractRange.prototype.getTextRanges = function() { + for(var output = [], i = 0, len = this.getTextRangeCount();i < len;i++)output.push(this.getTextRange(i)); + return output +}; +goog$dom$AbstractRange.prototype.getAnchorNode = function() { + return this.isReversed() ? this.getEndNode() : this.getStartNode() +}; +goog$dom$AbstractRange.prototype.getAnchorOffset = function() { + return this.isReversed() ? this.getEndOffset() : this.getStartOffset() +}; +goog$dom$AbstractRange.prototype.getFocusNode = function() { + return this.isReversed() ? this.getStartNode() : this.getEndNode() +}; +goog$dom$AbstractRange.prototype.getFocusOffset = function() { + return this.isReversed() ? this.getStartOffset() : this.getEndOffset() +}; +goog$dom$AbstractRange.prototype.isReversed = function() { + return false +}; +goog$dom$AbstractRange.prototype.getDocument = function() { + return goog$dom$getOwnerDocument(goog$userAgent$IE ? this.getContainer() : this.getStartNode()) +}; +goog$dom$AbstractRange.prototype.getWindow = function() { + return goog$dom$getWindow(this.getDocument()) +}; +goog$dom$AbstractRange.prototype.containsNode = function(node, opt_allowPartial) { + return this.containsRange(goog$dom$TextRange$createFromNodeContents(node, undefined), opt_allowPartial) +}; +var goog$dom$RangeIterator = function(node, opt_reverse) { + goog$dom$TagIterator.call(this, node, opt_reverse, true) +}; +goog$inherits(goog$dom$RangeIterator, goog$dom$TagIterator);var goog$dom$AbstractMultiRange = function() { +}; +goog$inherits(goog$dom$AbstractMultiRange, goog$dom$AbstractRange); +goog$dom$AbstractMultiRange.prototype.containsRange = function(otherRange, opt_allowPartial) { + var ranges = this.getTextRanges(), otherRanges = otherRange.getTextRanges(), fn = opt_allowPartial ? goog$array$some : goog$array$every; + return fn(otherRanges, function(otherRange) { + return goog$array$some(ranges, function(range) { + return range.containsRange(otherRange, opt_allowPartial) + }) + }) +};var goog$dom$TextRangeIterator = function(startNode, startOffset, endNode, endOffset, opt_reverse) { + var goNext; + if(startNode) { + this.startNode_ = startNode; + this.startOffset_ = startOffset; + this.endNode_ = endNode; + this.endOffset_ = endOffset; + if(startNode.nodeType == 1 && startNode.tagName != "BR") { + var startChildren = startNode.childNodes, candidate = startChildren[startOffset]; + if(candidate) { + this.startNode_ = candidate; + this.startOffset_ = 0 + }else { + if(startChildren.length)this.startNode_ = goog$array$peek(startChildren); + goNext = true + } + }if(endNode.nodeType == 1)if(this.endNode_ = endNode.childNodes[endOffset])this.endOffset_ = 0; + else this.endNode_ = endNode + }goog$dom$RangeIterator.call(this, opt_reverse ? this.endNode_ : this.startNode_, opt_reverse); + if(goNext)try { + this.next() + }catch(e) { + if(e != goog$iter$StopIteration)throw e; + } +}; +goog$inherits(goog$dom$TextRangeIterator, goog$dom$RangeIterator); +goog$dom$TextRangeIterator.prototype.startNode_ = null; +goog$dom$TextRangeIterator.prototype.endNode_ = null; +goog$dom$TextRangeIterator.prototype.startOffset_ = 0; +goog$dom$TextRangeIterator.prototype.endOffset_ = 0; +goog$dom$TextRangeIterator.prototype.getStartNode = function() { + return this.startNode_ +}; +goog$dom$TextRangeIterator.prototype.getEndNode = function() { + return this.endNode_ +}; +goog$dom$TextRangeIterator.prototype.isLast = function() { + return this.started_ && this.node == this.endNode_ && (!this.endOffset_ || !this.isStartTag()) +}; +goog$dom$TextRangeIterator.prototype.next = function() { + if(this.isLast())throw goog$iter$StopIteration;return goog$dom$TextRangeIterator.superClass_.next.call(this) +};var goog$userAgent$jscript$DETECTED_HAS_JSCRIPT_, goog$userAgent$jscript$DETECTED_VERSION_, JSCompiler_inline_hasScriptEngine_44 = "ScriptEngine" in goog$global; +goog$userAgent$jscript$DETECTED_VERSION_ = (goog$userAgent$jscript$DETECTED_HAS_JSCRIPT_ = JSCompiler_inline_hasScriptEngine_44 && goog$global.ScriptEngine() == "JScript") ? goog$global.ScriptEngineMajorVersion() + "." + goog$global.ScriptEngineMinorVersion() + "." + goog$global.ScriptEngineBuildVersion() : "0";var goog$dom$browserrange$AbstractRange = function() { +}; +goog$dom$browserrange$AbstractRange.prototype.containsRange = function(range, opt_allowPartial) { + return this.containsBrowserRange(range.range_, opt_allowPartial) +}; +goog$dom$browserrange$AbstractRange.prototype.containsBrowserRange = function(range, opt_allowPartial) { + try { + return opt_allowPartial ? this.compareBrowserRangeEndpoints(range, 0, 1) >= 0 && this.compareBrowserRangeEndpoints(range, 1, 0) <= 0 : this.compareBrowserRangeEndpoints(range, 0, 0) >= 0 && this.compareBrowserRangeEndpoints(range, 1, 1) <= 0 + }catch(e) { + if(!goog$userAgent$IE)throw e;return false + } +}; +goog$dom$browserrange$AbstractRange.prototype.containsNode = function(node, opt_allowPartial) { + return this.containsRange(goog$userAgent$IE ? goog$dom$browserrange$IeRange$createFromNodeContents(node) : goog$userAgent$WEBKIT ? new goog$dom$browserrange$WebKitRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)) : goog$userAgent$GECKO ? new goog$dom$browserrange$GeckoRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)) : new goog$dom$browserrange$W3cRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)), opt_allowPartial) +}; +goog$dom$browserrange$AbstractRange.prototype.__iterator__ = function() { + return new goog$dom$TextRangeIterator(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset()) +};var goog$dom$browserrange$W3cRange = function(range) { + this.range_ = range +}; +goog$inherits(goog$dom$browserrange$W3cRange, goog$dom$browserrange$AbstractRange); +var goog$dom$browserrange$W3cRange$getBrowserRangeForNode = function(node) { + var nodeRange = goog$dom$getOwnerDocument(node).createRange(); + if(node.nodeType == 3) { + nodeRange.setStart(node, 0); + nodeRange.setEnd(node, node.length) + }else { + for(var tempNode, leaf = node;tempNode = leaf.firstChild;)leaf = tempNode; + nodeRange.setStart(leaf, 0); + for(leaf = node;tempNode = leaf.lastChild;)leaf = tempNode; + nodeRange.setEnd(leaf, leaf.nodeType == 1 ? leaf.childNodes.length : leaf.length) + }return nodeRange +}, goog$dom$browserrange$W3cRange$getBrowserRangeForNodes_ = function(startNode, startOffset, endNode, endOffset) { + var nodeRange = goog$dom$getOwnerDocument(startNode).createRange(); + nodeRange.setStart(startNode, startOffset); + nodeRange.setEnd(endNode, endOffset); + return nodeRange +}; +goog$dom$browserrange$W3cRange.prototype.getContainer = function() { + return this.range_.commonAncestorContainer +}; +goog$dom$browserrange$W3cRange.prototype.getStartNode = function() { + return this.range_.startContainer +}; +goog$dom$browserrange$W3cRange.prototype.getStartOffset = function() { + return this.range_.startOffset +}; +goog$dom$browserrange$W3cRange.prototype.getEndNode = function() { + return this.range_.endContainer +}; +goog$dom$browserrange$W3cRange.prototype.getEndOffset = function() { + return this.range_.endOffset +}; +goog$dom$browserrange$W3cRange.prototype.compareBrowserRangeEndpoints = function(range, thisEndpoint, otherEndpoint) { + return this.range_.compareBoundaryPoints(otherEndpoint == 1 ? thisEndpoint == 1 ? goog$global.Range.START_TO_START : goog$global.Range.START_TO_END : thisEndpoint == 1 ? goog$global.Range.END_TO_START : goog$global.Range.END_TO_END, range) +}; +goog$dom$browserrange$W3cRange.prototype.isCollapsed = function() { + return this.range_.collapsed +}; +goog$dom$browserrange$W3cRange.prototype.select = function(reverse) { + var win = goog$dom$getWindow(goog$dom$getOwnerDocument(this.getStartNode())); + this.selectInternal(win.getSelection(), reverse) +}; +goog$dom$browserrange$W3cRange.prototype.selectInternal = function(selection) { + selection.addRange(this.range_) +}; +goog$dom$browserrange$W3cRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart) +};var goog$dom$browserrange$GeckoRange = function(range) { + goog$dom$browserrange$W3cRange.call(this, range) +}; +goog$inherits(goog$dom$browserrange$GeckoRange, goog$dom$browserrange$W3cRange); +goog$dom$browserrange$GeckoRange.prototype.selectInternal = function(selection, reversed) { + var anchorNode = reversed ? this.getEndNode() : this.getStartNode(), anchorOffset = reversed ? this.getEndOffset() : this.getStartOffset(), focusNode = reversed ? this.getStartNode() : this.getEndNode(), focusOffset = reversed ? this.getStartOffset() : this.getEndOffset(); + selection.collapse(anchorNode, anchorOffset); + if(anchorNode != focusNode || anchorOffset != focusOffset)selection.extend(focusNode, focusOffset) +};var goog$dom$browserrange$IeRange = function(range, doc) { + this.range_ = range; + this.doc_ = doc +}; +goog$inherits(goog$dom$browserrange$IeRange, goog$dom$browserrange$AbstractRange); +var goog$dom$browserrange$IeRange$logger_ = goog$debug$LogManager$getLogger("goog.dom.browserrange.IeRange"), goog$dom$browserrange$IeRange$getBrowserRangeForNode_ = function(node) { + var nodeRange = goog$dom$getOwnerDocument(node).body.createTextRange(); + if(node.nodeType == 1)nodeRange.moveToElementText(node); + else { + for(var offset = 0, sibling = node;sibling = sibling.previousSibling;) { + var nodeType = sibling.nodeType; + if(nodeType == 3)offset += sibling.length; + else if(nodeType == 1) { + nodeRange.moveToElementText(sibling); + break + } + }sibling || nodeRange.moveToElementText(node.parentNode); + nodeRange.collapse(!sibling); + offset && nodeRange.move("character", offset); + nodeRange.moveEnd("character", node.length) + }return nodeRange +}, goog$dom$browserrange$IeRange$getBrowserRangeForNodes_ = function(startNode, startOffset, endNode, endOffset) { + var child, collapse = false; + if(startNode.nodeType == 1) { + startOffset > startNode.childNodes.length && goog$dom$browserrange$IeRange$logger_.severe("Cannot have startOffset > startNode child count"); + child = startNode.childNodes[startOffset]; + collapse = !child; + startNode = child || startNode; + startOffset = 0 + }var leftRange = goog$dom$browserrange$IeRange$getBrowserRangeForNode_(startNode); + startOffset && leftRange.move("character", startOffset); + collapse && leftRange.collapse(false); + collapse = false; + if(endNode.nodeType == 1) { + startOffset > startNode.childNodes.length && goog$dom$browserrange$IeRange$logger_.severe("Cannot have endOffset > endNode child count"); + endNode = (child = endNode.childNodes[endOffset]) || endNode; + if(endNode.tagName == "BR")endOffset = 1; + else { + endOffset = 0; + collapse = !child + } + }var rightRange = goog$dom$browserrange$IeRange$getBrowserRangeForNode_(endNode); + rightRange.collapse(!collapse); + endOffset && rightRange.moveEnd("character", endOffset); + leftRange.setEndPoint("EndToEnd", rightRange); + return leftRange +}, goog$dom$browserrange$IeRange$createFromNodeContents = function(node) { + var range = new goog$dom$browserrange$IeRange(goog$dom$browserrange$IeRange$getBrowserRangeForNode_(node), goog$dom$getOwnerDocument(node)); + range.parentNode_ = node; + return range +}; +goog$dom$browserrange$IeRange.prototype.parentNode_ = null; +goog$dom$browserrange$IeRange.prototype.startNode_ = null; +goog$dom$browserrange$IeRange.prototype.endNode_ = null; +goog$dom$browserrange$IeRange.prototype.clearCachedValues_ = function() { + this.parentNode_ = this.startNode_ = this.endNode_ = null +}; +goog$dom$browserrange$IeRange.prototype.getContainer = function() { + if(!this.parentNode_) { + for(var selectText = this.range_.text, i = 1;selectText.charAt(selectText.length - i) == " ";i++)this.range_.moveEnd("character", -1); + for(var parent = this.range_.parentElement(), htmlText = this.range_.htmlText.replace(/(\r\n|\r|\n)+/g, " ");htmlText.length > parent.outerHTML.replace(/(\r\n|\r|\n)+/g, " ").length;)parent = parent.parentNode; + for(;parent.childNodes.length == 1 && parent.innerText == (parent.firstChild.nodeType == 3 ? parent.firstChild.nodeValue : parent.firstChild.innerText);) { + if(parent.firstChild.tagName == "IMG")break; + parent = parent.firstChild + }if(selectText.length == 0)parent = this.findDeepestContainer_(parent); + this.parentNode_ = parent + }return this.parentNode_ +}; +goog$dom$browserrange$IeRange.prototype.findDeepestContainer_ = function(node) { + for(var childNodes = node.childNodes, i = 0, len = childNodes.length;i < len;i++) { + var child = childNodes[i]; + if(child.nodeType == 1)if(this.range_.inRange(goog$dom$browserrange$IeRange$getBrowserRangeForNode_(child)))return this.findDeepestContainer_(child) + }return node +}; +goog$dom$browserrange$IeRange.prototype.getStartNode = function() { + return this.startNode_ || (this.startNode_ = this.getEndpointNode_(1)) +}; +goog$dom$browserrange$IeRange.prototype.getStartOffset = function() { + return this.getOffset_(1) +}; +goog$dom$browserrange$IeRange.prototype.getEndNode = function() { + return this.endNode_ || (this.endNode_ = this.getEndpointNode_(0)) +}; +goog$dom$browserrange$IeRange.prototype.getEndOffset = function() { + return this.getOffset_(0) +}; +goog$dom$browserrange$IeRange.prototype.containsRange = function(range, opt_allowPartial) { + return this.containsBrowserRange(range.range_, opt_allowPartial) +}; +goog$dom$browserrange$IeRange.prototype.compareBrowserRangeEndpoints = function(range, thisEndpoint, otherEndpoint) { + return this.range_.compareEndPoints((thisEndpoint == 1 ? "Start" : "End") + "To" + (otherEndpoint == 1 ? "Start" : "End"), range) +}; +goog$dom$browserrange$IeRange.prototype.getEndpointNode_ = function(endpoint, opt_node) { + var node = opt_node || this.getContainer(); + if(!node || !node.firstChild) { + if(endpoint == 0 && node.previousSibling && node.previousSibling.tagName == "BR" && this.getOffset_(endpoint, node) == 0)node = node.previousSibling; + return node.tagName == "BR" ? node.parentNode : node + }for(var child = endpoint == 1 ? node.firstChild : node.lastChild;child;) { + if(this.containsNode(child, true))return this.getEndpointNode_(endpoint, child); + child = endpoint == 1 ? child.nextSibling : child.previousSibling + }return node +}; +goog$dom$browserrange$IeRange.prototype.getOffset_ = function(endpoint, opt_container) { + var container = opt_container || (endpoint == 1 ? this.getStartNode() : this.getEndNode()); + if(container.nodeType == 1) { + for(var children = container.childNodes, len = children.length, i = endpoint == 1 ? 0 : len - 1;i >= 0 && i < len;) { + var child = children[i]; + if(this.containsNode(child, true)) { + endpoint == 0 && child.previousSibling && child.previousSibling.tagName == "BR" && this.getOffset_(endpoint, child) == 0 && i--; + break + }i += endpoint == 1 ? 1 : -1 + }return i == -1 ? 0 : i + }else { + var range = this.range_.duplicate(), nodeRange = goog$dom$browserrange$IeRange$getBrowserRangeForNode_(container); + range.setEndPoint(endpoint == 1 ? "EndToEnd" : "StartToStart", nodeRange); + var rangeLength = range.text.length; + return endpoint == 0 ? rangeLength : container.length - rangeLength + } +}; +goog$dom$browserrange$IeRange.prototype.isCollapsed = function() { + return this.range_.text == "" +}; +goog$dom$browserrange$IeRange.prototype.select = function() { + this.range_.select() +}; +goog$dom$browserrange$IeRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart); + if(toStart)this.endNode_ = this.startNode_; + else this.startNode_ = this.endNode_ +};var goog$dom$browserrange$WebKitRange = function(range) { + goog$dom$browserrange$W3cRange.call(this, range) +}; +goog$inherits(goog$dom$browserrange$WebKitRange, goog$dom$browserrange$W3cRange); +goog$dom$browserrange$WebKitRange.prototype.compareBrowserRangeEndpoints = function(range, thisEndpoint, otherEndpoint) { + if(goog$userAgent$isVersion("528"))return goog$dom$browserrange$WebKitRange.superClass_.compareBrowserRangeEndpoints.call(this, range, thisEndpoint, otherEndpoint); + return this.range_.compareBoundaryPoints(otherEndpoint == 1 ? thisEndpoint == 1 ? goog$global.Range.START_TO_START : goog$global.Range.END_TO_START : thisEndpoint == 1 ? goog$global.Range.START_TO_END : goog$global.Range.END_TO_END, range) +}; +goog$dom$browserrange$WebKitRange.prototype.selectInternal = function(selection, reversed) { + selection.removeAllRanges(); + reversed ? selection.setBaseAndExtent(this.getEndNode(), this.getEndOffset(), this.getStartNode(), this.getStartOffset()) : selection.setBaseAndExtent(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset()) +};var goog$dom$browserrange$createRangeFromNodes = function(startNode, startOffset, endNode, endOffset) { + return goog$userAgent$IE ? new goog$dom$browserrange$IeRange(goog$dom$browserrange$IeRange$getBrowserRangeForNodes_(startNode, startOffset, endNode, endOffset), goog$dom$getOwnerDocument(startNode)) : goog$userAgent$WEBKIT ? new goog$dom$browserrange$WebKitRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNodes_(startNode, startOffset, endNode, endOffset)) : goog$userAgent$GECKO ? new goog$dom$browserrange$GeckoRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNodes_(startNode, startOffset, + endNode, endOffset)) : new goog$dom$browserrange$W3cRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNodes_(startNode, startOffset, endNode, endOffset)) +};var goog$dom$TextRange = function() { +}; +goog$inherits(goog$dom$TextRange, goog$dom$AbstractRange); +var goog$dom$TextRange$createFromBrowserRangeWrapper_ = function(browserRange, opt_isReversed) { + var range = new goog$dom$TextRange; + range.browserRangeWrapper_ = browserRange; + range.isReversed_ = !!opt_isReversed; + return range +}, goog$dom$TextRange$createFromNodeContents = function(node, opt_isReversed) { + return goog$dom$TextRange$createFromBrowserRangeWrapper_(goog$userAgent$IE ? goog$dom$browserrange$IeRange$createFromNodeContents(node) : goog$userAgent$WEBKIT ? new goog$dom$browserrange$WebKitRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)) : goog$userAgent$GECKO ? new goog$dom$browserrange$GeckoRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)) : new goog$dom$browserrange$W3cRange(goog$dom$browserrange$W3cRange$getBrowserRangeForNode(node)), opt_isReversed) +}, goog$dom$TextRange$createFromNodes = function(anchorNode, anchorOffset, focusNode, focusOffset) { + var range = new goog$dom$TextRange; + range.isReversed_ = goog$dom$Range$isReversed(anchorNode, anchorOffset, focusNode, focusOffset); + if(anchorNode.tagName == "BR") { + var parent = anchorNode.parentNode; + anchorOffset = goog$array$indexOf(parent.childNodes, anchorNode); + anchorNode = parent + }if(focusNode.tagName == "BR") { + parent = focusNode.parentNode; + focusOffset = goog$array$indexOf(parent.childNodes, focusNode); + focusNode = parent + }if(range.isReversed_) { + range.startNode_ = focusNode; + range.startOffset_ = focusOffset; + range.endNode_ = anchorNode; + range.endOffset_ = anchorOffset + }else { + range.startNode_ = anchorNode; + range.startOffset_ = anchorOffset; + range.endNode_ = focusNode; + range.endOffset_ = focusOffset + }return range +}; +goog$dom$TextRange.prototype.browserRangeWrapper_ = null; +goog$dom$TextRange.prototype.startNode_ = null; +goog$dom$TextRange.prototype.startOffset_ = null; +goog$dom$TextRange.prototype.endNode_ = null; +goog$dom$TextRange.prototype.endOffset_ = null; +goog$dom$TextRange.prototype.isReversed_ = false; +goog$dom$TextRange.prototype.getType = function() { + return"text" +}; +goog$dom$TextRange.prototype.getBrowserRangeObject = function() { + return this.getBrowserRangeWrapper_().range_ +}; +goog$dom$TextRange.prototype.clearCachedValues_ = function() { + this.startNode_ = this.startOffset_ = this.endNode_ = this.endOffset_ = null +}; +goog$dom$TextRange.prototype.getTextRangeCount = function() { + return 1 +}; +goog$dom$TextRange.prototype.getTextRange = function() { + return this +}; +goog$dom$TextRange.prototype.getBrowserRangeWrapper_ = function() { + return this.browserRangeWrapper_ || (this.browserRangeWrapper_ = goog$dom$browserrange$createRangeFromNodes(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset())) +}; +goog$dom$TextRange.prototype.getContainer = function() { + return this.getBrowserRangeWrapper_().getContainer() +}; +goog$dom$TextRange.prototype.getStartNode = function() { + return this.startNode_ || (this.startNode_ = this.getBrowserRangeWrapper_().getStartNode()) +}; +goog$dom$TextRange.prototype.getStartOffset = function() { + return this.startOffset_ != null ? this.startOffset_ : (this.startOffset_ = this.getBrowserRangeWrapper_().getStartOffset()) +}; +goog$dom$TextRange.prototype.getEndNode = function() { + return this.endNode_ || (this.endNode_ = this.getBrowserRangeWrapper_().getEndNode()) +}; +goog$dom$TextRange.prototype.getEndOffset = function() { + return this.endOffset_ != null ? this.endOffset_ : (this.endOffset_ = this.getBrowserRangeWrapper_().getEndOffset()) +}; +goog$dom$TextRange.prototype.isReversed = function() { + return this.isReversed_ +}; +goog$dom$TextRange.prototype.containsRange = function(otherRange, opt_allowPartial) { + var otherRangeType = otherRange.getType(); + if(otherRangeType == "text")return this.getBrowserRangeWrapper_().containsRange(otherRange.getBrowserRangeWrapper_(), opt_allowPartial); + else if(otherRangeType == "control") { + var elements = otherRange.getElements(), fn = opt_allowPartial ? goog$array$some : goog$array$every; + return fn(elements, function(el) { + return this.containsNode(el, opt_allowPartial) + }, this) + } +}; +goog$dom$TextRange.prototype.isCollapsed = function() { + return this.getBrowserRangeWrapper_().isCollapsed() +}; +goog$dom$TextRange.prototype.__iterator__ = function() { + return new goog$dom$TextRangeIterator(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset()) +}; +goog$dom$TextRange.prototype.select = function() { + this.getBrowserRangeWrapper_().select(this.isReversed_) +}; +goog$dom$TextRange.prototype.saveUsingDom = function() { + return new goog$dom$DomSavedTextRange_(this) +}; +goog$dom$TextRange.prototype.collapse = function(toAnchor) { + var toStart = this.isReversed() ? !toAnchor : toAnchor; + this.browserRangeWrapper_ && this.browserRangeWrapper_.collapse(toStart); + if(toStart) { + this.endNode_ = this.startNode_; + this.endOffset_ = this.startOffset_ + }else { + this.startNode_ = this.endNode_; + this.startOffset_ = this.endOffset_ + }this.isReversed_ = false +}; +var goog$dom$DomSavedTextRange_ = function(range) { + this.anchorNode_ = range.getAnchorNode(); + this.anchorOffset_ = range.getAnchorOffset(); + this.focusNode_ = range.getFocusNode(); + this.focusOffset_ = range.getFocusOffset() +}; +goog$inherits(goog$dom$DomSavedTextRange_, goog$dom$SavedRange);var goog$dom$ControlRange = function() { +}; +goog$inherits(goog$dom$ControlRange, goog$dom$AbstractMultiRange); +goog$dom$ControlRange.prototype.range_ = null; +goog$dom$ControlRange.prototype.elements_ = null; +goog$dom$ControlRange.prototype.sortedElements_ = null; +goog$dom$ControlRange.prototype.clearCachedValues_ = function() { + this.sortedElements_ = this.elements_ = null +}; +goog$dom$ControlRange.prototype.getType = function() { + return"control" +}; +goog$dom$ControlRange.prototype.getBrowserRangeObject = function() { + return this.range_ || document.body.createControlRange() +}; +goog$dom$ControlRange.prototype.getTextRangeCount = function() { + return this.range_ ? this.range_.length : 0 +}; +goog$dom$ControlRange.prototype.getTextRange = function(i) { + return goog$dom$TextRange$createFromNodeContents(this.range_.item(i)) +}; +goog$dom$ControlRange.prototype.getContainer = function() { + return goog$dom$findCommonAncestor.apply(null, this.getElements()) +}; +goog$dom$ControlRange.prototype.getStartNode = function() { + return this.getSortedElements()[0] +}; +goog$dom$ControlRange.prototype.getStartOffset = function() { + return 0 +}; +goog$dom$ControlRange.prototype.getEndNode = function() { + var sorted = this.getSortedElements(), startsLast = goog$array$peek(sorted); + return goog$array$find(sorted, function(el) { + return goog$dom$contains(el, startsLast) + }) +}; +goog$dom$ControlRange.prototype.getEndOffset = function() { + return this.getEndNode().childNodes.length +}; +goog$dom$ControlRange.prototype.getElements = function() { + if(!this.elements_) { + this.elements_ = []; + if(this.range_)for(var i = 0;i < this.range_.length;i++)this.elements_.push(this.range_.item(i)) + }return this.elements_ +}; +goog$dom$ControlRange.prototype.getSortedElements = function() { + if(!this.sortedElements_) { + this.sortedElements_ = this.getElements().concat(); + this.sortedElements_.sort(function(a, b) { + return a.sourceIndex - b.sourceIndex + }) + }return this.sortedElements_ +}; +goog$dom$ControlRange.prototype.isCollapsed = function() { + return!this.range_ || !this.range_.length +}; +goog$dom$ControlRange.prototype.__iterator__ = function() { + return new goog$dom$ControlRangeIterator(this) +}; +goog$dom$ControlRange.prototype.select = function() { + this.range_ && this.range_.select() +}; +goog$dom$ControlRange.prototype.saveUsingDom = function() { + return new goog$dom$DomSavedControlRange_(this) +}; +goog$dom$ControlRange.prototype.collapse = function() { + this.range_ = null; + this.clearCachedValues_() +}; +var goog$dom$DomSavedControlRange_ = function(range) { + this.elements_ = range.getElements() +}; +goog$inherits(goog$dom$DomSavedControlRange_, goog$dom$SavedRange); +var goog$dom$ControlRangeIterator = function(range) { + if(range) { + this.elements_ = range.getSortedElements(); + this.startNode_ = this.elements_.shift(); + this.endNode_ = goog$array$peek(this.elements_) || this.startNode_ + }goog$dom$RangeIterator.call(this, this.startNode_, false) +}; +goog$inherits(goog$dom$ControlRangeIterator, goog$dom$RangeIterator); +goog$dom$ControlRangeIterator.prototype.startNode_ = null; +goog$dom$ControlRangeIterator.prototype.endNode_ = null; +goog$dom$ControlRangeIterator.prototype.elements_ = null; +goog$dom$ControlRangeIterator.prototype.getStartNode = function() { + return this.startNode_ +}; +goog$dom$ControlRangeIterator.prototype.getEndNode = function() { + return this.endNode_ +}; +goog$dom$ControlRangeIterator.prototype.isLast = function() { + return!this.depth && !this.elements_.length +}; +goog$dom$ControlRangeIterator.prototype.next = function() { + if(this.isLast())throw goog$iter$StopIteration;else if(!this.depth) { + var el = this.elements_.shift(); + this.setPosition(el, 1, 1); + return el + }return goog$dom$ControlRangeIterator.superClass_.next.call(this) +};var goog$dom$MultiRange = function() { + this.browserRanges_ = []; + this.ranges_ = []; + this.container_ = this.sortedRanges_ = null +}; +goog$inherits(goog$dom$MultiRange, goog$dom$AbstractMultiRange); +goog$dom$MultiRange.prototype.logger_ = goog$debug$LogManager$getLogger("goog.dom.MultiRange"); +goog$dom$MultiRange.prototype.clearCachedValues_ = function() { + this.ranges_ = []; + this.container_ = this.sortedRanges_ = null +}; +goog$dom$MultiRange.prototype.getType = function() { + return"mutli" +}; +goog$dom$MultiRange.prototype.getBrowserRangeObject = function() { + this.browserRanges_.length > 1 && this.logger_.warning("getBrowserRangeObject called on MultiRange with more than 1 range"); + return this.browserRanges_[0] +}; +goog$dom$MultiRange.prototype.getTextRangeCount = function() { + return this.browserRanges_.length +}; +goog$dom$MultiRange.prototype.getTextRange = function(i) { + this.ranges_[i] || (this.ranges_[i] = goog$dom$TextRange$createFromBrowserRangeWrapper_(goog$userAgent$IE ? new goog$dom$browserrange$IeRange(this.browserRanges_[i], goog$dom$getOwnerDocument(this.browserRanges_[i].parentElement())) : goog$userAgent$WEBKIT ? new goog$dom$browserrange$WebKitRange(this.browserRanges_[i]) : goog$userAgent$GECKO ? new goog$dom$browserrange$GeckoRange(this.browserRanges_[i]) : new goog$dom$browserrange$W3cRange(this.browserRanges_[i]), undefined)); + return this.ranges_[i] +}; +goog$dom$MultiRange.prototype.getContainer = function() { + if(!this.container_) { + for(var nodes = [], i = 0, len = this.getTextRangeCount();i < len;i++)nodes.push(this.getTextRange(i).getContainer()); + this.container_ = goog$dom$findCommonAncestor.apply(null, nodes) + }return this.container_ +}; +goog$dom$MultiRange.prototype.getSortedRanges = function() { + if(!this.sortedRanges_) { + this.sortedRanges_ = this.getTextRanges(); + this.sortedRanges_.sort(function(a, b) { + var aStartNode = a.getStartNode(), aStartOffset = a.getStartOffset(), bStartNode = b.getStartNode(), bStartOffset = b.getStartOffset(); + if(aStartNode == bStartNode && aStartOffset == bStartOffset)return 0; + return goog$dom$Range$isReversed(aStartNode, aStartOffset, bStartNode, bStartOffset) ? 1 : -1 + }) + }return this.sortedRanges_ +}; +goog$dom$MultiRange.prototype.getStartNode = function() { + return this.getSortedRanges()[0].getStartNode() +}; +goog$dom$MultiRange.prototype.getStartOffset = function() { + return this.getSortedRanges()[0].getStartOffset() +}; +goog$dom$MultiRange.prototype.getEndNode = function() { + return goog$array$peek(this.getSortedRanges()).getEndNode() +}; +goog$dom$MultiRange.prototype.getEndOffset = function() { + return goog$array$peek(this.getSortedRanges()).getEndOffset() +}; +goog$dom$MultiRange.prototype.isCollapsed = function() { + return this.browserRanges_.length == 0 || this.browserRanges_.length == 1 && this.getTextRange(0).isCollapsed() +}; +goog$dom$MultiRange.prototype.__iterator__ = function() { + return new goog$dom$MultiRangeIterator(this) +}; +goog$dom$MultiRange.prototype.select = function() { + var selection; + JSCompiler_inline_label_goog$dom$AbstractRange$getBrowserSelectionForWindow_50: { + var JSCompiler_inline_win = this.getWindow(); + if(JSCompiler_inline_win.getSelection)selection = JSCompiler_inline_win.getSelection(); + else { + var JSCompiler_inline_doc = JSCompiler_inline_win.document; + selection = JSCompiler_inline_doc.selection || JSCompiler_inline_doc.getSelection && JSCompiler_inline_doc.getSelection() + } + }selection.removeAllRanges(); + for(var i = 0, len = this.getTextRangeCount();i < len;i++)selection.addRange(this.getTextRange(i).getBrowserRangeObject()) +}; +goog$dom$MultiRange.prototype.saveUsingDom = function() { + return new goog$dom$DomSavedMultiRange_(this) +}; +goog$dom$MultiRange.prototype.collapse = function(toAnchor) { + if(!this.isCollapsed()) { + var range = toAnchor ? this.getTextRange(0) : this.getTextRange(this.getTextRangeCount() - 1); + this.clearCachedValues_(); + range.collapse(toAnchor); + this.ranges_ = [range]; + this.sortedRanges_ = [range]; + this.browserRanges_ = [range.getBrowserRangeObject()] + } +}; +var goog$dom$DomSavedMultiRange_ = function(range) { + this.savedRanges_ = goog$array$map(range.getTextRanges(), function(range) { + return range.saveUsingDom() + }) +}; +goog$inherits(goog$dom$DomSavedMultiRange_, goog$dom$SavedRange); +var goog$dom$MultiRangeIterator = function(range) { + if(range) { + this.ranges_ = range.getSortedRanges(); + if(this.ranges_.length) { + this.startNode_ = this.ranges_[0].getStartNode(); + this.endNode_ = goog$array$peek(this.ranges_).getEndNode() + } + }goog$dom$RangeIterator.call(this, this.startNode_, false) +}; +goog$inherits(goog$dom$MultiRangeIterator, goog$dom$RangeIterator); +goog$dom$MultiRangeIterator.prototype.startNode_ = null; +goog$dom$MultiRangeIterator.prototype.endNode_ = null; +goog$dom$MultiRangeIterator.prototype.ranges_ = null; +goog$dom$MultiRangeIterator.prototype.getStartNode = function() { + return this.startNode_ +}; +goog$dom$MultiRangeIterator.prototype.getEndNode = function() { + return this.endNode_ +}; +goog$dom$MultiRangeIterator.prototype.isLast = function() { + return this.ranges_.length == 1 && this.ranges_[0].isLast() +}; +goog$dom$MultiRangeIterator.prototype.next = function() { + do try { + this.ranges_[0].next(); + break + }catch(ex) { + if(ex != goog$iter$StopIteration)throw ex;this.ranges_.shift() + }while(this.ranges_.length); + if(this.ranges_.length) { + var range = this.ranges_[0]; + this.setPosition(range.node, range.tagType, range.depth); + return range.node + }else throw goog$iter$StopIteration; +};var goog$dom$Range$createCaret = function(node, offset) { + return goog$dom$TextRange$createFromNodes(node, offset, node, offset) +}, goog$dom$Range$createFromNodes = function(startNode, startOffset, endNode, endOffset) { + return goog$dom$TextRange$createFromNodes(startNode, startOffset, endNode, endOffset) +}, goog$dom$Range$isReversed = function(anchorNode, anchorOffset, focusNode, focusOffset) { + if(anchorNode == focusNode)return focusOffset < anchorOffset; + var child; + if(anchorNode.nodeType == 1 && anchorOffset)if(child = anchorNode.childNodes[anchorOffset]) { + anchorNode = child; + anchorOffset = 0 + }else if(goog$dom$contains(anchorNode, focusNode))return true; + if(focusNode.nodeType == 1 && focusOffset)if(child = focusNode.childNodes[focusOffset]) { + focusNode = child; + focusOffset = 0 + }else if(goog$dom$contains(focusNode, anchorNode))return false; + return(goog$dom$compareNodeOrder(anchorNode, focusNode) || anchorOffset - focusOffset) > 0 +};window.createCaret = goog$dom$Range$createCaret; +window.createFromNodes = goog$dom$Range$createFromNodes; +try { + goog$dom$Range$createCaret(document.body, 0).select() +}catch(e$$13) { +}; + +/************************************************** + Trace: + 56.427 Start Handling request + 0 56.427 Start Building cUnit + 1 56.428 Done 1 ms Building cUnit + 0 56.428 Start Checking memcacheg + 0 56.428 Start Connecting to memcacheg + 8 56.436 Done 8 ms Connecting to memcacheg + 1 56.437 Done 9 ms Checking memcacheg + 0 56.437 Done 10 ms Handling request +**************************************************/ diff --git a/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html b/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html new file mode 100644 index 0000000000..140f1a2045 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html @@ -0,0 +1,1081 @@ +<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Rich Text Tests</title>
+ <script src="js/range.js"></script>
+ <script>
+ /**
+ * Color class allows cross-browser comparison of values, which can
+ * be returned from queryCommandValue in several formats:
+ * 0xff00ff
+ * rgb(255, 0, 0)
+ * Number containing the hex value
+ */
+ function Color(value) {
+ this.compare = function(other) {
+ if (!this.valid || !other.valid) {
+ return false;
+ }
+ return this.red == other.red && this.green == other.green && this.blue == other.blue;
+ }
+ this.parse = function(value) {
+ var hexMatch = String(value).match(/#([0-9a-f]{6})/i);
+ if (hexMatch) {
+ this.red = parseInt(hexMatch[1].substring(0, 2), 16);
+ this.green = parseInt(hexMatch[1].substring(2, 4), 16);
+ this.blue = parseInt(hexMatch[1].substring(4, 6), 16);
+ return true;
+ }
+ var rgbMatch = String(value).match(/rgb\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/i);
+ if (rgbMatch) {
+ this.red = Number(rgbMatch[1]);
+ this.green = Number(rgbMatch[2]);
+ this.blue = Number(rgbMatch[3]);
+ return true;
+ }
+ if (Number(value)) {
+ this.red = value & 0xFF;
+ this.green = (value & 0xFF00) >> 8;
+ this.blue = (value & 0xFF0000) >> 16;
+ return true;
+ }
+ return false;
+ }
+ this.toString = function() {
+ return this.red + ',' + this.green + ',' + this.blue;
+ }
+ this.valid = this.parse(value);
+ }
+
+ /**
+ * Utility class for converting font sizes to the size
+ * attribute in a font tag. Currently only converts px because
+ * only the sizes and px ever come from queryCommandValue.
+ */
+ function Size(value) {
+ var pxMatch = String(value).match(/([0-9]+)px/);
+ if (pxMatch) {
+ var px = Number(pxMatch[1]);
+ if (px <= 10) {
+ this.size = 1;
+ } else if (px <= 13) {
+ this.size = 2;
+ } else if (px <= 16) {
+ this.size = 3;
+ } else if (px <= 18) {
+ this.size = 4;
+ } else if (px <= 24) {
+ this.size = 5;
+ } else if (px <= 32) {
+ this.size = 6;
+ } else if (px <= 47) {
+ this.size = 7;
+ } else {
+ this.size = NaN;
+ }
+ } else if (Number(value)) {
+ this.size = Number(value);
+ } else {
+ switch (value) {
+ case 'x-small':
+ this.size = 1;
+ break;
+ case 'small':
+ this.size = 2;
+ break;
+ case 'medium':
+ this.size = 3;
+ break;
+ case 'large':
+ this.size = 4;
+ break;
+ case 'x-large':
+ this.size = 5;
+ break;
+ case 'xx-large':
+ this.size = 6;
+ break;
+ case 'xxx-large':
+ case '-webkit-xxx-large':
+ this.size = 7;
+ break;
+ default:
+ this.size = null;
+ }
+ }
+ this.compare = function(other) {
+ return this.size == other.size;
+ }
+ this.toString = function() {
+ return String(this.size);
+ }
+ }
+
+ var IMAGE_URI = '/tests/editor/libeditor/tests/green.png';
+
+ var APPLY_TESTS = {
+ 'backcolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'background-color'},
+ 'bold' : {
+ opt_arg: null,
+ styleWithCSS: 'font-weight'},
+ 'createbookmark' : {
+ opt_arg: 'bookmark_name'},
+ 'createlink' : {
+ opt_arg: 'http://www.openweb.org'},
+ 'decreasefontsize' : {
+ opt_arg: null},
+ 'fontname' : {
+ opt_arg: 'Arial',
+ styleWithCSS: 'font-family'},
+ 'fontsize' : {
+ opt_arg: 4,
+ styleWithCSS: 'font-size'},
+ 'forecolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'color'},
+ 'formatblock' : {
+ opt_arg: 'h1',
+ wholeline: true},
+ 'hilitecolor' : {
+ opt_arg: '#FF0000',
+ styleWithCSS: 'background-color'},
+ 'indent' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'margin'},
+ 'inserthorizontalrule' : {
+ opt_arg: null,
+ collapse: true},
+ 'inserthtml': {
+ opt_arg: '<br>',
+ collapse: true},
+ 'insertimage': {
+ opt_arg: IMAGE_URI,
+ collapse: true},
+ 'insertorderedlist' : {
+ opt_arg: null,
+ wholeline: true},
+ 'insertunorderedlist' : {
+ opt_arg: null,
+ wholeline: true},
+ 'italic' : {
+ opt_arg: null,
+ styleWithCSS: 'font-style'},
+ 'justifycenter' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyfull' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyleft' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'justifyright' : {
+ opt_arg: null,
+ wholeline: true,
+ styleWithCSS: 'text-align'},
+ 'strikethrough' : {
+ opt_arg: null,
+ styleWithCSS: 'text-decoration'},
+ 'subscript' : {
+ opt_arg: null,
+ styleWithCSS: 'vertical-align'},
+ 'superscript' : {
+ opt_arg: null,
+ styleWithCSS: 'vertical-align'},
+ 'underline' : {
+ opt_arg: null,
+ styleWithCSS: 'text-decoration'}};
+
+ var UNAPPLY_TESTS = {
+ 'bold' : {
+ tags: [
+ ['<b>', '</b>'],
+ ['<STRONG>', '</STRONG>'],
+ ['<span style="font-weight: bold;">', '</span>']]},
+ 'italic' : {
+ tags: [
+ ['<i>', '</i>'],
+ ['<EM>', '</EM>'],
+ ['<span style="font-style: italic;">', '</span>']]},
+ 'outdent' : {
+ unapply: 'indent',
+ block: true,
+ tags: [
+ ['<blockquote>', '</blockquote>'],
+ ['<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;">', '</blockquote>'],
+ ['<ul><li>', '</li></ul>'],
+ ['<ol><li>', '</li></ol>'],
+ ['<div style="margin-left: 40px;">', '</div>']]},
+ 'removeformat' : {
+ unapply: '*',
+ block: true,
+ tags: [
+ ['<b>', '</b>'],
+ ['<a href="http://www.foo.com">', '</a>'],
+ ['<table><tr><td>', '</td></tr></table>']]},
+ 'strikethrough' : {
+ tags: [
+ ['<strike>', '</strike>'],
+ ['<s>', '</s>'],
+ ['<del>', '</del>'],
+ ['<span style="text-decoration: line-through;">', '</span>']]},
+ 'subscript' : {
+ tags: [
+ ['<sub>', '</sub>'],
+ ['<span style="vertical-align: sub;">', '</span>']]},
+ 'superscript' : {
+ tags: [
+ ['<sup>', '</sup>'],
+ ['<span style="vertical-align: super;">', '</span>']]},
+ 'unbookmark' : {
+ unapply: 'createbookmark',
+ tags: [
+ ['<a name="bookmark">', '</a>']]},
+ 'underline' : {
+ tags: [
+ ['<u>', '</u>'],
+ ['<span style="text-decoration: underline;">', '</span>']]},
+ 'unlink' : {
+ unapply: 'createbookmark',
+ tags: [
+ ['<a href="http://www.foo.com">', '</a>']]}};
+
+ var QUERY_TESTS = {
+ 'backcolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', expected: new Color('#ffccaa')},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'bold' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<b>foo bar baz</b>', expected: true},
+ {html: '<STRONG>foo bar baz</STRONG>', expected: true},
+ {html: '<span style="font-weight:bold">foo bar baz</span>', expected: true},
+ {html: '<b style="font-weight:normal">foo bar baz</b>', expected: false},
+ {html: '<b><span style="font-weight:normal;">foo bar baz</span>', expected: false}
+ ]
+ },
+ 'fontname' : {
+ type: 'value',
+ tests: [
+ {html: '<font face="Arial">foo bar baz</font>', expected: 'Arial'},
+ {html: '<span style="font-family:Arial">foo bar baz</span>', expected: 'Arial'},
+ {html: '<font face="Arial" style="font-family:Courier">foo bar baz</font>', expected: 'Courier'},
+ {html: '<font face="Courier"><font face="Arial">foo bar baz</font></font>', expected: 'Arial'},
+ {html: '<span style="font-family:Courier"><font face="Arial">foo bar baz</font></span>', expected: 'Arial'}
+ ]
+ },
+ 'fontsize' : {
+ type: 'value',
+ tests: [
+ {html: '<font size=4>foo bar baz</font>', expected: new Size(4)},
+ // IE adds +1 to font size from font-size style attributes.
+ // This is hard to correct for since it does NOT add +1 to size attribute from font tag.
+ {html: '<span class="Apple-style-span" style="font-size: large;">foo bar baz</span>', expected: new Size(4)},
+ {html: '<font size=1 style="font-size:x-large;">foo bar baz</font>', expected: new Size(5)}
+ ]
+ },
+ 'forecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<font color="#ff0000">foo bar baz</font>', expected: new Color('#ff0000')},
+ {html: '<span style="color:#ff0000">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<font color="#0000ff" style="color:#ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'hilitecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', expected: new Color('#ffccaa')},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', expected: new Color('#ff0000')},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', expected: new Color('#ff0000')}
+ ]
+ },
+ 'insertorderedlist' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<ol><li>foo bar baz</li></ol>', expected: true},
+ {html: '<ul><li>foo bar baz</li></ul>', expected: false}
+ ]
+ },
+ 'insertunorderedlist' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<ol><li>foo bar baz</li></ol>', expected: false},
+ {html: '<ul><li>foo bar baz</li></ul>', expected: true}
+ ]
+ },
+ 'italic' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<i>foo bar baz</i>', expected: true},
+ {html: '<EM>foo bar baz</EM>', expected: true},
+ {html: '<span style="font-style:italic">foo bar baz</span>', expected: true},
+ {html: '<i><span style="font-style:normal">foo bar baz</span></i>', expected: false}
+ ]
+ },
+ 'justifycenter' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="center">foo bar baz</div>', expected: true},
+ {html: '<p align="center">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: center;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyfull' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="justify">foo bar baz</div>', expected: true},
+ {html: '<p align="justify">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: justify;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyleft' : {
+ type: 'state',
+ tests: [
+ {html: '<div align="left">foo bar baz</div>', expected: true},
+ {html: '<p align="left">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: left;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'justifyright' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<div align="right">foo bar baz</div>', expected: true},
+ {html: '<p align="right">foo bar baz</p>', expected: true},
+ {html: '<div style="text-align: right;">foo bar baz</div>', expected: true}
+ ]
+ },
+ 'strikethrough' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<strike>foo bar baz</strike>', expected: true},
+ {html: '<strike style="text-decoration: none">foo bar baz</strike>', expected: false},
+ {html: '<s>foo bar baz</s>', expected: true},
+ {html: '<del>foo bar baz</del>', expected: true},
+ {html: '<span style="text-decoration:line-through">foo bar baz</span>', expected: true}
+ ]
+ },
+ 'subscript' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<sub>foo bar baz</sub>', expected: true}
+ ]
+ },
+ 'superscript' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<sup>foo bar baz</sup>', expected: true}
+ ]
+ },
+ 'underline' : {
+ type: 'state',
+ tests: [
+ {html: 'foo bar baz', expected: false},
+ {html: '<u>foo bar baz</u>', expected: true},
+ {html: '<a href="http://www.foo.com">foo bar baz</a>', expected: true},
+ {html: '<span style="text-decoration:underline">foo bar baz</span>', expected: true},
+ {html: '<u style="text-decoration:none">foo bar baz</u>', expected: false},
+ {html: '<a style="text-decoration:none" href="http://www.foo.com">foo bar baz</a>', expected: false}
+ ]
+ }
+ };
+
+ var CHANGE_TESTS = {
+ 'backcolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', opt_arg: '#884422'},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', opt_arg: '#0000ff'},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', opt_arg: '#0000ff'}
+ ]
+ },
+ 'fontname' : {
+ type: 'value',
+ tests: [
+ {html: '<font face="Arial">foo bar baz</font>', opt_arg: 'Courier'},
+ {html: '<span style="font-family:Arial">foo bar baz</span>', opt_arg: 'Courier'},
+ {html: '<font face="Arial" style="font-family:Verdana">foo bar baz</font>', opt_arg: 'Courier'},
+ {html: '<font face="Verdana"><font face="Arial">foo bar baz</font></font>', opt_arg: 'Courier'},
+ {html: '<span style="font-family:Verdana"><font face="Arial">foo bar baz</font></span>', opt_arg: 'Courier'}
+ ]
+ },
+ 'fontsize' : {
+ type: 'value',
+ tests: [
+ {html: '<font size=4>foo bar baz</font>', opt_arg: 1},
+ {html: '<span class="Apple-style-span" style="font-size: large;">foo bar baz</span>', opt_arg: 1},
+ {html: '<font size=1 style="font-size:x-small;">foo bar baz</font>', opt_arg: 5}
+ ]
+ },
+ 'forecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<font color="#ff0000">foo bar baz</font>', opt_arg: '#00ff00'},
+ {html: '<span style="color:#ff0000">foo bar baz</span>', opt_arg: '#00ff00'},
+ {html: '<font color="#0000ff" style="color:#ff0000">foo bar baz</span>', opt_arg: '#00ff00'}
+ ]
+ },
+ 'hilitecolor' : {
+ type: 'value',
+ tests: [
+ {html: '<FONT style="BACKGROUND-COLOR: #ffccaa">foo bar baz</FONT>', opt_arg: '#884422'},
+ {html: '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">foo bar baz</span>', opt_arg: '#00ff00'},
+ {html: '<span style="background-color: #ff0000">foo bar baz</span>', opt_arg: '#00ff00'}
+ ]
+ }
+ };
+
+ /** The document of the editable iframe */
+ var editorDoc = null;
+ /** Dummy text to apply and unapply formatting to */
+ var TEST_CONTENT = 'foo bar baz';
+ /**
+ * Word in dummy text that should change. Formatting is sometimes applied
+ * to a single word instead of the entire text node because sometimes a
+ * style might get applied to the body node instead of wrapped around
+ * the text, and that's not what's being tested.
+ */
+ var TEST_WORD = 'bar';
+ /** Constant for indicating an action is unsupported (threw exception) */
+ var UNSUPPORTED = 'UNSUPPORTED';
+ /** <br> and <p> are acceptable HTML to be left over from block elements */
+ var BLOCK_REMOVE_TAGS = [/\s*<br>\s*/i, /\s*<p>\s*/i];
+ /** Array used to accumulate test results */
+ // Tack on the actual display tests with bogus data
+ // otherwise the beacon will fail.
+ var results = ['apply=0', 'unapply=0', 'change=0', 'query=0'];
+
+ /**
+ *
+ */
+ function resetIframe(newHtml) {
+ // These attributes can get set on the iframe by some errant execCommands
+ editorDoc.body.setAttribute('style', '');
+ editorDoc.body.setAttribute('bgcolor', '');
+ editorDoc.body.innerHTML = newHtml;
+ }
+
+ /**
+ * Finds the text node in the given node containing the given word.
+ * Returns null if not found.
+ */
+ function findTextNode(word, node) {
+ if (node.nodeType == 3) {
+ // Text node, check value.
+ if (node.data.includes(word)) {
+ return node;
+ }
+ } else if (node.nodeType == 1) {
+ // Element node, check children.
+ for (var i = 0; i < node.childNodes.length; i++) {
+ var result = findTextNode(word, node.childNodes[i]);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the selection to be collapsed at the start of the word,
+ * or the start of the editor if no word is passed in.
+ */
+ function selectStart(word) {
+ var textNode = findTextNode(word || '', editorDoc.body);
+ var startOffset = 0;
+ if (word) {
+ startOffset = textNode.data.indexOf(word);
+ }
+ var range = createCaret(textNode, startOffset);
+ range.select();
+ }
+
+ /**
+ * Selects the given word in the editor iframe.
+ */
+ function selectWord(word) {
+ var textNode = findTextNode(word, editorDoc.body);
+ if (!textNode) {
+ return;
+ }
+ var start = textNode.data.indexOf(word);
+ var range = createFromNodes(textNode, start, textNode, start + word.length);
+ range.select();
+ }
+
+ /**
+ * Gets the HTML before the text, so that we know how the browser
+ * applied a style
+ */
+ function getSurroundingTags(text) {
+ var html = editorDoc.body.innerHTML;
+ var tagStart = html.indexOf('<');
+ var index = editorDoc.body.innerHTML.indexOf(text);
+ if (tagStart == -1 || index == -1) {
+ return '';
+ }
+ return editorDoc.body.innerHTML.substring(tagStart, index);
+ }
+
+ /**
+ * Does the test for an apply execCommand.
+ */
+ function doApplyTest(command, styleWithCSS) {
+ try {
+ // Set styleWithCSS
+ try {
+ editorDoc.execCommand('styleWithCSS', false, styleWithCSS);
+ } catch (ex) {
+ // Ignore errors
+ }
+ resetIframe(TEST_CONTENT);
+ if (APPLY_TESTS[command].collapse) {
+ selectStart(TEST_WORD);
+ } else {
+ selectWord(TEST_WORD);
+ }
+ try {
+ editorDoc.execCommand(command, false, APPLY_TESTS[command].opt_arg);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ return getSurroundingTags(APPLY_TESTS[command].wholeline? TEST_CONTENT : TEST_WORD);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Outputs the result of the apply command to a table.
+ * @return {boolean} success
+ */
+ function outputApplyResult(command, result, styleWithCSS) {
+ // The apply command "succeeded" if HTML was generated.
+ var success = (result != UNSUPPORTED) && result;
+ // Except for styleWithCSS commands, which only succeed if the
+ // expected style was applied.
+ if (styleWithCSS) {
+ success = result && result.toLowerCase().includes(APPLY_TESTS[command].styleWithCSS);
+ }
+ results.push('a-' + command + '-' + (styleWithCSS ? 1 : 0) + '=' + (success ? '1' : '0'));
+
+ // Each command is displayed as a table row with 3 columns
+ var tr = document.createElement('TR');
+ tr.className = success ? 'success' : 'fail';
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: styleWithCSS
+ var td = document.createElement('TD');
+ td.innerHTML = styleWithCSS ? 'true' : 'false';
+ tr.appendChild(td);
+
+ // Column 3: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 4: generated HTML (for passing commands)
+ td = document.createElement('TD');
+ // Escape the HTML in the result for printing.
+ result = result.replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = result;
+ tr.appendChild(td);
+ var table = document.getElementById('apply_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ /**
+ * Does the test for an unapply execCommand.
+ */
+ function doUnapplyTest(command, index) {
+ try {
+ var wordStart = TEST_CONTENT.indexOf(TEST_WORD);
+ resetIframe(
+ TEST_CONTENT.substring(0, wordStart) +
+ UNAPPLY_TESTS[command].tags[index][0] +
+ TEST_WORD +
+ UNAPPLY_TESTS[command].tags[index][1] +
+ TEST_CONTENT.substring(wordStart + TEST_WORD.length));
+ selectWord(TEST_WORD);
+ try {
+ editorDoc.execCommand(command, false, UNAPPLY_TESTS[command].opt_arg || null);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ return getSurroundingTags(TEST_WORD);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Check if the given unapply execCommand succeeded. It succeeded if
+ * the following conditions are true:
+ * - The execCommand did not throw an exception
+ * - One of the following:
+ * - The html was removed after the execCommand
+ * - The html was block and the html was replaced with <p> or <br>
+ */
+ function unapplyCommandSucceeded(command, result) {
+ if (result != UNSUPPORTED) {
+ if (!result) {
+ return true;
+ } else if (UNAPPLY_TESTS[command].block) {
+ for (var i = 0; i < BLOCK_REMOVE_TAGS.length; i++) {
+ if (result.match(BLOCK_REMOVE_TAGS[i])) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Outputs the result of the unapply command to a table.
+ * @return {boolean} success
+ */
+ function outputUnapplyResult(command, result, index) {
+ // The apply command "succeeded" if HTML was removed.
+ var success = unapplyCommandSucceeded(command, result);
+ results.push('u-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Each command is displayed as a table row with 5 columns
+ var tr = document.createElement('TR');
+ tr.className = success ? 'success' : 'fail';
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: command name being unapplied
+ var td = document.createElement('TD');
+ td.innerHTML = UNAPPLY_TESTS[command].unapply || command;
+ tr.appendChild(td);
+
+ // Column 3: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 4: html being removed
+ td = document.createElement('TD');
+ // Escape the html for printing.
+ var htmlToRemove = UNAPPLY_TESTS[command].tags[index][0].replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = htmlToRemove;
+ tr.appendChild(td);
+
+ // Column 5: resulting html
+ td = document.createElement('TD');
+ // Escape the HTML in the result for printing.
+ result = result.replace(/\</g, '<').replace(/\>/g, '>');
+ td.innerHTML = success ? ' ' : result;
+ tr.appendChild(td);
+ var table = document.getElementById('unapply_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ /**
+ * Does a queryCommandState or queryCommandValue test for an execCommand.
+ */
+ function doQueryTest(command, index) {
+ try {
+ resetIframe(QUERY_TESTS[command].tests[index].html);
+ selectWord(TEST_WORD);
+ // Dummy val that won't match any expected vals, including false.
+ var result = UNSUPPORTED;
+ if (QUERY_TESTS[command].type == 'state') {
+ try {
+ result = editorDoc.queryCommandState(command);
+ } catch (ex) {
+ result = UNSUPPORTED;
+ }
+ } else {
+ try {
+ // A return value of false indicates the command is not supported.
+ result = editorDoc.queryCommandValue(command) || UNSUPPORTED;
+ } catch (ex) {
+ result = UNSUPPORTED;
+ }
+ }
+ return result;
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Check if the given queryCommandState or queryCommandValue succeeded.
+ */
+ function queryCommandSucceeded(command, index, result) {
+ var expected = QUERY_TESTS[command].tests[index].expected;
+ if (expected instanceof Color) {
+ return expected.compare(new Color(result));
+ } else if (expected instanceof Size) {
+ return expected.compare(new Size(result));
+ } else {
+ return (result == expected);
+ }
+ }
+
+ /**
+ * @return {boolean} success
+ */
+ function outputQueryResult(command, index, result) {
+ // Create table row for results.
+ var tr = document.createElement('TR');
+ var success = queryCommandSucceeded(command, index, result);
+ tr.className = success ? 'success' : 'fail';
+ results.push('q-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: pass/fail
+ td = document.createElement('TD');
+ td.innerHTML = success ? 'PASS' : 'FAIL';
+ tr.appendChild(td);
+
+ // Column 3: test HTML
+ td = document.createElement('TD');
+ var testHtml = QUERY_TESTS[command].tests[index].html.replace(/</g, '<').replace(/>/g, '>');
+ td.innerHTML = testHtml.substring(0, testHtml.indexOf(TEST_CONTENT));
+ tr.appendChild(td);
+
+ // Column 4: Expected result
+ td = document.createElement('TD');
+ td.innerHTML = QUERY_TESTS[command].tests[index].expected;
+ tr.appendChild(td);
+
+ // Column 5: Actual result
+ td = document.createElement('TD');
+ td.innerHTML = result;
+ tr.appendChild(td);
+
+ // Append result to the state or value table, depending on what
+ // type of command this is.
+ var table = document.getElementById(
+ QUERY_TESTS[command].type == 'state' ? 'querystate_output' : 'queryvalue_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ function doChangeTest(command, index) {
+ try {
+ resetIframe(CHANGE_TESTS[command].tests[index].html);
+ selectWord(TEST_CONTENT);
+ try {
+ editorDoc.execCommand(command, false, CHANGE_TESTS[command].tests[index].opt_arg);
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ } catch (ex) {
+ return UNSUPPORTED;
+ }
+ }
+
+ function checkChangeSuccess(command, index) {
+ var textNode = findTextNode(TEST_CONTENT, editorDoc.body);
+ if (!textNode) {
+ // The text has been removed from the document, or split up for no reason.
+ return false;
+ }
+ var expected = null, attributeName = null, styleName = null;
+ switch (command) {
+ case 'backcolor':
+ case 'hilitecolor':
+ expected = new Color(CHANGE_TESTS[command].tests[index].opt_arg);
+ styleName = 'backgroundColor';
+ break;
+ case 'fontname':
+ expected = CHANGE_TESTS[command].tests[index].opt_arg;
+ attributeName = 'face';
+ styleName = 'fontFamily';
+ break;
+ case 'fontsize':
+ expected = new Size(CHANGE_TESTS[command].tests[index].opt_arg);
+ attributeName = 'size';
+ styleName = 'fontSize';
+ break;
+ case 'forecolor':
+ expected = new Color(CHANGE_TESTS[command].tests[index].opt_arg);
+ attributeName = 'color';
+ styleName = 'color';
+ }
+ var foundExpected = false;
+
+ // Loop through all the parent nodes that format the text node,
+ // checking that there is exactly one font attribute or
+ // style, and that it's set correctly.
+ var currentNode = textNode.parentNode;
+ while(currentNode && currentNode.nodeName != 'BODY') {
+ // Check font attribute.
+ if (attributeName && currentNode.nodeName == 'FONT' && currentNode.getAttribute(attributeName)) {
+ var foundAttribute = false;
+ switch(command) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ foundAttribute = new Color(currentNode.getAttribute(attributeName)).compare(expected);
+ break;
+ case 'fontsize':
+ foundAttribute = new Size(currentNode.getAttribute(attributeName)).compare(expected);
+ break;
+ case 'fontname':
+ foundAttribute = (currentNode.getAttribute(attributeName).toLowerCase() == expected.toLowerCase());
+ }
+ if (foundAttribute && foundExpected) {
+ // This is the correct attribute, but the style has been applied
+ // twice. This makes it hard for other browsers to remove the
+ // style.
+ return false;
+ } else if (!foundAttribute) {
+ // This node has an incorrect font attribute.
+ return false;
+ }
+ // The expected font attribute was found.
+ foundExpected = true;
+ }
+ // Check node style.
+ if (currentNode.style[styleName]) {
+ var foundStyle = false;
+ switch(command) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ foundStyle = new Color(currentNode.style[styleName]).compare(expected);
+ break;
+ case 'fontsize':
+ foundStyle = new Size(currentNode.style[styleName]).compare(expected);
+ break;
+ case 'fontname':
+ foundStyle = (currentNode.style[styleName].toLowerCase() == expected.toLowerCase());
+ }
+ if (foundStyle && foundExpected) {
+ // This is the correct style, but the style has been
+ // applied twice. This makes it hard for other browsers to
+ // remove the style.
+ return false;
+ } else if (!foundStyle) {
+ // This node has an incorrect font style.
+ return false;
+ }
+ foundExpected = true;
+ }
+ currentNode = currentNode.parentNode;
+ }
+ return foundExpected;
+ }
+
+ /**
+ * @return {boolean} success
+ */
+ function outputChangeResult(command, index) {
+ // Each command is displayed as a table row with 4 columns
+ var tr = document.createElement('TR');
+ var success = checkChangeSuccess(command, index);
+ tr.className = success ? 'success' : 'fail';
+ results.push('c-' + command + '-' + index + '=' + (success ? '1' : '0'));
+
+ // Column 1: command name
+ var td = document.createElement('TD');
+ td.innerHTML = command;
+ tr.appendChild(td);
+
+ // Column 2: status
+ td = document.createElement('TD');
+ td.innerHTML = (success == null) ? '?' : (success == true ? 'PASS' : 'FAIL');
+ tr.appendChild(td);
+
+ // Column 3: opt_arg
+ td = document.createElement('TD');
+ td.innerHTML = CHANGE_TESTS[command].tests[index].opt_arg;
+ tr.appendChild(td);
+
+ // Column 4: original html
+ td = document.createElement('TD');
+ td.innerHTML = CHANGE_TESTS[command].tests[index].html.replace(/\</g, '<').replace(/\>/g, '>');;
+ tr.appendChild(td);
+
+ // Column 5: resulting html
+ td = document.createElement('TD');
+ td.innerHTML = editorDoc.body.innerHTML.replace(/\</g, '<').replace(/\>/g, '>');;
+ tr.appendChild(td);
+
+ var table = document.getElementById('change_output');
+ table.appendChild(tr);
+ return success;
+ }
+
+ function runTests() {
+ // Wrap initialization code in a try/catch so we can fail gracefully
+ // on older browsers.
+ try {
+ editorDoc = document.getElementById('editor').contentWindow.document;
+ // Default styleWithCSS to false, since it's not supported by IE.
+ try {
+ editorDoc.execCommand('styleWithCSS', false, false);
+ } catch (ex) {
+ // Not supported by IE.
+ }
+ } catch (ex) {}
+
+ // Apply tests
+ var apply_score = 0;
+ var apply_count = 0;
+ var unapply_score= 0;
+ var unapply_count = 0;
+ var change_score = 0;
+ var change_count = 0;
+ var query_score = 0;
+ var query_count = 0;
+ for (var command in APPLY_TESTS) {
+ try {
+ var result = doApplyTest(command, false);
+ var success = outputApplyResult(command, result, false);
+ apply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ apply_count++;
+ if (APPLY_TESTS[command].styleWithCSS) {
+ try {
+ var result = doApplyTest(command, true);
+ var success = outputApplyResult(command, result, true);
+ apply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ apply_count++;
+ }
+ }
+
+ // Unapply tests
+ for (var command in UNAPPLY_TESTS) {
+ for (var i = 0; i < UNAPPLY_TESTS[command].tags.length; i++) {
+ try {
+ var result = doUnapplyTest(command, i);
+ var success = outputUnapplyResult(command, result, i);
+ unapply_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ unapply_count++;
+ }
+ }
+
+ // Query tests
+ for (var command in QUERY_TESTS) {
+ for (var i = 0; i < QUERY_TESTS[command].tests.length; i++) {
+ try {
+ var result = doQueryTest(command, i);
+ var success = outputQueryResult(command, i, result);
+ query_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ query_count++;
+ }
+ }
+
+ // Change tests
+ for (var command in CHANGE_TESTS) {
+ for (var i = 0; i < CHANGE_TESTS[command].tests.length; i++) {
+ try {
+ doChangeTest(command, i);
+ var success = outputChangeResult(command, i);
+ change_score += success ? 1 : 0;
+ } catch (ex) {
+ // An exception is counted as a failed test, don't increment success.
+ }
+ change_count++;
+ }
+ }
+
+ // Beacon all test results.
+ // and construct a shorter version for the results page.
+ try {
+ document.getElementById('apply-score').innerHTML =
+ apply_score + '/' + apply_count;
+ document.getElementById('unapply-score').innerHTML =
+ unapply_score + '/' + unapply_count;
+ document.getElementById('query-score').innerHTML =
+ query_score + '/' + query_count;
+ document.getElementById('change-score').innerHTML =
+ change_score + '/' + change_count;
+ } catch (ex) {}
+ var continueParams = [
+ 'apply=' + apply_score,
+ 'unapply=' + unapply_score,
+ 'query=' + query_score,
+ 'change=' + change_score
+ ];
+ parent.sendScore(results, continueParams);
+ }
+ </script>
+ <style>
+ .success {
+ background-color: #93c47d;
+ }
+ .fail {
+ background-color: #ea9999;
+ }
+ .score {
+ color: #666;
+ }
+ </style>
+</head>
+<body onload="runTests()">
+ <h1>Apply Formatting <span id="apply-score" class="score"></span></h1>
+ <table id="apply"><tbody id="apply_output"><tr><th>Command</th><th>styleWithCSS</th><th>Status</th><th>Output</th></tr></tbody></table>
+ <h1>Unapply Formatting <span id="unapply-score" class="score"></span></h1>
+ <table id="unapply">
+ <thead><tr><th>Command</th><th>Command unapplied</th><th>Status</th><th>HTML Attempted to Unapply</th><th>Resulting HTML</th></tr></thead>
+ <tbody id="unapply_output"></tbody></table>
+ <h1>Query Formatting State <span id="query-score" class="score"></span></h1>
+ <table id="querystate">
+ <thead><tr><th>Command</th><th>Status</th><th>HTML</th><th>Expected</th><th>Actual</th></tr></thead>
+ <tbody id="querystate_output"></tbody></table>
+ <h1>Query Formatting Value </h1>
+ <table id="queryvalue">
+ <thead><tr><th>Command</th><th>Status</th><th>HTML</th><th>Expected</th><th>Actual</th></tr></thead>
+ <tbody id="queryvalue_output"></tbody></table>
+ <h1>Change Formatting <span id="change-score" class="score"></span></h1>
+ <table id="change">
+ <thead><tr><th>Command</th><th>Status</th><th>Argument</th><th>Original HTML</th><th>Resulting HTML</th></tr></thead>
+ <tbody id="change_output"></tbody></table>
+ <iframe name="editor" id="editor" src="editable.html"></iframe>
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream b/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream new file mode 100644 index 0000000000..2071454a85 --- /dev/null +++ b/editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream @@ -0,0 +1,16 @@ +#!/bin/sh + +set -x + +if test -d richtext; then + rm -drf richtext; +fi + +svn checkout http://browserscope.googlecode.com/svn/trunk/categories/richtext/static richtext | tail -1 | sed 's/[^0-9]//g' > current_revision + +find richtext -type d -name .svn -exec rm -drf \{\} \; 2> /dev/null + +hg add current_revision richtext + +hg stat . + |