summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/tests/browserscope/lib
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/tests/browserscope/lib')
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/LICENSE202
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/README58
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/README.Mozilla17
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/currentStatus.js43
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/current_revision1
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/richtext/editable.html11
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/richtext/js/range.js1069
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/richtext/richtext.html1081
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext/update_from_upstream16
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/LICENSE202
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/README58
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/README.Mozilla27
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js1768
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/current_revision1
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/platformFailures.js28
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/__init__.py0
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/common.py25
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/handlers.py107
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/common.css116
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-body.html11
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-dM.html17
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-div.html11
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable.css66
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/canonicalize.js436
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js489
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/output.js456
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/pad.js269
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range-bootstrap.js5
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range.js6184
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/run.js383
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/units.js416
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/variables.js227
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/output.html138
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/richtext2.html107
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/__init__.py17
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/apply.py364
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/applyCSS.py244
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/change.py273
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/changeCSS.py210
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/delete.py330
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/forwarddelete.py315
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/insert.py285
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryEnabled.py215
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryIndeterm.py214
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryState.py575
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/querySupported.py226
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryValue.py429
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/selection.py801
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapply.py462
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapplyCSS.py226
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/richtext2/unittestexample.html103
-rw-r--r--editor/libeditor/tests/browserscope/lib/richtext2/update_from_upstream19
52 files changed, 19353 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_, "&amp;").replace(goog$string$ltRe_, "&lt;").replace(goog$string$gtRe_, "&gt;").replace(goog$string$quotRe_, "&quot;");
+ else {
+ if(!goog$string$allRe_.test(str))return str;
+ if(str.includes("&"))str = str.replace(goog$string$amperRe_, "&amp;");
+ if(str.includes("<"))str = str.replace(goog$string$ltRe_, "&lt;");
+ if(str.includes(">"))str = str.replace(goog$string$gtRe_, "&gt;");
+ if(str.includes('"'))str = str.replace(goog$string$quotRe_, "&quot;");
+ 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, '&lt;').replace(/\>/g, '&gt;');
+ 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, '&lt;').replace(/\>/g, '&gt;');
+ 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, '&lt;').replace(/\>/g, '&gt;');
+ td.innerHTML = success ? '&nbsp;' : 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, '&lt;').replace(/>/g, '&gt;');
+ 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, '&lt;').replace(/\>/g, '&gt;');;
+ tr.appendChild(td);
+
+ // Column 5: resulting html
+ td = document.createElement('TD');
+ td.innerHTML = editorDoc.body.innerHTML.replace(/\</g, '&lt;').replace(/\>/g, '&gt;');;
+ 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 .
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/LICENSE b/editor/libeditor/tests/browserscope/lib/richtext2/LICENSE
new file mode 100644
index 0000000000..57bc88a15a
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/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/richtext2/README b/editor/libeditor/tests/browserscope/lib/richtext2/README
new file mode 100644
index 0000000000..a3bc3110f4
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/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/richtext2/README.Mozilla b/editor/libeditor/tests/browserscope/lib/richtext2/README.Mozilla
new file mode 100644
index 0000000000..7d730874c6
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/README.Mozilla
@@ -0,0 +1,27 @@
+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.
+
+The current status of the test suite needs to be updated whenever an editor
+bug gets fixed, which makes us pass one of the tests. When that happens,
+you should set the UPDATE_TEST_RESULTS constant to true in test_richtext2.html,
+run the test suite, paste the result JSON string in a JSON beautifier (such
+as http://jsbeautifier.org/), and use the result to update currentStatus.js.
+
+As a special case, if there are platform-specific failures, these are instead
+recorded manually in platformFailures.js. (Currently, this applies only to
+tests that are dependent on underlying platform support for the Thai script.)
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js b/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js
new file mode 100644
index 0000000000..e0a4cc0fb0
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js
@@ -0,0 +1,1768 @@
+/**
+ * The current status of the test suite.
+ *
+ * See README.Mozilla for details on how to generate this.
+ */
+const knownFailures = {
+ value: {
+ "A-Proposed-CB:name_TEXT-1_SI-dM": true,
+ "A-Proposed-CB:name_TEXT-1_SI-body": true,
+ "A-Proposed-CB:name_TEXT-1_SI-div": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-dM": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-body": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-div": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-dM": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-body": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-div": true,
+ "A-Proposed-FS:large_TEXT-1_SI-dM": true,
+ "A-Proposed-FS:large_TEXT-1_SI-body": true,
+ "A-Proposed-FS:large_TEXT-1_SI-div": true,
+ "A-Proposed-H:H1_TEXT-1_SC-dM": true,
+ "A-Proposed-H:H1_TEXT-1_SC-body": true,
+ "A-Proposed-H:H1_TEXT-1_SC-div": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-dM": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-body": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-div": true,
+ "AC-Proposed-SUB_TEXT-1_SI-dM": true,
+ "AC-Proposed-SUB_TEXT-1_SI-body": true,
+ "AC-Proposed-SUB_TEXT-1_SI-div": true,
+ "AC-Proposed-SUP_TEXT-1_SI-dM": true,
+ "AC-Proposed-SUP_TEXT-1_SI-body": true,
+ "AC-Proposed-SUP_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-div": true,
+
+ // Those tests expect that <font> elements can be nested, but they don't
+ // match with the other browsers' behavior.
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-dM": true,
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-body": true,
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-div": true,
+
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-dM": true,
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-body": true,
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-div": true,
+
+ // Those tests expect that <font> elements can be nested, but they don't
+ // match with the other browsers' behavior.
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-dM": true,
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-body": true,
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-div": true,
+
+ "C-Proposed-FS:larger_FONTsz:4-dM": true,
+ "C-Proposed-FS:larger_FONTsz:4-body": true,
+ "C-Proposed-FS:larger_FONTsz:4-div": true,
+ "C-Proposed-FS:smaller_FONTsz:4-dM": true,
+ "C-Proposed-FS:smaller_FONTsz:4-body": true,
+ "C-Proposed-FS:smaller_FONTsz:4-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-div": true,
+ "CC-Proposed-I_B-1_SW-dM": true,
+ "CC-Proposed-I_B-1_SW-body": true,
+ "CC-Proposed-I_B-1_SW-div": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-1_SI-dM": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-1_SI-body": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-1_SI-div": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-dM": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-body": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-div": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-dM": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-body": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-div": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-dM": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-body": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-div": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-dM": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-body": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-div": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-dM": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-body": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-div": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-dM": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-body": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-div": true,
+ "U-RFC-UNLINK_A-1_SO-dM": true,
+ "U-RFC-UNLINK_A-1_SO-body": true,
+ "U-RFC-UNLINK_A-1_SO-div": true,
+ "U-RFC-UNLINK_A-1_SW-dM": true,
+ "U-RFC-UNLINK_A-1_SW-body": true,
+ "U-RFC-UNLINK_A-1_SW-div": true,
+ "U-RFC-UNLINK_A-2_SO-dM": true,
+ "U-RFC-UNLINK_A-2_SO-body": true,
+ "U-RFC-UNLINK_A-2_SO-div": true,
+ "U-RFC-UNLINK_A2-1_SO-dM": true,
+ "U-RFC-UNLINK_A2-1_SO-body": true,
+ "U-RFC-UNLINK_A2-1_SO-div": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-dM": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-body": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-div": true,
+ "U-Proposed-B_B-2_SL-dM": true,
+ "U-Proposed-B_B-2_SL-body": true,
+ "U-Proposed-B_B-2_SL-div": true,
+ "U-Proposed-B_B-2_SR-dM": true,
+ "U-Proposed-B_B-2_SR-body": true,
+ "U-Proposed-B_B-2_SR-div": true,
+ "U-Proposed-U_U-S-2_SI-dM": true,
+ "U-Proposed-U_U-S-2_SI-body": true,
+ "U-Proposed-U_U-S-2_SI-div": true,
+ "U-Proposed-S_DEL-1_SW-dM": true,
+ "U-Proposed-S_DEL-1_SW-body": true,
+ "U-Proposed-S_DEL-1_SW-div": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-dM": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-body": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-div": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-dM": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-body": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-div": true,
+ "U-Proposed-UNLINK_A-1_SC-dM": true,
+ "U-Proposed-UNLINK_A-1_SC-body": true,
+ "U-Proposed-UNLINK_A-1_SC-div": true,
+ "U-Proposed-UNLINK_A-1_SI-dM": true,
+ "U-Proposed-UNLINK_A-1_SI-body": true,
+ "U-Proposed-UNLINK_A-1_SI-div": true,
+ "U-Proposed-UNLINK_A-2_SL-dM": true,
+ "U-Proposed-UNLINK_A-2_SL-body": true,
+ "U-Proposed-UNLINK_A-2_SL-div": true,
+ "U-Proposed-UNLINK_A-3_SR-dM": true,
+ "U-Proposed-UNLINK_A-3_SR-body": true,
+ "U-Proposed-UNLINK_A-3_SR-div": true,
+ "U-Proposed-OUTDENT_BQ-1_SW-dM": true,
+ "U-Proposed-OUTDENT_BQ-1_SW-body": true,
+ "U-Proposed-OUTDENT_BQ-1_SW-div": true,
+ "U-Proposed-OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW-dM": true,
+ "U-Proposed-OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW-body": true,
+ "U-Proposed-OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW-div": true,
+ "U-Proposed-OUTDENT_OL-LI-1_SW-dM": true,
+ "U-Proposed-OUTDENT_OL-LI-1_SW-body": true,
+ "U-Proposed-OUTDENT_OL-LI-1_SW-div": true,
+ "U-Proposed-OUTDENT_UL-LI-1_SW-dM": true,
+ "U-Proposed-OUTDENT_UL-LI-1_SW-body": true,
+ "U-Proposed-OUTDENT_UL-LI-1_SW-div": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-dM": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-body": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-div": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-dM": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-body": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-div": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-dM": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-body": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-div": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-dM": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-body": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-div": true,
+ "UC-Proposed-S_SPANc:s-1_SW-dM": true,
+ "UC-Proposed-S_SPANc:s-1_SW-body": true,
+ "UC-Proposed-S_SPANc:s-1_SW-div": true,
+ "UC-Proposed-S_SPANc:s-2_SI-dM": true,
+ "UC-Proposed-S_SPANc:s-2_SI-body": true,
+ "UC-Proposed-S_SPANc:s-2_SI-div": true,
+ "D-Proposed-CHAR-3_SC-dM": true,
+ "D-Proposed-CHAR-3_SC-body": true,
+ "D-Proposed-CHAR-3_SC-div": true,
+ "D-Proposed-CHAR-4_SC-dM": true,
+ "D-Proposed-CHAR-4_SC-body": true,
+ "D-Proposed-CHAR-4_SC-div": true,
+ "D-Proposed-CHAR-5_SC-dM": true,
+ "D-Proposed-CHAR-5_SC-body": true,
+ "D-Proposed-CHAR-5_SC-div": true,
+ "D-Proposed-CHAR-5_SI-1-dM": true,
+ "D-Proposed-CHAR-5_SI-1-body": true,
+ "D-Proposed-CHAR-5_SI-1-div": true,
+ "D-Proposed-CHAR-5_SI-2-dM": true,
+ "D-Proposed-CHAR-5_SI-2-body": true,
+ "D-Proposed-CHAR-5_SI-2-div": true,
+ "D-Proposed-CHAR-5_SR-dM": true,
+ "D-Proposed-CHAR-5_SR-body": true,
+ "D-Proposed-CHAR-5_SR-div": true,
+ "D-Proposed-CHAR-6_SC-dM": true,
+ "D-Proposed-CHAR-6_SC-body": true,
+ "D-Proposed-CHAR-6_SC-div": true,
+ "D-Proposed-CHAR-7_SC-dM": true,
+ "D-Proposed-CHAR-7_SC-body": true,
+ "D-Proposed-CHAR-7_SC-div": true,
+ "D-Proposed-OL-LI-1_SW-dM": true,
+ "D-Proposed-OL-LI-1_SW-body": true,
+ "D-Proposed-OL-LI-1_SW-div": true,
+ "D-Proposed-OL-LI-1_SO-dM": true,
+ "D-Proposed-OL-LI-1_SO-body": true,
+ "D-Proposed-OL-LI-1_SO-div": true,
+ "D-Proposed-TR2rs:2-1_SO1-dM": true,
+ "D-Proposed-TR2rs:2-1_SO1-body": true,
+ "D-Proposed-TR2rs:2-1_SO1-div": true,
+ "D-Proposed-TR2rs:2-1_SO2-dM": true,
+ "D-Proposed-TR2rs:2-1_SO2-body": true,
+ "D-Proposed-TR2rs:2-1_SO2-div": true,
+ "D-Proposed-TR3rs:3-1_SO1-dM": true,
+ "D-Proposed-TR3rs:3-1_SO1-body": true,
+ "D-Proposed-TR3rs:3-1_SO1-div": true,
+ "D-Proposed-TR3rs:3-1_SO2-dM": true,
+ "D-Proposed-TR3rs:3-1_SO2-body": true,
+ "D-Proposed-TR3rs:3-1_SO2-div": true,
+ "D-Proposed-TR3rs:3-1_SO3-dM": true,
+ "D-Proposed-TR3rs:3-1_SO3-body": true,
+ "D-Proposed-TR3rs:3-1_SO3-div": true,
+ "D-Proposed-DIV:ce:false-1_SB-dM": true,
+ "D-Proposed-DIV:ce:false-1_SL-dM": true,
+ "D-Proposed-DIV:ce:false-1_SL-body": true,
+ "D-Proposed-DIV:ce:false-1_SL-div": true,
+ "D-Proposed-DIV:ce:false-1_SR-dM": true,
+ "D-Proposed-DIV:ce:false-1_SR-body": true,
+ "D-Proposed-DIV:ce:false-1_SR-div": true,
+ "D-Proposed-DIV:ce:false-1_SI-dM": true,
+ "FD-Proposed-OL-LI-1_SW-dM": true,
+ "FD-Proposed-OL-LI-1_SW-body": true,
+ "FD-Proposed-OL-LI-1_SW-div": true,
+ "FD-Proposed-OL-LI-1_SO-dM": true,
+ "FD-Proposed-OL-LI-1_SO-body": true,
+ "FD-Proposed-OL-LI-1_SO-div": true,
+ "FD-Proposed-TR2rs:2-1_SO1-dM": true,
+ "FD-Proposed-TR2rs:2-1_SO1-body": true,
+ "FD-Proposed-TR2rs:2-1_SO1-div": true,
+ "FD-Proposed-TR2rs:2-1_SO2-dM": true,
+ "FD-Proposed-TR2rs:2-1_SO2-body": true,
+ "FD-Proposed-TR2rs:2-1_SO2-div": true,
+ "FD-Proposed-TR3rs:3-1_SO1-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO1-body": true,
+ "FD-Proposed-TR3rs:3-1_SO1-div": true,
+ "FD-Proposed-TR3rs:3-1_SO2-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO2-body": true,
+ "FD-Proposed-TR3rs:3-1_SO2-div": true,
+ "FD-Proposed-TR3rs:3-1_SO3-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO3-body": true,
+ "FD-Proposed-TR3rs:3-1_SO3-div": true,
+ "FD-Proposed-DIV:ce:false-1_SB-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SL-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SL-body": true,
+ "FD-Proposed-DIV:ce:false-1_SL-div": true,
+ "FD-Proposed-DIV:ce:false-1_SR-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SR-body": true,
+ "FD-Proposed-DIV:ce:false-1_SR-div": true,
+ "FD-Proposed-DIV:ce:false-1_SI-dM": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-dM": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-body": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-div": true,
+ "I-Proposed-IIMG:._IMG-1_SO-dM": true,
+ "I-Proposed-IIMG:._IMG-1_SO-body": true,
+ "I-Proposed-IIMG:._IMG-1_SO-div": true,
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-dM": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-body": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-div": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-dM": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-body": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-div": true,
+ "Q-Proposed-HEADING_TEXT-1-dM": true,
+ "Q-Proposed-HEADING_TEXT-1-body": true,
+ "Q-Proposed-HEADING_TEXT-1-div": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-dM": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-body": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-div": true,
+ "Q-Proposed-PASTE_TEXT-1-dM": true,
+ "Q-Proposed-PASTE_TEXT-1-body": true,
+ "Q-Proposed-PASTE_TEXT-1-div": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-dM": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-body": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-div": true,
+ "Q-Proposed-UNSELECT_TEXT-1-dM": true,
+ "Q-Proposed-UNSELECT_TEXT-1-body": true,
+ "Q-Proposed-UNSELECT_TEXT-1-div": true,
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-dM": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-body": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-div": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false),
+ "QE-Proposed-COPY_TEXT-1-dM": true,
+ "QE-Proposed-COPY_TEXT-1-body": true,
+ "QE-Proposed-COPY_TEXT-1-div": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
+ "QE-Proposed-CUT_TEXT-1-dM": true,
+ "QE-Proposed-CUT_TEXT-1-body": true,
+ "QE-Proposed-CUT_TEXT-1-div": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-dM": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-body": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-div": true,
+ "QE-Proposed-HEADING_TEXT-1-dM": true,
+ "QE-Proposed-HEADING_TEXT-1-body": true,
+ "QE-Proposed-HEADING_TEXT-1-div": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-dM": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-body": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-div": true,
+ "QE-Proposed-PASTE_TEXT-1-dM": true,
+ "QE-Proposed-PASTE_TEXT-1-body": true,
+ "QE-Proposed-PASTE_TEXT-1-div": true,
+ "QE-Proposed-REDO_TEXT-1-dM": true,
+ "QE-Proposed-REDO_TEXT-1-body": true,
+ "QE-Proposed-REDO_TEXT-1-div": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-dM": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-body": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-div": true,
+ "QE-Proposed-UNSELECT_TEXT-1-dM": true,
+ "QE-Proposed-UNSELECT_TEXT-1-body": true,
+ "QE-Proposed-UNSELECT_TEXT-1-div": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-dM": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-body": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-div": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-dM": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-body": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-div": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-dM": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-body": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-div": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-dM": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-body": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-div": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-dM": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-body": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-div": true,
+ "QS-Proposed-JC_MYJC-1-SI-dM": true,
+ "QS-Proposed-JC_MYJC-1-SI-body": true,
+ "QS-Proposed-JC_MYJC-1-SI-div": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-dM": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-body": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-div": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-dM": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-body": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-div": true,
+ "QS-Proposed-JF_MYJF-1-SI-dM": true,
+ "QS-Proposed-JF_MYJF-1-SI-body": true,
+ "QS-Proposed-JF_MYJF-1-SI-div": true,
+ "QS-Proposed-JL_TEXT_SI-dM": true,
+ "QS-Proposed-JL_TEXT_SI-body": true,
+ "QS-Proposed-JL_TEXT_SI-div": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-dM": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-body": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-div": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-dM": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-body": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-div": true,
+ "QS-Proposed-JR_MYJR-1-SI-dM": true,
+ "QS-Proposed-JR_MYJR-1-SI-body": true,
+ "QS-Proposed-JR_MYJR-1-SI-div": true,
+ "QV-Proposed-B_TEXT_SI-dM": true,
+ "QV-Proposed-B_TEXT_SI-body": true,
+ "QV-Proposed-B_TEXT_SI-div": true,
+ "QV-Proposed-B_B-1_SI-dM": true,
+ "QV-Proposed-B_B-1_SI-body": true,
+ "QV-Proposed-B_B-1_SI-div": true,
+ "QV-Proposed-B_STRONG-1_SI-dM": true,
+ "QV-Proposed-B_STRONG-1_SI-body": true,
+ "QV-Proposed-B_STRONG-1_SI-div": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-dM": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-body": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-div": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-dM": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-body": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-div": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-dM": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-body": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-div": true,
+ "QV-Proposed-B_SPAN.b-1_SI-dM": true,
+ "QV-Proposed-B_SPAN.b-1_SI-body": true,
+ "QV-Proposed-B_SPAN.b-1_SI-div": true,
+ "QV-Proposed-B_MYB-1-SI-dM": true,
+ "QV-Proposed-B_MYB-1-SI-body": true,
+ "QV-Proposed-B_MYB-1-SI-div": true,
+ "QV-Proposed-I_TEXT_SI-dM": true,
+ "QV-Proposed-I_TEXT_SI-body": true,
+ "QV-Proposed-I_TEXT_SI-div": true,
+ "QV-Proposed-I_I-1_SI-dM": true,
+ "QV-Proposed-I_I-1_SI-body": true,
+ "QV-Proposed-I_I-1_SI-div": true,
+ "QV-Proposed-I_EM-1_SI-dM": true,
+ "QV-Proposed-I_EM-1_SI-body": true,
+ "QV-Proposed-I_EM-1_SI-div": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-dM": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-body": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-div": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-dM": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-body": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-div": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-dM": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-body": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-div": true,
+ "QV-Proposed-I_SPAN.i-1_SI-dM": true,
+ "QV-Proposed-I_SPAN.i-1_SI-body": true,
+ "QV-Proposed-I_SPAN.i-1_SI-div": true,
+ "QV-Proposed-I_MYI-1-SI-dM": true,
+ "QV-Proposed-I_MYI-1-SI-body": true,
+ "QV-Proposed-I_MYI-1-SI-div": true,
+ "QV-Proposed-FB_H1-H2-1_SL-dM": true,
+ "QV-Proposed-FB_H1-H2-1_SL-body": true,
+ "QV-Proposed-FB_H1-H2-1_SL-div": true,
+ "QV-Proposed-FB_H1-H2-1_SR-dM": true,
+ "QV-Proposed-FB_H1-H2-1_SR-body": true,
+ "QV-Proposed-FB_H1-H2-1_SR-div": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-dM": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-body": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-div": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-dM": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-body": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-div": true,
+ "QV-Proposed-H_H1-1_SC-dM": true,
+ "QV-Proposed-H_H1-1_SC-body": true,
+ "QV-Proposed-H_H1-1_SC-div": true,
+ "QV-Proposed-H_H3-1_SC-dM": true,
+ "QV-Proposed-H_H3-1_SC-body": true,
+ "QV-Proposed-H_H3-1_SC-div": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-dM": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-body": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-div": true,
+ "QV-Proposed-H_P-1_SC-dM": false,
+ "QV-Proposed-H_P-1_SC-body": false,
+ "QV-Proposed-H_P-1_SC-div": false,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-dM": true,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-body": true,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-div": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-dM": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-body": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-div": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-dM": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-body": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-div": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-dM": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-body": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-div": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-dM": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-body": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-div": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-dM": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-body": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-div": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-dM": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-body": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-div": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-dM": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-body": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-div": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-dM": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-body": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-div": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-dM": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-body": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-div": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-dM": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-body": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-div": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-dM": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-body": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-div": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-dM": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-body": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-div": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-dM": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-body": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-div": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-dM": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-body": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-div": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-dM": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-body": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-div": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-dM": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-body": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-div": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-dM": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-body": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-div": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-dM": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-body": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-div": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-dM": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-body": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-div": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-dM": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-body": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-div": true,
+ },
+ select: {
+ "S-Proposed-UNSEL_TEXT-1_SI-dM": true,
+ "S-Proposed-UNSEL_TEXT-1_SI-body": true,
+ "S-Proposed-UNSEL_TEXT-1_SI-div": true,
+ "S-Proposed-SM:m.f.c_TEXT-1_SI-1-dM": true,
+ "S-Proposed-SM:m.f.c_TEXT-1_SI-1-body": true,
+ "S-Proposed-SM:m.f.c_TEXT-1_SI-1-div": true,
+ "S-Proposed-SM:m.b.c_TEXT-1_SI-1-dM": true,
+ "S-Proposed-SM:m.b.c_TEXT-1_SI-1-body": true,
+ "S-Proposed-SM:m.b.c_TEXT-1_SI-1-div": true,
+ "S-Proposed-SM:m.b.w_TEXT-1_SI-1-dM": true,
+ "S-Proposed-SM:m.b.w_TEXT-1_SI-1-body": true,
+ "S-Proposed-SM:m.b.w_TEXT-1_SI-1-div": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-dM": SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-body": SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-div": SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.c_CHAR-5_SI-2-dM": true,
+ "S-Proposed-SM:m.f.c_CHAR-5_SI-2-body": true,
+ "S-Proposed-SM:m.f.c_CHAR-5_SI-2-div": true,
+ "S-Proposed-SM:m.f.c_CHAR-5_SR-dM": true,
+ "S-Proposed-SM:m.f.c_CHAR-5_SR-body": true,
+ "S-Proposed-SM:m.f.c_CHAR-5_SR-div": true,
+ "S-Proposed-SM:m.b.c_CHAR-5_SR-dM": true,
+ "S-Proposed-SM:m.b.c_CHAR-5_SR-body": true,
+ "S-Proposed-SM:m.b.c_CHAR-5_SR-div": true,
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-1-dM": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-1-body": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-1-div": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-2-dM": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-2-body": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-2-div": !SpecialPowers.getBoolPref("intl.icu4x.segmenter.enabled", true),
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-5-dM": true,
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-5-body": true,
+ "S-Proposed-SM:m.f.w_TEXT-jp_SC-5-div": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-3-dM": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-3-body": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-3-div": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-4-dM": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-4-body": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-4-div": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-5-dM": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-5-body": true,
+ "S-Proposed-SM:e.b.w_TEXT-1_SI-5-div": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-1-dM": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-1-body": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-1-div": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-3-dM": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-3-body": true,
+ "S-Proposed-SM:e.f.w_TEXT-1_SIR-3-div": true,
+ "S-Proposed-SM:e.f.lb_BR.BR-1_SI-1-dM": true,
+ "S-Proposed-SM:e.f.lb_BR.BR-1_SI-1-body": true,
+ "S-Proposed-SM:e.f.lb_BR.BR-1_SI-1-div": true,
+ "S-Proposed-SM:e.f.lb_P.P.P-1_SI-1-dM": true,
+ "S-Proposed-SM:e.f.lb_P.P.P-1_SI-1-body": true,
+ "S-Proposed-SM:e.f.lb_P.P.P-1_SI-1-div": true,
+ "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-dM": true,
+ "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-body": true,
+ "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-div": true,
+ "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-dM": true,
+ "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-body": true,
+ "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-div": true,
+ "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-dM": true,
+ "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-body": true,
+ "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-div": true,
+ "A-Proposed-B_TEXT-1_SIR-dM": true,
+ "A-Proposed-B_TEXT-1_SIR-body": true,
+ "A-Proposed-B_TEXT-1_SIR-div": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-dM": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-body": true,
+ "A-Proposed-FS:18px_TEXT-1_SI-div": true,
+ "A-Proposed-FS:large_TEXT-1_SI-dM": true,
+ "A-Proposed-FS:large_TEXT-1_SI-body": true,
+ "A-Proposed-FS:large_TEXT-1_SI-div": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-dM": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-body": true,
+ "A-Proposed-INCFS:2_TEXT-1_SI-div": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-dM": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-body": true,
+ "A-Proposed-DECFS:2_TEXT-1_SI-div": true,
+ "A-Proposed-CB:name_TEXT-1_SI-dM": true,
+ "A-Proposed-CB:name_TEXT-1_SI-body": true,
+ "A-Proposed-CB:name_TEXT-1_SI-div": true,
+ "A-Proposed-H:H1_TEXT-1_SC-dM": true,
+ "A-Proposed-H:H1_TEXT-1_SC-body": true,
+ "A-Proposed-H:H1_TEXT-1_SC-div": true,
+ "AC-Proposed-SUB_TEXT-1_SI-dM": true,
+ "AC-Proposed-SUB_TEXT-1_SI-body": true,
+ "AC-Proposed-SUB_TEXT-1_SI-div": true,
+ "AC-Proposed-SUP_TEXT-1_SI-dM": true,
+ "AC-Proposed-SUP_TEXT-1_SI-body": true,
+ "AC-Proposed-SUP_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:2_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:18px_TEXT-1_SI-div": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-dM": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-body": true,
+ "AC-Proposed-FS:large_TEXT-1_SI-div": true,
+
+ // Those tests expect that <font> elements can be nested, but they don't
+ // match with the other browsers' behavior.
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-dM": true,
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-body": true,
+ "C-Proposed-FC:g_FONTc:b.sz:6-1_SI-div": true,
+
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-dM": true,
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-body": true,
+ "C-Proposed-FS:1_SPAN.ass.s:fs:large-1_SW-div": true,
+
+ // Those tests expect that <font> elements can be nested, but they don't
+ // match with the other browsers' behavior.
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-dM": true,
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-body": true,
+ "C-Proposed-FS:2_FONTc:b.sz:6-1_SI-div": true,
+
+ "C-Proposed-FS:larger_FONTsz:4-dM": true,
+ "C-Proposed-FS:larger_FONTsz:4-body": true,
+ "C-Proposed-FS:larger_FONTsz:4-div": true,
+ "C-Proposed-FS:smaller_FONTsz:4-dM": true,
+ "C-Proposed-FS:smaller_FONTsz:4-body": true,
+ "C-Proposed-FS:smaller_FONTsz:4-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SO-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONTsz:4-1_SW-div": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-dM": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-body": true,
+ "C-Proposed-FB:h1_ADDRESS-FONT.ass.sz:4-1_SW-div": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-dM": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-body": true,
+ "CC-Proposed-BC:gray_P-SPANs:bc:b-3_SL-div": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-dM": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-body": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SL-div": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-dM": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-body": true,
+ "CC-Proposed-BC:gray_SPANs:bc:b-2_SR-div": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:1_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:18px_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-dM": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-body": true,
+ "CC-Proposed-FS:4_SPANs:fs:l-1_SW-div": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-dM": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-body": true,
+ "CC-Proposed-FS:4_SPANs:fs:18px-1_SW-div": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-dM": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-body": true,
+ "CC-Proposed-FS:larger_SPANs:fs:l-1_SI-div": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-dM": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-body": true,
+ "CC-Proposed-FS:smaller_SPANs:fs:l-1_SI-div": true,
+ "U-RFC-UNLINK_A-1_SO-dM": true,
+ "U-RFC-UNLINK_A-1_SO-body": true,
+ "U-RFC-UNLINK_A-1_SO-div": true,
+ "U-RFC-UNLINK_A-1_SW-dM": true,
+ "U-RFC-UNLINK_A-1_SW-body": true,
+ "U-RFC-UNLINK_A-1_SW-div": true,
+ "U-RFC-UNLINK_A-2_SO-dM": true,
+ "U-RFC-UNLINK_A-2_SO-body": true,
+ "U-RFC-UNLINK_A-2_SO-div": true,
+ "U-RFC-UNLINK_A2-1_SO-dM": true,
+ "U-RFC-UNLINK_A2-1_SO-body": true,
+ "U-RFC-UNLINK_A2-1_SO-div": true,
+ "U-Proposed-B_B-P3-1_SO12-dM": true,
+ "U-Proposed-B_B-P3-1_SO12-body": true,
+ "U-Proposed-B_B-P3-1_SO12-div": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-dM": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-body": true,
+ "U-Proposed-B_B-P-I..P-1_SO-I-div": true,
+ "U-Proposed-B_B-2_SL-dM": true,
+ "U-Proposed-B_B-2_SL-body": true,
+ "U-Proposed-B_B-2_SL-div": true,
+ "U-Proposed-B_B-2_SR-dM": true,
+ "U-Proposed-B_B-2_SR-body": true,
+ "U-Proposed-B_B-2_SR-div": true,
+ "U-Proposed-I_I-P3-1_SO2-dM": true,
+ "U-Proposed-I_I-P3-1_SO2-body": true,
+ "U-Proposed-I_I-P3-1_SO2-div": true,
+ "U-Proposed-U_U-S-2_SI-dM": true,
+ "U-Proposed-U_U-S-2_SI-body": true,
+ "U-Proposed-U_U-S-2_SI-div": true,
+ "U-Proposed-U_U-P3-1_SO-dM": true,
+ "U-Proposed-U_U-P3-1_SO-body": true,
+ "U-Proposed-U_U-P3-1_SO-div": true,
+ "U-Proposed-S_DEL-1_SW-dM": true,
+ "U-Proposed-S_DEL-1_SW-body": true,
+ "U-Proposed-S_DEL-1_SW-div": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-dM": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-body": true,
+ "U-Proposed-SUB_SPANs:va:sub-1_SW-div": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-dM": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-body": true,
+ "U-Proposed-SUP_SPANs:va:super-1_SW-div": true,
+ "U-Proposed-UNLINK_A-1_SC-dM": true,
+ "U-Proposed-UNLINK_A-1_SC-body": true,
+ "U-Proposed-UNLINK_A-1_SC-div": true,
+ "U-Proposed-UNLINK_A-1_SI-dM": true,
+ "U-Proposed-UNLINK_A-1_SI-body": true,
+ "U-Proposed-UNLINK_A-1_SI-div": true,
+ "U-Proposed-UNLINK_A-2_SL-dM": true,
+ "U-Proposed-UNLINK_A-2_SL-body": true,
+ "U-Proposed-UNLINK_A-2_SL-div": true,
+ "U-Proposed-UNLINK_A-3_SR-dM": true,
+ "U-Proposed-UNLINK_A-3_SR-body": true,
+ "U-Proposed-UNLINK_A-3_SR-div": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-dM": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-body": true,
+ "U-Proposed-OUTDENT_DIV-1_SW-div": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-dM": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-body": true,
+ "U-Proposed-REMOVEFORMAT_Ahref:url-1_SW-div": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-dM": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-body": true,
+ "U-Proposed-REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW-div": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-dM": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-body": true,
+ "U-Proposed-UNBOOKMARK_An:name-1_SW-div": true,
+ "UC-Proposed-S_SPANc:s-1_SW-dM": true,
+ "UC-Proposed-S_SPANc:s-1_SW-body": true,
+ "UC-Proposed-S_SPANc:s-1_SW-div": true,
+ "UC-Proposed-S_SPANc:s-2_SI-dM": true,
+ "UC-Proposed-S_SPANc:s-2_SI-body": true,
+ "UC-Proposed-S_SPANc:s-2_SI-div": true,
+ "D-Proposed-CHAR-3_SC-dM": true,
+ "D-Proposed-CHAR-3_SC-body": true,
+ "D-Proposed-CHAR-3_SC-div": true,
+ "D-Proposed-CHAR-4_SC-dM": true,
+ "D-Proposed-CHAR-4_SC-body": true,
+ "D-Proposed-CHAR-4_SC-div": true,
+ "D-Proposed-CHAR-5_SC-dM": true,
+ "D-Proposed-CHAR-5_SC-body": true,
+ "D-Proposed-CHAR-5_SC-div": true,
+ "D-Proposed-CHAR-5_SI-1-dM": true,
+ "D-Proposed-CHAR-5_SI-1-body": true,
+ "D-Proposed-CHAR-5_SI-1-div": true,
+ "D-Proposed-CHAR-5_SI-2-dM": true,
+ "D-Proposed-CHAR-5_SI-2-body": true,
+ "D-Proposed-CHAR-5_SI-2-div": true,
+ "D-Proposed-CHAR-5_SR-dM": true,
+ "D-Proposed-CHAR-5_SR-body": true,
+ "D-Proposed-CHAR-5_SR-div": true,
+ "D-Proposed-CHAR-6_SC-dM": true,
+ "D-Proposed-CHAR-6_SC-body": true,
+ "D-Proposed-CHAR-6_SC-div": true,
+ "D-Proposed-CHAR-7_SC-dM": true,
+ "D-Proposed-CHAR-7_SC-body": true,
+ "D-Proposed-CHAR-7_SC-div": true,
+ "D-Proposed-B-1_SW-div": true,
+ "D-Proposed-B-1_SL-dM": true,
+ "D-Proposed-B-1_SL-body": true,
+ "D-Proposed-B-1_SL-div": true,
+ "D-Proposed-B-1_SR-dM": true,
+ "D-Proposed-B-1_SR-body": true,
+ "D-Proposed-B-1_SR-div": true,
+ "D-Proposed-B.I-1_SM-dM": true,
+ "D-Proposed-B.I-1_SM-body": true,
+ "D-Proposed-B.I-1_SM-div": true,
+ "D-Proposed-OL-LI2-1_SO1-dM": true,
+ "D-Proposed-OL-LI2-1_SO1-body": true,
+ "D-Proposed-OL-LI2-1_SO1-div": true,
+ "D-Proposed-OL-LI-1_SW-dM": true,
+ "D-Proposed-OL-LI-1_SW-body": true,
+ "D-Proposed-OL-LI-1_SW-div": true,
+ "D-Proposed-OL-LI-1_SO-dM": true,
+ "D-Proposed-OL-LI-1_SO-body": true,
+ "D-Proposed-OL-LI-1_SO-div": true,
+ "D-Proposed-HR.BR-1_SM-dM": true,
+ "D-Proposed-HR.BR-1_SM-body": true,
+ "D-Proposed-HR.BR-1_SM-div": true,
+ "D-Proposed-TR2rs:2-1_SO1-dM": true,
+ "D-Proposed-TR2rs:2-1_SO1-body": true,
+ "D-Proposed-TR2rs:2-1_SO1-div": true,
+ "D-Proposed-TR2rs:2-1_SO2-dM": true,
+ "D-Proposed-TR2rs:2-1_SO2-body": true,
+ "D-Proposed-TR2rs:2-1_SO2-div": true,
+ "D-Proposed-TR3rs:3-1_SO1-dM": true,
+ "D-Proposed-TR3rs:3-1_SO1-body": true,
+ "D-Proposed-TR3rs:3-1_SO1-div": true,
+ "D-Proposed-TR3rs:3-1_SO2-dM": true,
+ "D-Proposed-TR3rs:3-1_SO2-body": true,
+ "D-Proposed-TR3rs:3-1_SO2-div": true,
+ "D-Proposed-TR3rs:3-1_SO3-dM": true,
+ "D-Proposed-TR3rs:3-1_SO3-body": true,
+ "D-Proposed-TR3rs:3-1_SO3-div": true,
+ "D-Proposed-DIV:ce:false-1_SB-dM": true,
+ "D-Proposed-DIV:ce:false-1_SL-dM": true,
+ "D-Proposed-DIV:ce:false-1_SL-body": true,
+ "D-Proposed-DIV:ce:false-1_SL-div": true,
+ "D-Proposed-DIV:ce:false-1_SR-dM": true,
+ "D-Proposed-DIV:ce:false-1_SR-body": true,
+ "D-Proposed-DIV:ce:false-1_SR-div": true,
+ "D-Proposed-DIV:ce:false-1_SI-dM": true,
+ "D-Proposed-SPAN:d:ib-2_SL-dM": true,
+ "D-Proposed-SPAN:d:ib-2_SL-body": true,
+ "D-Proposed-SPAN:d:ib-2_SL-div": true,
+ "D-Proposed-SPAN:d:ib-3_SR-dM": true,
+ "D-Proposed-SPAN:d:ib-3_SR-body": true,
+ "D-Proposed-SPAN:d:ib-3_SR-div": true,
+ "FD-Proposed-B-1_SW-div": true,
+ "FD-Proposed-OL-LI-1_SW-dM": true,
+ "FD-Proposed-OL-LI-1_SW-body": true,
+ "FD-Proposed-OL-LI-1_SW-div": true,
+ "FD-Proposed-OL-LI-1_SO-dM": true,
+ "FD-Proposed-OL-LI-1_SO-body": true,
+ "FD-Proposed-OL-LI-1_SO-div": true,
+ "FD-Proposed-TABLE-1_SB-dM": true,
+ "FD-Proposed-TABLE-1_SB-body": true,
+ "FD-Proposed-TABLE-1_SB-div": true,
+ "FD-Proposed-TD-1_SE-dM": true,
+ "FD-Proposed-TD-1_SE-body": true,
+ "FD-Proposed-TD-1_SE-div": true,
+ "FD-Proposed-TD2-1_SE1-dM": true,
+ "FD-Proposed-TD2-1_SE1-body": true,
+ "FD-Proposed-TD2-1_SE1-div": true,
+ "FD-Proposed-TD2-1_SM-dM": true,
+ "FD-Proposed-TD2-1_SM-body": true,
+ "FD-Proposed-TD2-1_SM-div": true,
+ "FD-Proposed-TR2rs:2-1_SO1-dM": true,
+ "FD-Proposed-TR2rs:2-1_SO1-body": true,
+ "FD-Proposed-TR2rs:2-1_SO1-div": true,
+ "FD-Proposed-TR2rs:2-1_SO2-dM": true,
+ "FD-Proposed-TR2rs:2-1_SO2-body": true,
+ "FD-Proposed-TR2rs:2-1_SO2-div": true,
+ "FD-Proposed-TR3rs:3-1_SO1-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO1-body": true,
+ "FD-Proposed-TR3rs:3-1_SO1-div": true,
+ "FD-Proposed-TR3rs:3-1_SO2-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO2-body": true,
+ "FD-Proposed-TR3rs:3-1_SO2-div": true,
+ "FD-Proposed-TR3rs:3-1_SO3-dM": true,
+ "FD-Proposed-TR3rs:3-1_SO3-body": true,
+ "FD-Proposed-TR3rs:3-1_SO3-div": true,
+ "FD-Proposed-DIV:ce:false-1_SB-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SL-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SL-body": true,
+ "FD-Proposed-DIV:ce:false-1_SL-div": true,
+ "FD-Proposed-DIV:ce:false-1_SR-dM": true,
+ "FD-Proposed-DIV:ce:false-1_SR-body": true,
+ "FD-Proposed-DIV:ce:false-1_SR-div": true,
+ "FD-Proposed-DIV:ce:false-1_SI-dM": true,
+ "I-Proposed-IHR_TEXT-1_SC-dM": true,
+ "I-Proposed-IHR_TEXT-1_SC-body": true,
+ "I-Proposed-IHR_TEXT-1_SC-div": true,
+ "I-Proposed-IHR_TEXT-1_SI-dM": true,
+ "I-Proposed-IHR_TEXT-1_SI-body": true,
+ "I-Proposed-IHR_TEXT-1_SI-div": true,
+ "I-Proposed-IHR_B-1_SC-dM": true,
+ "I-Proposed-IHR_B-1_SC-body": true,
+ "I-Proposed-IHR_B-1_SC-div": true,
+ "I-Proposed-IHR_B-1_SS-dM": true,
+ "I-Proposed-IHR_B-1_SS-body": true,
+ "I-Proposed-IHR_B-1_SS-div": true,
+ "I-Proposed-IHR_B-I-1_SMR-dM": true,
+ "I-Proposed-IHR_B-I-1_SMR-body": true,
+ "I-Proposed-IHR_B-I-1_SMR-div": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-dM": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-body": true,
+ "I-Proposed-IIMG:._SPAN-IMG-1_SO-div": true,
+ "I-Proposed-IIMG:._IMG-1_SO-dM": true,
+ "I-Proposed-IIMG:._IMG-1_SO-body": true,
+ "I-Proposed-IIMG:._IMG-1_SO-div": true,
+ "I-Proposed-IHTML:BR_TEXT-1_SC-dM": true,
+ "I-Proposed-IHTML:BR_TEXT-1_SC-body": true,
+ "I-Proposed-IHTML:BR_TEXT-1_SC-div": true,
+ "I-Proposed-IHTML:S_TEXT-1_SI-dM": true,
+ "I-Proposed-IHTML:S_TEXT-1_SI-body": true,
+ "I-Proposed-IHTML:S_TEXT-1_SI-div": true,
+ "I-Proposed-IHTML:H1.H2_TEXT-1_SI-dM": true,
+ "I-Proposed-IHTML:H1.H2_TEXT-1_SI-body": true,
+ "I-Proposed-IHTML:H1.H2_TEXT-1_SI-div": true,
+ "I-Proposed-IHTML:P-B_TEXT-1_SI-dM": true,
+ "I-Proposed-IHTML:P-B_TEXT-1_SI-body": true,
+ "I-Proposed-IHTML:P-B_TEXT-1_SI-div": true,
+ "Q-Proposed-SELECTALL_TEXT-1-dM": true,
+ "Q-Proposed-SELECTALL_TEXT-1-body": true,
+ "Q-Proposed-SELECTALL_TEXT-1-div": true,
+ "Q-Proposed-UNSELECT_TEXT-1-dM": true,
+ "Q-Proposed-UNSELECT_TEXT-1-body": true,
+ "Q-Proposed-UNSELECT_TEXT-1-div": true,
+ "Q-Proposed-UNDO_TEXT-1-dM": true,
+ "Q-Proposed-UNDO_TEXT-1-body": true,
+ "Q-Proposed-UNDO_TEXT-1-div": true,
+ "Q-Proposed-REDO_TEXT-1-dM": true,
+ "Q-Proposed-REDO_TEXT-1-body": true,
+ "Q-Proposed-REDO_TEXT-1-div": true,
+ "Q-Proposed-BOLD_TEXT-1-dM": true,
+ "Q-Proposed-BOLD_TEXT-1-body": true,
+ "Q-Proposed-BOLD_TEXT-1-div": true,
+ "Q-Proposed-BOLD_B-dM": true,
+ "Q-Proposed-BOLD_B-body": true,
+ "Q-Proposed-BOLD_B-div": true,
+ "Q-Proposed-ITALIC_TEXT-1-dM": true,
+ "Q-Proposed-ITALIC_TEXT-1-body": true,
+ "Q-Proposed-ITALIC_TEXT-1-div": true,
+ "Q-Proposed-ITALIC_I-dM": true,
+ "Q-Proposed-ITALIC_I-body": true,
+ "Q-Proposed-ITALIC_I-div": true,
+ "Q-Proposed-UNDERLINE_TEXT-1-dM": true,
+ "Q-Proposed-UNDERLINE_TEXT-1-body": true,
+ "Q-Proposed-UNDERLINE_TEXT-1-div": true,
+ "Q-Proposed-STRIKETHROUGH_TEXT-1-dM": true,
+ "Q-Proposed-STRIKETHROUGH_TEXT-1-body": true,
+ "Q-Proposed-STRIKETHROUGH_TEXT-1-div": true,
+ "Q-Proposed-SUBSCRIPT_TEXT-1-dM": true,
+ "Q-Proposed-SUBSCRIPT_TEXT-1-body": true,
+ "Q-Proposed-SUBSCRIPT_TEXT-1-div": true,
+ "Q-Proposed-SUPERSCRIPT_TEXT-1-dM": true,
+ "Q-Proposed-SUPERSCRIPT_TEXT-1-body": true,
+ "Q-Proposed-SUPERSCRIPT_TEXT-1-div": true,
+ "Q-Proposed-FORMATBLOCK_TEXT-1-dM": true,
+ "Q-Proposed-FORMATBLOCK_TEXT-1-body": true,
+ "Q-Proposed-FORMATBLOCK_TEXT-1-div": true,
+ "Q-Proposed-CREATELINK_TEXT-1-dM": true,
+ "Q-Proposed-CREATELINK_TEXT-1-body": true,
+ "Q-Proposed-CREATELINK_TEXT-1-div": true,
+ "Q-Proposed-UNLINK_TEXT-1-dM": true,
+ "Q-Proposed-UNLINK_TEXT-1-body": true,
+ "Q-Proposed-UNLINK_TEXT-1-div": true,
+ "Q-Proposed-INSERTHTML_TEXT-1-dM": true,
+ "Q-Proposed-INSERTHTML_TEXT-1-body": true,
+ "Q-Proposed-INSERTHTML_TEXT-1-div": true,
+ "Q-Proposed-INSERTHORIZONTALRULE_TEXT-1-dM": true,
+ "Q-Proposed-INSERTHORIZONTALRULE_TEXT-1-body": true,
+ "Q-Proposed-INSERTHORIZONTALRULE_TEXT-1-div": true,
+ "Q-Proposed-INSERTIMAGE_TEXT-1-dM": true,
+ "Q-Proposed-INSERTIMAGE_TEXT-1-body": true,
+ "Q-Proposed-INSERTIMAGE_TEXT-1-div": true,
+ "Q-Proposed-INSERTLINEBREAK_TEXT-1-dM": true,
+ "Q-Proposed-INSERTLINEBREAK_TEXT-1-body": true,
+ "Q-Proposed-INSERTLINEBREAK_TEXT-1-div": true,
+ "Q-Proposed-INSERTPARAGRAPH_TEXT-1-dM": true,
+ "Q-Proposed-INSERTPARAGRAPH_TEXT-1-body": true,
+ "Q-Proposed-INSERTPARAGRAPH_TEXT-1-div": true,
+ "Q-Proposed-INSERTORDEREDLIST_TEXT-1-dM": true,
+ "Q-Proposed-INSERTORDEREDLIST_TEXT-1-body": true,
+ "Q-Proposed-INSERTORDEREDLIST_TEXT-1-div": true,
+ "Q-Proposed-INSERTUNORDEREDLIST_TEXT-1-dM": true,
+ "Q-Proposed-INSERTUNORDEREDLIST_TEXT-1-body": true,
+ "Q-Proposed-INSERTUNORDEREDLIST_TEXT-1-div": true,
+ "Q-Proposed-INSERTTEXT_TEXT-1-dM": true,
+ "Q-Proposed-INSERTTEXT_TEXT-1-body": true,
+ "Q-Proposed-INSERTTEXT_TEXT-1-div": true,
+ "Q-Proposed-DELETE_TEXT-1-dM": true,
+ "Q-Proposed-DELETE_TEXT-1-body": true,
+ "Q-Proposed-DELETE_TEXT-1-div": true,
+ "Q-Proposed-FORWARDDELETE_TEXT-1-dM": true,
+ "Q-Proposed-FORWARDDELETE_TEXT-1-body": true,
+ "Q-Proposed-FORWARDDELETE_TEXT-1-div": true,
+ "Q-Proposed-STYLEWITHCSS_TEXT-1-dM": true,
+ "Q-Proposed-STYLEWITHCSS_TEXT-1-body": true,
+ "Q-Proposed-STYLEWITHCSS_TEXT-1-div": true,
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-dM": true,
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-body": true,
+ "Q-Proposed-CONTENTREADONLY_TEXT-1-div": true,
+ "Q-Proposed-BACKCOLOR_TEXT-1-dM": true,
+ "Q-Proposed-BACKCOLOR_TEXT-1-body": true,
+ "Q-Proposed-BACKCOLOR_TEXT-1-div": true,
+ "Q-Proposed-FORECOLOR_TEXT-1-dM": true,
+ "Q-Proposed-FORECOLOR_TEXT-1-body": true,
+ "Q-Proposed-FORECOLOR_TEXT-1-div": true,
+ "Q-Proposed-HILITECOLOR_TEXT-1-dM": true,
+ "Q-Proposed-HILITECOLOR_TEXT-1-body": true,
+ "Q-Proposed-HILITECOLOR_TEXT-1-div": true,
+ "Q-Proposed-FONTNAME_TEXT-1-dM": true,
+ "Q-Proposed-FONTNAME_TEXT-1-body": true,
+ "Q-Proposed-FONTNAME_TEXT-1-div": true,
+ "Q-Proposed-FONTSIZE_TEXT-1-dM": true,
+ "Q-Proposed-FONTSIZE_TEXT-1-body": true,
+ "Q-Proposed-FONTSIZE_TEXT-1-div": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-dM": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-body": true,
+ "Q-Proposed-INCREASEFONTSIZE_TEXT-1-div": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-dM": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-body": true,
+ "Q-Proposed-DECREASEFONTSIZE_TEXT-1-div": true,
+ "Q-Proposed-HEADING_TEXT-1-dM": true,
+ "Q-Proposed-HEADING_TEXT-1-body": true,
+ "Q-Proposed-HEADING_TEXT-1-div": true,
+ "Q-Proposed-INDENT_TEXT-1-dM": true,
+ "Q-Proposed-INDENT_TEXT-1-body": true,
+ "Q-Proposed-INDENT_TEXT-1-div": true,
+ "Q-Proposed-OUTDENT_TEXT-1-dM": true,
+ "Q-Proposed-OUTDENT_TEXT-1-body": true,
+ "Q-Proposed-OUTDENT_TEXT-1-div": true,
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
+ "Q-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-dM": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-body": true,
+ "Q-Proposed-UNBOOKMARK_TEXT-1-div": true,
+ "Q-Proposed-JUSTIFYCENTER_TEXT-1-dM": true,
+ "Q-Proposed-JUSTIFYCENTER_TEXT-1-body": true,
+ "Q-Proposed-JUSTIFYCENTER_TEXT-1-div": true,
+ "Q-Proposed-JUSTIFYFULL_TEXT-1-dM": true,
+ "Q-Proposed-JUSTIFYFULL_TEXT-1-body": true,
+ "Q-Proposed-JUSTIFYFULL_TEXT-1-div": true,
+ "Q-Proposed-JUSTIFYLEFT_TEXT-1-dM": true,
+ "Q-Proposed-JUSTIFYLEFT_TEXT-1-body": true,
+ "Q-Proposed-JUSTIFYLEFT_TEXT-1-div": true,
+ "Q-Proposed-JUSTIFYRIGHT_TEXT-1-dM": true,
+ "Q-Proposed-JUSTIFYRIGHT_TEXT-1-body": true,
+ "Q-Proposed-JUSTIFYRIGHT_TEXT-1-div": true,
+ "Q-Proposed-REMOVEFORMAT_TEXT-1-dM": true,
+ "Q-Proposed-REMOVEFORMAT_TEXT-1-body": true,
+ "Q-Proposed-REMOVEFORMAT_TEXT-1-div": true,
+ "Q-Proposed-COPY_TEXT-1-dM": true,
+ "Q-Proposed-COPY_TEXT-1-body": true,
+ "Q-Proposed-COPY_TEXT-1-div": true,
+ "Q-Proposed-CUT_TEXT-1-dM": true,
+ "Q-Proposed-CUT_TEXT-1-body": true,
+ "Q-Proposed-CUT_TEXT-1-div": true,
+ "Q-Proposed-PASTE_TEXT-1-dM": true,
+ "Q-Proposed-PASTE_TEXT-1-body": true,
+ "Q-Proposed-PASTE_TEXT-1-div": true,
+ "Q-Proposed-garbage-1_TEXT-1-dM": true,
+ "Q-Proposed-garbage-1_TEXT-1-body": true,
+ "Q-Proposed-garbage-1_TEXT-1-div": true,
+ "QE-Proposed-SELECTALL_TEXT-1-dM": true,
+ "QE-Proposed-SELECTALL_TEXT-1-body": true,
+ "QE-Proposed-SELECTALL_TEXT-1-div": true,
+ "QE-Proposed-UNSELECT_TEXT-1-dM": true,
+ "QE-Proposed-UNSELECT_TEXT-1-body": true,
+ "QE-Proposed-UNSELECT_TEXT-1-div": true,
+ "QE-Proposed-UNDO_TEXT-1-dM": true,
+ "QE-Proposed-UNDO_TEXT-1-body": true,
+ "QE-Proposed-UNDO_TEXT-1-div": true,
+ "QE-Proposed-REDO_TEXT-1-dM": true,
+ "QE-Proposed-REDO_TEXT-1-body": true,
+ "QE-Proposed-REDO_TEXT-1-div": true,
+ "QE-Proposed-BOLD_TEXT-1-dM": true,
+ "QE-Proposed-BOLD_TEXT-1-body": true,
+ "QE-Proposed-BOLD_TEXT-1-div": true,
+ "QE-Proposed-ITALIC_TEXT-1-dM": true,
+ "QE-Proposed-ITALIC_TEXT-1-body": true,
+ "QE-Proposed-ITALIC_TEXT-1-div": true,
+ "QE-Proposed-UNDERLINE_TEXT-1-dM": true,
+ "QE-Proposed-UNDERLINE_TEXT-1-body": true,
+ "QE-Proposed-UNDERLINE_TEXT-1-div": true,
+ "QE-Proposed-STRIKETHROUGH_TEXT-1-dM": true,
+ "QE-Proposed-STRIKETHROUGH_TEXT-1-body": true,
+ "QE-Proposed-STRIKETHROUGH_TEXT-1-div": true,
+ "QE-Proposed-SUBSCRIPT_TEXT-1-dM": true,
+ "QE-Proposed-SUBSCRIPT_TEXT-1-body": true,
+ "QE-Proposed-SUBSCRIPT_TEXT-1-div": true,
+ "QE-Proposed-SUPERSCRIPT_TEXT-1-dM": true,
+ "QE-Proposed-SUPERSCRIPT_TEXT-1-body": true,
+ "QE-Proposed-SUPERSCRIPT_TEXT-1-div": true,
+ "QE-Proposed-FORMATBLOCK_TEXT-1-dM": true,
+ "QE-Proposed-FORMATBLOCK_TEXT-1-body": true,
+ "QE-Proposed-FORMATBLOCK_TEXT-1-div": true,
+ "QE-Proposed-CREATELINK_TEXT-1-dM": true,
+ "QE-Proposed-CREATELINK_TEXT-1-body": true,
+ "QE-Proposed-CREATELINK_TEXT-1-div": true,
+ "QE-Proposed-UNLINK_TEXT-1-dM": true,
+ "QE-Proposed-UNLINK_TEXT-1-body": true,
+ "QE-Proposed-UNLINK_TEXT-1-div": true,
+ "QE-Proposed-INSERTHTML_TEXT-1-dM": true,
+ "QE-Proposed-INSERTHTML_TEXT-1-body": true,
+ "QE-Proposed-INSERTHTML_TEXT-1-div": true,
+ "QE-Proposed-INSERTHORIZONTALRULE_TEXT-1-dM": true,
+ "QE-Proposed-INSERTHORIZONTALRULE_TEXT-1-body": true,
+ "QE-Proposed-INSERTHORIZONTALRULE_TEXT-1-div": true,
+ "QE-Proposed-INSERTIMAGE_TEXT-1-dM": true,
+ "QE-Proposed-INSERTIMAGE_TEXT-1-body": true,
+ "QE-Proposed-INSERTIMAGE_TEXT-1-div": true,
+ "QE-Proposed-INSERTLINEBREAK_TEXT-1-dM": true,
+ "QE-Proposed-INSERTLINEBREAK_TEXT-1-body": true,
+ "QE-Proposed-INSERTLINEBREAK_TEXT-1-div": true,
+ "QE-Proposed-INSERTPARAGRAPH_TEXT-1-dM": true,
+ "QE-Proposed-INSERTPARAGRAPH_TEXT-1-body": true,
+ "QE-Proposed-INSERTPARAGRAPH_TEXT-1-div": true,
+ "QE-Proposed-INSERTORDEREDLIST_TEXT-1-dM": true,
+ "QE-Proposed-INSERTORDEREDLIST_TEXT-1-body": true,
+ "QE-Proposed-INSERTORDEREDLIST_TEXT-1-div": true,
+ "QE-Proposed-INSERTUNORDEREDLIST_TEXT-1-dM": true,
+ "QE-Proposed-INSERTUNORDEREDLIST_TEXT-1-body": true,
+ "QE-Proposed-INSERTUNORDEREDLIST_TEXT-1-div": true,
+ "QE-Proposed-INSERTTEXT_TEXT-1-dM": true,
+ "QE-Proposed-INSERTTEXT_TEXT-1-body": true,
+ "QE-Proposed-INSERTTEXT_TEXT-1-div": true,
+ "QE-Proposed-DELETE_TEXT-1-dM": true,
+ "QE-Proposed-DELETE_TEXT-1-body": true,
+ "QE-Proposed-DELETE_TEXT-1-div": true,
+ "QE-Proposed-FORWARDDELETE_TEXT-1-dM": true,
+ "QE-Proposed-FORWARDDELETE_TEXT-1-body": true,
+ "QE-Proposed-FORWARDDELETE_TEXT-1-div": true,
+ "QE-Proposed-STYLEWITHCSS_TEXT-1-dM": true,
+ "QE-Proposed-STYLEWITHCSS_TEXT-1-body": true,
+ "QE-Proposed-STYLEWITHCSS_TEXT-1-div": true,
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-dM": true,
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-body": true,
+ "QE-Proposed-CONTENTREADONLY_TEXT-1-div": true,
+ "QE-Proposed-BACKCOLOR_TEXT-1-dM": true,
+ "QE-Proposed-BACKCOLOR_TEXT-1-body": true,
+ "QE-Proposed-BACKCOLOR_TEXT-1-div": true,
+ "QE-Proposed-FORECOLOR_TEXT-1-dM": true,
+ "QE-Proposed-FORECOLOR_TEXT-1-body": true,
+ "QE-Proposed-FORECOLOR_TEXT-1-div": true,
+ "QE-Proposed-HILITECOLOR_TEXT-1-dM": true,
+ "QE-Proposed-HILITECOLOR_TEXT-1-body": true,
+ "QE-Proposed-HILITECOLOR_TEXT-1-div": true,
+ "QE-Proposed-FONTNAME_TEXT-1-dM": true,
+ "QE-Proposed-FONTNAME_TEXT-1-body": true,
+ "QE-Proposed-FONTNAME_TEXT-1-div": true,
+ "QE-Proposed-FONTSIZE_TEXT-1-dM": true,
+ "QE-Proposed-FONTSIZE_TEXT-1-body": true,
+ "QE-Proposed-FONTSIZE_TEXT-1-div": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-dM": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-body": true,
+ "QE-Proposed-INCREASEFONTSIZE_TEXT-1-div": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-dM": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-body": true,
+ "QE-Proposed-DECREASEFONTSIZE_TEXT-1-div": true,
+ "QE-Proposed-HEADING_TEXT-1-dM": true,
+ "QE-Proposed-HEADING_TEXT-1-body": true,
+ "QE-Proposed-HEADING_TEXT-1-div": true,
+ "QE-Proposed-INDENT_TEXT-1-dM": true,
+ "QE-Proposed-INDENT_TEXT-1-body": true,
+ "QE-Proposed-INDENT_TEXT-1-div": true,
+ "QE-Proposed-OUTDENT_TEXT-1-dM": true,
+ "QE-Proposed-OUTDENT_TEXT-1-body": true,
+ "QE-Proposed-OUTDENT_TEXT-1-div": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
+ "QE-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-dM": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-body": true,
+ "QE-Proposed-UNBOOKMARK_TEXT-1-div": true,
+ "QE-Proposed-JUSTIFYCENTER_TEXT-1-dM": true,
+ "QE-Proposed-JUSTIFYCENTER_TEXT-1-body": true,
+ "QE-Proposed-JUSTIFYCENTER_TEXT-1-div": true,
+ "QE-Proposed-JUSTIFYFULL_TEXT-1-dM": true,
+ "QE-Proposed-JUSTIFYFULL_TEXT-1-body": true,
+ "QE-Proposed-JUSTIFYFULL_TEXT-1-div": true,
+ "QE-Proposed-JUSTIFYLEFT_TEXT-1-dM": true,
+ "QE-Proposed-JUSTIFYLEFT_TEXT-1-body": true,
+ "QE-Proposed-JUSTIFYLEFT_TEXT-1-div": true,
+ "QE-Proposed-JUSTIFYRIGHT_TEXT-1-dM": true,
+ "QE-Proposed-JUSTIFYRIGHT_TEXT-1-body": true,
+ "QE-Proposed-JUSTIFYRIGHT_TEXT-1-div": true,
+ "QE-Proposed-REMOVEFORMAT_TEXT-1-dM": true,
+ "QE-Proposed-REMOVEFORMAT_TEXT-1-body": true,
+ "QE-Proposed-REMOVEFORMAT_TEXT-1-div": true,
+ "QE-Proposed-COPY_TEXT-1-dM": true,
+ "QE-Proposed-COPY_TEXT-1-body": true,
+ "QE-Proposed-COPY_TEXT-1-div": true,
+ "QE-Proposed-CUT_TEXT-1-dM": true,
+ "QE-Proposed-CUT_TEXT-1-body": true,
+ "QE-Proposed-CUT_TEXT-1-div": true,
+ "QE-Proposed-PASTE_TEXT-1-dM": true,
+ "QE-Proposed-PASTE_TEXT-1-body": true,
+ "QE-Proposed-PASTE_TEXT-1-div": true,
+ "QE-Proposed-garbage-1_TEXT-1-dM": true,
+ "QE-Proposed-garbage-1_TEXT-1-body": true,
+ "QE-Proposed-garbage-1_TEXT-1-div": true,
+ "QI-Proposed-SELECTALL_TEXT-1-dM": true,
+ "QI-Proposed-SELECTALL_TEXT-1-body": true,
+ "QI-Proposed-SELECTALL_TEXT-1-div": true,
+ "QI-Proposed-UNSELECT_TEXT-1-dM": true,
+ "QI-Proposed-UNSELECT_TEXT-1-body": true,
+ "QI-Proposed-UNSELECT_TEXT-1-div": true,
+ "QI-Proposed-UNDO_TEXT-1-dM": true,
+ "QI-Proposed-UNDO_TEXT-1-body": true,
+ "QI-Proposed-UNDO_TEXT-1-div": true,
+ "QI-Proposed-REDO_TEXT-1-dM": true,
+ "QI-Proposed-REDO_TEXT-1-body": true,
+ "QI-Proposed-REDO_TEXT-1-div": true,
+ "QI-Proposed-BOLD_TEXT-1-dM": true,
+ "QI-Proposed-BOLD_TEXT-1-body": true,
+ "QI-Proposed-BOLD_TEXT-1-div": true,
+ "QI-Proposed-ITALIC_TEXT-1-dM": true,
+ "QI-Proposed-ITALIC_TEXT-1-body": true,
+ "QI-Proposed-ITALIC_TEXT-1-div": true,
+ "QI-Proposed-UNDERLINE_TEXT-1-dM": true,
+ "QI-Proposed-UNDERLINE_TEXT-1-body": true,
+ "QI-Proposed-UNDERLINE_TEXT-1-div": true,
+ "QI-Proposed-STRIKETHROUGH_TEXT-1-dM": true,
+ "QI-Proposed-STRIKETHROUGH_TEXT-1-body": true,
+ "QI-Proposed-STRIKETHROUGH_TEXT-1-div": true,
+ "QI-Proposed-SUBSCRIPT_TEXT-1-dM": true,
+ "QI-Proposed-SUBSCRIPT_TEXT-1-body": true,
+ "QI-Proposed-SUBSCRIPT_TEXT-1-div": true,
+ "QI-Proposed-SUPERSCRIPT_TEXT-1-dM": true,
+ "QI-Proposed-SUPERSCRIPT_TEXT-1-body": true,
+ "QI-Proposed-SUPERSCRIPT_TEXT-1-div": true,
+ "QI-Proposed-FORMATBLOCK_TEXT-1-dM": true,
+ "QI-Proposed-FORMATBLOCK_TEXT-1-body": true,
+ "QI-Proposed-FORMATBLOCK_TEXT-1-div": true,
+ "QI-Proposed-CREATELINK_TEXT-1-dM": true,
+ "QI-Proposed-CREATELINK_TEXT-1-body": true,
+ "QI-Proposed-CREATELINK_TEXT-1-div": true,
+ "QI-Proposed-UNLINK_TEXT-1-dM": true,
+ "QI-Proposed-UNLINK_TEXT-1-body": true,
+ "QI-Proposed-UNLINK_TEXT-1-div": true,
+ "QI-Proposed-INSERTHTML_TEXT-1-dM": true,
+ "QI-Proposed-INSERTHTML_TEXT-1-body": true,
+ "QI-Proposed-INSERTHTML_TEXT-1-div": true,
+ "QI-Proposed-INSERTHORIZONTALRULE_TEXT-1-dM": true,
+ "QI-Proposed-INSERTHORIZONTALRULE_TEXT-1-body": true,
+ "QI-Proposed-INSERTHORIZONTALRULE_TEXT-1-div": true,
+ "QI-Proposed-INSERTIMAGE_TEXT-1-dM": true,
+ "QI-Proposed-INSERTIMAGE_TEXT-1-body": true,
+ "QI-Proposed-INSERTIMAGE_TEXT-1-div": true,
+ "QI-Proposed-INSERTLINEBREAK_TEXT-1-dM": true,
+ "QI-Proposed-INSERTLINEBREAK_TEXT-1-body": true,
+ "QI-Proposed-INSERTLINEBREAK_TEXT-1-div": true,
+ "QI-Proposed-INSERTPARAGRAPH_TEXT-1-dM": true,
+ "QI-Proposed-INSERTPARAGRAPH_TEXT-1-body": true,
+ "QI-Proposed-INSERTPARAGRAPH_TEXT-1-div": true,
+ "QI-Proposed-INSERTORDEREDLIST_TEXT-1-dM": true,
+ "QI-Proposed-INSERTORDEREDLIST_TEXT-1-body": true,
+ "QI-Proposed-INSERTORDEREDLIST_TEXT-1-div": true,
+ "QI-Proposed-INSERTUNORDEREDLIST_TEXT-1-dM": true,
+ "QI-Proposed-INSERTUNORDEREDLIST_TEXT-1-body": true,
+ "QI-Proposed-INSERTUNORDEREDLIST_TEXT-1-div": true,
+ "QI-Proposed-INSERTTEXT_TEXT-1-dM": true,
+ "QI-Proposed-INSERTTEXT_TEXT-1-body": true,
+ "QI-Proposed-INSERTTEXT_TEXT-1-div": true,
+ "QI-Proposed-DELETE_TEXT-1-dM": true,
+ "QI-Proposed-DELETE_TEXT-1-body": true,
+ "QI-Proposed-DELETE_TEXT-1-div": true,
+ "QI-Proposed-FORWARDDELETE_TEXT-1-dM": true,
+ "QI-Proposed-FORWARDDELETE_TEXT-1-body": true,
+ "QI-Proposed-FORWARDDELETE_TEXT-1-div": true,
+ "QI-Proposed-STYLEWITHCSS_TEXT-1-dM": true,
+ "QI-Proposed-STYLEWITHCSS_TEXT-1-body": true,
+ "QI-Proposed-STYLEWITHCSS_TEXT-1-div": true,
+ "QI-Proposed-CONTENTREADONLY_TEXT-1-dM": true,
+ "QI-Proposed-CONTENTREADONLY_TEXT-1-body": true,
+ "QI-Proposed-CONTENTREADONLY_TEXT-1-div": true,
+ "QI-Proposed-BACKCOLOR_TEXT-1-dM": true,
+ "QI-Proposed-BACKCOLOR_TEXT-1-body": true,
+ "QI-Proposed-BACKCOLOR_TEXT-1-div": true,
+ "QI-Proposed-FORECOLOR_TEXT-1-dM": true,
+ "QI-Proposed-FORECOLOR_TEXT-1-body": true,
+ "QI-Proposed-FORECOLOR_TEXT-1-div": true,
+ "QI-Proposed-HILITECOLOR_TEXT-1-dM": true,
+ "QI-Proposed-HILITECOLOR_TEXT-1-body": true,
+ "QI-Proposed-HILITECOLOR_TEXT-1-div": true,
+ "QI-Proposed-FONTNAME_TEXT-1-dM": true,
+ "QI-Proposed-FONTNAME_TEXT-1-body": true,
+ "QI-Proposed-FONTNAME_TEXT-1-div": true,
+ "QI-Proposed-FONTSIZE_TEXT-1-dM": true,
+ "QI-Proposed-FONTSIZE_TEXT-1-body": true,
+ "QI-Proposed-FONTSIZE_TEXT-1-div": true,
+ "QI-Proposed-INCREASEFONTSIZE_TEXT-1-dM": true,
+ "QI-Proposed-INCREASEFONTSIZE_TEXT-1-body": true,
+ "QI-Proposed-INCREASEFONTSIZE_TEXT-1-div": true,
+ "QI-Proposed-DECREASEFONTSIZE_TEXT-1-dM": true,
+ "QI-Proposed-DECREASEFONTSIZE_TEXT-1-body": true,
+ "QI-Proposed-DECREASEFONTSIZE_TEXT-1-div": true,
+ "QI-Proposed-HEADING_TEXT-1-dM": true,
+ "QI-Proposed-HEADING_TEXT-1-body": true,
+ "QI-Proposed-HEADING_TEXT-1-div": true,
+ "QI-Proposed-INDENT_TEXT-1-dM": true,
+ "QI-Proposed-INDENT_TEXT-1-body": true,
+ "QI-Proposed-INDENT_TEXT-1-div": true,
+ "QI-Proposed-OUTDENT_TEXT-1-dM": true,
+ "QI-Proposed-OUTDENT_TEXT-1-body": true,
+ "QI-Proposed-OUTDENT_TEXT-1-div": true,
+ "QI-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
+ "QI-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
+ "QI-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
+ "QI-Proposed-UNBOOKMARK_TEXT-1-dM": true,
+ "QI-Proposed-UNBOOKMARK_TEXT-1-body": true,
+ "QI-Proposed-UNBOOKMARK_TEXT-1-div": true,
+ "QI-Proposed-JUSTIFYCENTER_TEXT-1-dM": true,
+ "QI-Proposed-JUSTIFYCENTER_TEXT-1-body": true,
+ "QI-Proposed-JUSTIFYCENTER_TEXT-1-div": true,
+ "QI-Proposed-JUSTIFYFULL_TEXT-1-dM": true,
+ "QI-Proposed-JUSTIFYFULL_TEXT-1-body": true,
+ "QI-Proposed-JUSTIFYFULL_TEXT-1-div": true,
+ "QI-Proposed-JUSTIFYLEFT_TEXT-1-dM": true,
+ "QI-Proposed-JUSTIFYLEFT_TEXT-1-body": true,
+ "QI-Proposed-JUSTIFYLEFT_TEXT-1-div": true,
+ "QI-Proposed-JUSTIFYRIGHT_TEXT-1-dM": true,
+ "QI-Proposed-JUSTIFYRIGHT_TEXT-1-body": true,
+ "QI-Proposed-JUSTIFYRIGHT_TEXT-1-div": true,
+ "QI-Proposed-REMOVEFORMAT_TEXT-1-dM": true,
+ "QI-Proposed-REMOVEFORMAT_TEXT-1-body": true,
+ "QI-Proposed-REMOVEFORMAT_TEXT-1-div": true,
+ "QI-Proposed-COPY_TEXT-1-dM": true,
+ "QI-Proposed-COPY_TEXT-1-body": true,
+ "QI-Proposed-COPY_TEXT-1-div": true,
+ "QI-Proposed-CUT_TEXT-1-dM": true,
+ "QI-Proposed-CUT_TEXT-1-body": true,
+ "QI-Proposed-CUT_TEXT-1-div": true,
+ "QI-Proposed-PASTE_TEXT-1-dM": true,
+ "QI-Proposed-PASTE_TEXT-1-body": true,
+ "QI-Proposed-PASTE_TEXT-1-div": true,
+ "QI-Proposed-garbage-1_TEXT-1-dM": true,
+ "QI-Proposed-garbage-1_TEXT-1-body": true,
+ "QI-Proposed-garbage-1_TEXT-1-div": true,
+ "QS-Proposed-B_TEXT_SI-dM": true,
+ "QS-Proposed-B_TEXT_SI-body": true,
+ "QS-Proposed-B_TEXT_SI-div": true,
+ "QS-Proposed-B_B-1_SI-dM": true,
+ "QS-Proposed-B_B-1_SI-body": true,
+ "QS-Proposed-B_B-1_SI-div": true,
+ "QS-Proposed-B_STRONG-1_SI-dM": true,
+ "QS-Proposed-B_STRONG-1_SI-body": true,
+ "QS-Proposed-B_STRONG-1_SI-div": true,
+ "QS-Proposed-B_SPANs:fw:b-1_SI-dM": true,
+ "QS-Proposed-B_SPANs:fw:b-1_SI-body": true,
+ "QS-Proposed-B_SPANs:fw:b-1_SI-div": true,
+ "QS-Proposed-B_SPANs:fw:n-1_SI-dM": true,
+ "QS-Proposed-B_SPANs:fw:n-1_SI-body": true,
+ "QS-Proposed-B_SPANs:fw:n-1_SI-div": true,
+ "QS-Proposed-B_Bs:fw:n-1_SI-dM": true,
+ "QS-Proposed-B_Bs:fw:n-1_SI-body": true,
+ "QS-Proposed-B_Bs:fw:n-1_SI-div": true,
+ "QS-Proposed-B_B-SPANs:fw:n-1_SI-dM": true,
+ "QS-Proposed-B_B-SPANs:fw:n-1_SI-body": true,
+ "QS-Proposed-B_B-SPANs:fw:n-1_SI-div": true,
+ "QS-Proposed-B_SPAN.b-1-SI-dM": true,
+ "QS-Proposed-B_SPAN.b-1-SI-body": true,
+ "QS-Proposed-B_SPAN.b-1-SI-div": true,
+ "QS-Proposed-B_MYB-1-SI-dM": true,
+ "QS-Proposed-B_MYB-1-SI-body": true,
+ "QS-Proposed-B_MYB-1-SI-div": true,
+ "QS-Proposed-B_B-I-1_SC-dM": true,
+ "QS-Proposed-B_B-I-1_SC-body": true,
+ "QS-Proposed-B_B-I-1_SC-div": true,
+ "QS-Proposed-B_B-I-1_SL-dM": true,
+ "QS-Proposed-B_B-I-1_SL-body": true,
+ "QS-Proposed-B_B-I-1_SL-div": true,
+ "QS-Proposed-B_B-I-1_SR-dM": true,
+ "QS-Proposed-B_B-I-1_SR-body": true,
+ "QS-Proposed-B_B-I-1_SR-div": true,
+ "QS-Proposed-B_STRONG-I-1_SC-dM": true,
+ "QS-Proposed-B_STRONG-I-1_SC-body": true,
+ "QS-Proposed-B_STRONG-I-1_SC-div": true,
+ "QS-Proposed-B_B-I-U-1_SC-dM": true,
+ "QS-Proposed-B_B-I-U-1_SC-body": true,
+ "QS-Proposed-B_B-I-U-1_SC-div": true,
+ "QS-Proposed-B_B-I-U-1_SM-dM": true,
+ "QS-Proposed-B_B-I-U-1_SM-body": true,
+ "QS-Proposed-B_B-I-U-1_SM-div": true,
+ "QS-Proposed-B_TEXT-B-1_SO-1-dM": true,
+ "QS-Proposed-B_TEXT-B-1_SO-1-body": true,
+ "QS-Proposed-B_TEXT-B-1_SO-1-div": true,
+ "QS-Proposed-B_TEXT-B-1_SO-2-dM": true,
+ "QS-Proposed-B_TEXT-B-1_SO-2-body": true,
+ "QS-Proposed-B_TEXT-B-1_SO-2-div": true,
+ "QS-Proposed-B_TEXT-B-1_SL-dM": true,
+ "QS-Proposed-B_TEXT-B-1_SL-body": true,
+ "QS-Proposed-B_TEXT-B-1_SL-div": true,
+ "QS-Proposed-B_TEXT-B-1_SR-dM": true,
+ "QS-Proposed-B_TEXT-B-1_SR-body": true,
+ "QS-Proposed-B_TEXT-B-1_SR-div": true,
+ "QS-Proposed-B_TEXT-B-1_SO-3-dM": true,
+ "QS-Proposed-B_TEXT-B-1_SO-3-body": true,
+ "QS-Proposed-B_TEXT-B-1_SO-3-div": true,
+ "QS-Proposed-B_B.TEXT.B-1_SM-dM": true,
+ "QS-Proposed-B_B.TEXT.B-1_SM-body": true,
+ "QS-Proposed-B_B.TEXT.B-1_SM-div": true,
+ "QS-Proposed-B_B.B.B-1_SM-dM": true,
+ "QS-Proposed-B_B.B.B-1_SM-body": true,
+ "QS-Proposed-B_B.B.B-1_SM-div": true,
+ "QS-Proposed-B_B.STRONG.B-1_SM-dM": true,
+ "QS-Proposed-B_B.STRONG.B-1_SM-body": true,
+ "QS-Proposed-B_B.STRONG.B-1_SM-div": true,
+ "QS-Proposed-B_SPAN.b.MYB.SPANs:fw:b-1_SM-dM": true,
+ "QS-Proposed-B_SPAN.b.MYB.SPANs:fw:b-1_SM-body": true,
+ "QS-Proposed-B_SPAN.b.MYB.SPANs:fw:b-1_SM-div": true,
+ "QS-Proposed-I_TEXT_SI-dM": true,
+ "QS-Proposed-I_TEXT_SI-body": true,
+ "QS-Proposed-I_TEXT_SI-div": true,
+ "QS-Proposed-I_I-1_SI-dM": true,
+ "QS-Proposed-I_I-1_SI-body": true,
+ "QS-Proposed-I_I-1_SI-div": true,
+ "QS-Proposed-I_EM-1_SI-dM": true,
+ "QS-Proposed-I_EM-1_SI-body": true,
+ "QS-Proposed-I_EM-1_SI-div": true,
+ "QS-Proposed-I_SPANs:fs:i-1_SI-dM": true,
+ "QS-Proposed-I_SPANs:fs:i-1_SI-body": true,
+ "QS-Proposed-I_SPANs:fs:i-1_SI-div": true,
+ "QS-Proposed-I_SPANs:fs:n-1_SI-dM": true,
+ "QS-Proposed-I_SPANs:fs:n-1_SI-body": true,
+ "QS-Proposed-I_SPANs:fs:n-1_SI-div": true,
+ "QS-Proposed-I_I-SPANs:fs:n-1_SI-dM": true,
+ "QS-Proposed-I_I-SPANs:fs:n-1_SI-body": true,
+ "QS-Proposed-I_I-SPANs:fs:n-1_SI-div": true,
+ "QS-Proposed-I_SPAN.i-1-SI-dM": true,
+ "QS-Proposed-I_SPAN.i-1-SI-body": true,
+ "QS-Proposed-I_SPAN.i-1-SI-div": true,
+ "QS-Proposed-I_MYI-1-SI-dM": true,
+ "QS-Proposed-I_MYI-1-SI-body": true,
+ "QS-Proposed-I_MYI-1-SI-div": true,
+ "QS-Proposed-U_TEXT_SI-dM": true,
+ "QS-Proposed-U_TEXT_SI-body": true,
+ "QS-Proposed-U_TEXT_SI-div": true,
+ "QS-Proposed-U_U-1_SI-dM": true,
+ "QS-Proposed-U_U-1_SI-body": true,
+ "QS-Proposed-U_U-1_SI-div": true,
+ "QS-Proposed-U_Us:td:n-1_SI-dM": true,
+ "QS-Proposed-U_Us:td:n-1_SI-body": true,
+ "QS-Proposed-U_Us:td:n-1_SI-div": true,
+ "QS-Proposed-U_Ah:url-1_SI-dM": true,
+ "QS-Proposed-U_Ah:url-1_SI-body": true,
+ "QS-Proposed-U_Ah:url-1_SI-div": true,
+ "QS-Proposed-U_Ah:url.s:td:n-1_SI-dM": true,
+ "QS-Proposed-U_Ah:url.s:td:n-1_SI-body": true,
+ "QS-Proposed-U_Ah:url.s:td:n-1_SI-div": true,
+ "QS-Proposed-U_SPANs:td:u-1_SI-dM": true,
+ "QS-Proposed-U_SPANs:td:u-1_SI-body": true,
+ "QS-Proposed-U_SPANs:td:u-1_SI-div": true,
+ "QS-Proposed-U_SPAN.u-1-SI-dM": true,
+ "QS-Proposed-U_SPAN.u-1-SI-body": true,
+ "QS-Proposed-U_SPAN.u-1-SI-div": true,
+ "QS-Proposed-U_MYU-1-SI-dM": true,
+ "QS-Proposed-U_MYU-1-SI-body": true,
+ "QS-Proposed-U_MYU-1-SI-div": true,
+ "QS-Proposed-S_TEXT_SI-dM": true,
+ "QS-Proposed-S_TEXT_SI-body": true,
+ "QS-Proposed-S_TEXT_SI-div": true,
+ "QS-Proposed-S_S-1_SI-dM": true,
+ "QS-Proposed-S_S-1_SI-body": true,
+ "QS-Proposed-S_S-1_SI-div": true,
+ "QS-Proposed-S_STRIKE-1_SI-dM": true,
+ "QS-Proposed-S_STRIKE-1_SI-body": true,
+ "QS-Proposed-S_STRIKE-1_SI-div": true,
+ "QS-Proposed-S_STRIKEs:td:n-1_SI-dM": true,
+ "QS-Proposed-S_STRIKEs:td:n-1_SI-body": true,
+ "QS-Proposed-S_STRIKEs:td:n-1_SI-div": true,
+ "QS-Proposed-S_DEL-1_SI-dM": true,
+ "QS-Proposed-S_DEL-1_SI-body": true,
+ "QS-Proposed-S_DEL-1_SI-div": true,
+ "QS-Proposed-S_SPANs:td:lt-1_SI-dM": true,
+ "QS-Proposed-S_SPANs:td:lt-1_SI-body": true,
+ "QS-Proposed-S_SPANs:td:lt-1_SI-div": true,
+ "QS-Proposed-S_SPAN.s-1-SI-dM": true,
+ "QS-Proposed-S_SPAN.s-1-SI-body": true,
+ "QS-Proposed-S_SPAN.s-1-SI-div": true,
+ "QS-Proposed-S_MYS-1-SI-dM": true,
+ "QS-Proposed-S_MYS-1-SI-body": true,
+ "QS-Proposed-S_MYS-1-SI-div": true,
+ "QS-Proposed-S_S.STRIKE.DEL-1_SM-dM": true,
+ "QS-Proposed-S_S.STRIKE.DEL-1_SM-body": true,
+ "QS-Proposed-S_S.STRIKE.DEL-1_SM-div": true,
+ "QS-Proposed-SUB_TEXT_SI-dM": true,
+ "QS-Proposed-SUB_TEXT_SI-body": true,
+ "QS-Proposed-SUB_TEXT_SI-div": true,
+ "QS-Proposed-SUB_SUB-1_SI-dM": true,
+ "QS-Proposed-SUB_SUB-1_SI-body": true,
+ "QS-Proposed-SUB_SUB-1_SI-div": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-dM": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-body": true,
+ "QS-Proposed-SUB_SPAN.sub-1-SI-div": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-dM": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-body": true,
+ "QS-Proposed-SUB_MYSUB-1-SI-div": true,
+ "QS-Proposed-SUP_TEXT_SI-dM": true,
+ "QS-Proposed-SUP_TEXT_SI-body": true,
+ "QS-Proposed-SUP_TEXT_SI-div": true,
+ "QS-Proposed-SUP_SUP-1_SI-dM": true,
+ "QS-Proposed-SUP_SUP-1_SI-body": true,
+ "QS-Proposed-SUP_SUP-1_SI-div": true,
+ "QS-Proposed-IOL_TEXT_SI-dM": true,
+ "QS-Proposed-IOL_TEXT_SI-body": true,
+ "QS-Proposed-IOL_TEXT_SI-div": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-dM": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-body": true,
+ "QS-Proposed-SUP_SPAN.sup-1-SI-div": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-dM": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-body": true,
+ "QS-Proposed-SUP_MYSUP-1-SI-div": true,
+ "QS-Proposed-IOL_TEXT-1_SI-dM": true,
+ "QS-Proposed-IOL_TEXT-1_SI-body": true,
+ "QS-Proposed-IOL_TEXT-1_SI-div": true,
+ "QS-Proposed-IOL_OL-LI-1_SI-dM": true,
+ "QS-Proposed-IOL_OL-LI-1_SI-body": true,
+ "QS-Proposed-IOL_OL-LI-1_SI-div": true,
+ "QS-Proposed-IOL_UL_LI-1_SI-dM": true,
+ "QS-Proposed-IOL_UL_LI-1_SI-body": true,
+ "QS-Proposed-IOL_UL_LI-1_SI-div": true,
+ "QS-Proposed-IUL_TEXT_SI-dM": true,
+ "QS-Proposed-IUL_TEXT_SI-body": true,
+ "QS-Proposed-IUL_TEXT_SI-div": true,
+ "QS-Proposed-IUL_OL-LI-1_SI-dM": true,
+ "QS-Proposed-IUL_OL-LI-1_SI-body": true,
+ "QS-Proposed-IUL_OL-LI-1_SI-div": true,
+ "QS-Proposed-IUL_UL-LI-1_SI-dM": true,
+ "QS-Proposed-IUL_UL-LI-1_SI-body": true,
+ "QS-Proposed-IUL_UL-LI-1_SI-div": true,
+ "QS-Proposed-JC_TEXT_SI-dM": true,
+ "QS-Proposed-JC_TEXT_SI-body": true,
+ "QS-Proposed-JC_TEXT_SI-div": true,
+ "QS-Proposed-JC_DIVa:c-1_SI-dM": true,
+ "QS-Proposed-JC_DIVa:c-1_SI-body": true,
+ "QS-Proposed-JC_DIVa:c-1_SI-div": true,
+ "QS-Proposed-JC_Pa:c-1_SI-dM": true,
+ "QS-Proposed-JC_Pa:c-1_SI-body": true,
+ "QS-Proposed-JC_Pa:c-1_SI-div": true,
+ "QS-Proposed-JC_SPANs:ta:c-1_SI-dM": true,
+ "QS-Proposed-JC_SPANs:ta:c-1_SI-body": true,
+ "QS-Proposed-JC_SPANs:ta:c-1_SI-div": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-dM": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-body": true,
+ "QS-Proposed-JC_SPAN.jc-1-SI-div": true,
+ "QS-Proposed-JC_MYJC-1-SI-dM": true,
+ "QS-Proposed-JC_MYJC-1-SI-body": true,
+ "QS-Proposed-JC_MYJC-1-SI-div": true,
+ "QS-Proposed-JF_TEXT_SI-dM": true,
+ "QS-Proposed-JF_TEXT_SI-body": true,
+ "QS-Proposed-JF_TEXT_SI-div": true,
+ "QS-Proposed-JF_DIVa:j-1_SI-dM": true,
+ "QS-Proposed-JF_DIVa:j-1_SI-body": true,
+ "QS-Proposed-JF_DIVa:j-1_SI-div": true,
+ "QS-Proposed-JF_Pa:j-1_SI-dM": true,
+ "QS-Proposed-JF_Pa:j-1_SI-body": true,
+ "QS-Proposed-JF_Pa:j-1_SI-div": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-dM": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-body": true,
+ "QS-Proposed-JF_SPANs:ta:j-1_SI-div": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-dM": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-body": true,
+ "QS-Proposed-JF_SPAN.jf-1-SI-div": true,
+ "QS-Proposed-JF_MYJF-1-SI-dM": true,
+ "QS-Proposed-JF_MYJF-1-SI-body": true,
+ "QS-Proposed-JF_MYJF-1-SI-div": true,
+ "QS-Proposed-JL_TEXT_SI-dM": true,
+ "QS-Proposed-JL_TEXT_SI-body": true,
+ "QS-Proposed-JL_TEXT_SI-div": true,
+ "QS-Proposed-JL_DIVa:l-1_SI-dM": true,
+ "QS-Proposed-JL_DIVa:l-1_SI-body": true,
+ "QS-Proposed-JL_DIVa:l-1_SI-div": true,
+ "QS-Proposed-JL_Pa:l-1_SI-dM": true,
+ "QS-Proposed-JL_Pa:l-1_SI-body": true,
+ "QS-Proposed-JL_Pa:l-1_SI-div": true,
+ "QS-Proposed-JL_SPANs:ta:l-1_SI-dM": true,
+ "QS-Proposed-JL_SPANs:ta:l-1_SI-body": true,
+ "QS-Proposed-JL_SPANs:ta:l-1_SI-div": true,
+ "QS-Proposed-JL_SPAN.jl-1-SI-dM": true,
+ "QS-Proposed-JL_SPAN.jl-1-SI-body": true,
+ "QS-Proposed-JL_SPAN.jl-1-SI-div": true,
+ "QS-Proposed-JL_MYJL-1-SI-dM": true,
+ "QS-Proposed-JL_MYJL-1-SI-body": true,
+ "QS-Proposed-JL_MYJL-1-SI-div": true,
+ "QS-Proposed-JR_TEXT_SI-dM": true,
+ "QS-Proposed-JR_TEXT_SI-body": true,
+ "QS-Proposed-JR_TEXT_SI-div": true,
+ "QS-Proposed-JR_DIVa:r-1_SI-dM": true,
+ "QS-Proposed-JR_DIVa:r-1_SI-body": true,
+ "QS-Proposed-JR_DIVa:r-1_SI-div": true,
+ "QS-Proposed-JR_Pa:r-1_SI-dM": true,
+ "QS-Proposed-JR_Pa:r-1_SI-body": true,
+ "QS-Proposed-JR_Pa:r-1_SI-div": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-dM": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-body": true,
+ "QS-Proposed-JR_SPANs:ta:r-1_SI-div": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-dM": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-body": true,
+ "QS-Proposed-JR_SPAN.jr-1-SI-div": true,
+ "QS-Proposed-JR_MYJR-1-SI-dM": true,
+ "QS-Proposed-JR_MYJR-1-SI-body": true,
+ "QS-Proposed-JR_MYJR-1-SI-div": true,
+ "QV-Proposed-B_TEXT_SI-dM": true,
+ "QV-Proposed-B_TEXT_SI-body": true,
+ "QV-Proposed-B_TEXT_SI-div": true,
+ "QV-Proposed-B_B-1_SI-dM": true,
+ "QV-Proposed-B_B-1_SI-body": true,
+ "QV-Proposed-B_B-1_SI-div": true,
+ "QV-Proposed-B_STRONG-1_SI-dM": true,
+ "QV-Proposed-B_STRONG-1_SI-body": true,
+ "QV-Proposed-B_STRONG-1_SI-div": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-dM": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-body": true,
+ "QV-Proposed-B_SPANs:fw:b-1_SI-div": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-dM": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-body": true,
+ "QV-Proposed-B_SPANs:fw:n-1_SI-div": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-dM": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-body": true,
+ "QV-Proposed-B_Bs:fw:n-1_SI-div": true,
+ "QV-Proposed-B_SPAN.b-1_SI-dM": true,
+ "QV-Proposed-B_SPAN.b-1_SI-body": true,
+ "QV-Proposed-B_SPAN.b-1_SI-div": true,
+ "QV-Proposed-B_MYB-1-SI-dM": true,
+ "QV-Proposed-B_MYB-1-SI-body": true,
+ "QV-Proposed-B_MYB-1-SI-div": true,
+ "QV-Proposed-I_TEXT_SI-dM": true,
+ "QV-Proposed-I_TEXT_SI-body": true,
+ "QV-Proposed-I_TEXT_SI-div": true,
+ "QV-Proposed-I_I-1_SI-dM": true,
+ "QV-Proposed-I_I-1_SI-body": true,
+ "QV-Proposed-I_I-1_SI-div": true,
+ "QV-Proposed-I_EM-1_SI-dM": true,
+ "QV-Proposed-I_EM-1_SI-body": true,
+ "QV-Proposed-I_EM-1_SI-div": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-dM": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-body": true,
+ "QV-Proposed-I_SPANs:fs:i-1_SI-div": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-dM": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-body": true,
+ "QV-Proposed-I_SPANs:fs:n-1_SI-div": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-dM": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-body": true,
+ "QV-Proposed-I_I-SPANs:fs:n-1_SI-div": true,
+ "QV-Proposed-I_SPAN.i-1_SI-dM": true,
+ "QV-Proposed-I_SPAN.i-1_SI-body": true,
+ "QV-Proposed-I_SPAN.i-1_SI-div": true,
+ "QV-Proposed-I_MYI-1-SI-dM": true,
+ "QV-Proposed-I_MYI-1-SI-body": true,
+ "QV-Proposed-I_MYI-1-SI-div": true,
+ "QV-Proposed-FB_TEXT-1_SC-dM": true,
+ "QV-Proposed-FB_TEXT-1_SC-body": true,
+ "QV-Proposed-FB_TEXT-1_SC-div": true,
+ "QV-Proposed-FB_H1-1_SC-dM": true,
+ "QV-Proposed-FB_H1-1_SC-body": true,
+ "QV-Proposed-FB_H1-1_SC-div": true,
+ "QV-Proposed-FB_PRE-1_SC-dM": true,
+ "QV-Proposed-FB_PRE-1_SC-body": true,
+ "QV-Proposed-FB_PRE-1_SC-div": true,
+ "QV-Proposed-FB_BQ-1_SC-dM": true,
+ "QV-Proposed-FB_BQ-1_SC-body": true,
+ "QV-Proposed-FB_BQ-1_SC-div": true,
+ "QV-Proposed-FB_ADDRESS-1_SC-dM": true,
+ "QV-Proposed-FB_ADDRESS-1_SC-body": true,
+ "QV-Proposed-FB_ADDRESS-1_SC-div": true,
+ "QV-Proposed-FB_H1-H2-1_SC-dM": true,
+ "QV-Proposed-FB_H1-H2-1_SC-body": true,
+ "QV-Proposed-FB_H1-H2-1_SC-div": true,
+ "QV-Proposed-FB_H1-H2-1_SL-dM": true,
+ "QV-Proposed-FB_H1-H2-1_SL-body": true,
+ "QV-Proposed-FB_H1-H2-1_SL-div": true,
+ "QV-Proposed-FB_H1-H2-1_SR-dM": true,
+ "QV-Proposed-FB_H1-H2-1_SR-body": true,
+ "QV-Proposed-FB_H1-H2-1_SR-div": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-dM": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-body": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SL-div": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SR-dM": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SR-body": true,
+ "QV-Proposed-FB_TEXT-ADDRESS-1_SR-div": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-dM": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-body": true,
+ "QV-Proposed-FB_H1-H2.TEXT.H2-1_SM-div": true,
+ "QV-Proposed-H_H1-1_SC-dM": true,
+ "QV-Proposed-H_H1-1_SC-body": true,
+ "QV-Proposed-H_H1-1_SC-div": true,
+ "QV-Proposed-H_H3-1_SC-dM": true,
+ "QV-Proposed-H_H3-1_SC-body": true,
+ "QV-Proposed-H_H3-1_SC-div": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-dM": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-body": true,
+ "QV-Proposed-H_H1-H2-H3-H4-1_SC-div": true,
+ "QV-Proposed-H_P-1_SC-dM": true,
+ "QV-Proposed-H_P-1_SC-body": true,
+ "QV-Proposed-H_P-1_SC-div": true,
+ "QV-Proposed-FN_FONTf:a-1_SI-dM": true,
+ "QV-Proposed-FN_FONTf:a-1_SI-body": true,
+ "QV-Proposed-FN_FONTf:a-1_SI-div": true,
+ "QV-Proposed-FN_SPANs:ff:a-1_SI-dM": true,
+ "QV-Proposed-FN_SPANs:ff:a-1_SI-body": true,
+ "QV-Proposed-FN_SPANs:ff:a-1_SI-div": true,
+ "QV-Proposed-FN_FONTf:a.s:ff:c-1_SI-dM": true,
+ "QV-Proposed-FN_FONTf:a.s:ff:c-1_SI-body": true,
+ "QV-Proposed-FN_FONTf:a.s:ff:c-1_SI-div": true,
+ "QV-Proposed-FN_FONTf:a-FONTf:c-1_SI-dM": true,
+ "QV-Proposed-FN_FONTf:a-FONTf:c-1_SI-body": true,
+ "QV-Proposed-FN_FONTf:a-FONTf:c-1_SI-div": true,
+ "QV-Proposed-FN_SPANs:ff:c-FONTf:a-1_SI-dM": true,
+ "QV-Proposed-FN_SPANs:ff:c-FONTf:a-1_SI-body": true,
+ "QV-Proposed-FN_SPANs:ff:c-FONTf:a-1_SI-div": true,
+ "QV-Proposed-FN_SPAN.fs18px-1_SI-dM": true,
+ "QV-Proposed-FN_SPAN.fs18px-1_SI-body": true,
+ "QV-Proposed-FN_SPAN.fs18px-1_SI-div": true,
+ "QV-Proposed-FN_MYCOURIER-1-SI-dM": true,
+ "QV-Proposed-FN_MYCOURIER-1-SI-body": true,
+ "QV-Proposed-FN_MYCOURIER-1-SI-div": true,
+ "QV-Proposed-FS_FONTsz:4-1_SI-dM": true,
+ "QV-Proposed-FS_FONTsz:4-1_SI-body": true,
+ "QV-Proposed-FS_FONTsz:4-1_SI-div": true,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-dM": true,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-body": true,
+ "QV-Proposed-FS_FONTs:fs:l-1_SI-div": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-dM": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-body": true,
+ "QV-Proposed-FS_FONT.ass.s:fs:l-1_SI-div": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-dM": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-body": true,
+ "QV-Proposed-FS_FONTsz:1.s:fs:xl-1_SI-div": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-dM": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-body": true,
+ "QV-Proposed-FS_SPAN.large-1_SI-div": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-dM": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-body": true,
+ "QV-Proposed-FS_SPAN.fs18px-1_SI-div": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-dM": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-body": true,
+ "QV-Proposed-FA_MYLARGE-1-SI-div": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-dM": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-body": true,
+ "QV-Proposed-FA_MYFS18PX-1-SI-div": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-dM": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-body": true,
+ "QV-Proposed-BC_FONTs:bc:fca-1_SI-div": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-dM": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-body": true,
+ "QV-Proposed-BC_SPANs:bc:abc-1_SI-div": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-dM": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-body": true,
+ "QV-Proposed-BC_FONTs:bc:084-SPAN-1_SI-div": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-dM": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-body": true,
+ "QV-Proposed-BC_SPANs:bc:cde-SPAN-1_SI-div": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-dM": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-body": true,
+ "QV-Proposed-BC_SPAN.ass.s:bc:rgb-1_SI-div": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-dM": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-body": true,
+ "QV-Proposed-BC_SPAN.bcred-1_SI-div": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-dM": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-body": true,
+ "QV-Proposed-BC_MYBCRED-1-SI-div": true,
+ "QV-Proposed-FC_FONTc:f00-1_SI-dM": true,
+ "QV-Proposed-FC_FONTc:f00-1_SI-body": true,
+ "QV-Proposed-FC_FONTc:f00-1_SI-div": true,
+ "QV-Proposed-FC_SPANs:c:0f0-1_SI-dM": true,
+ "QV-Proposed-FC_SPANs:c:0f0-1_SI-body": true,
+ "QV-Proposed-FC_SPANs:c:0f0-1_SI-div": true,
+ "QV-Proposed-FC_FONTc:333.s:c:999-1_SI-dM": true,
+ "QV-Proposed-FC_FONTc:333.s:c:999-1_SI-body": true,
+ "QV-Proposed-FC_FONTc:333.s:c:999-1_SI-div": true,
+ "QV-Proposed-FC_FONTc:641-SPAN-1_SI-dM": true,
+ "QV-Proposed-FC_FONTc:641-SPAN-1_SI-body": true,
+ "QV-Proposed-FC_FONTc:641-SPAN-1_SI-div": true,
+ "QV-Proposed-FC_SPANs:c:d95-SPAN-1_SI-dM": true,
+ "QV-Proposed-FC_SPANs:c:d95-SPAN-1_SI-body": true,
+ "QV-Proposed-FC_SPANs:c:d95-SPAN-1_SI-div": true,
+ "QV-Proposed-FC_SPAN.red-1_SI-dM": true,
+ "QV-Proposed-FC_SPAN.red-1_SI-body": true,
+ "QV-Proposed-FC_SPAN.red-1_SI-div": true,
+ "QV-Proposed-FC_MYRED-1-SI-dM": true,
+ "QV-Proposed-FC_MYRED-1-SI-body": true,
+ "QV-Proposed-FC_MYRED-1-SI-div": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-dM": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-body": true,
+ "QV-Proposed-HC_FONTs:bc:fc0-1_SI-div": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-dM": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-body": true,
+ "QV-Proposed-HC_SPANs:bc:a0c-1_SI-div": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-dM": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-body": true,
+ "QV-Proposed-HC_SPAN.ass.s:bc:rgb-1_SI-div": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-dM": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-body": true,
+ "QV-Proposed-HC_FONTs:bc:83e-SPAN-1_SI-div": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-dM": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-body": true,
+ "QV-Proposed-HC_SPANs:bc:b12-SPAN-1_SI-div": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-dM": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-body": true,
+ "QV-Proposed-HC_SPAN.bcred-1_SI-div": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-dM": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-body": true,
+ "QV-Proposed-HC_MYBCRED-1-SI-div": true,
+ },
+};
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/current_revision b/editor/libeditor/tests/browserscope/lib/richtext2/current_revision
new file mode 100644
index 0000000000..cc34bb3975
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/current_revision
@@ -0,0 +1 @@
+805
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/platformFailures.js b/editor/libeditor/tests/browserscope/lib/richtext2/platformFailures.js
new file mode 100644
index 0000000000..61e1dad671
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/platformFailures.js
@@ -0,0 +1,28 @@
+/**
+ * Platform-specific failures not included in the main currentStatus.js list.
+ */
+var platformFailures;
+if (navigator.appVersion.includes("Android")) {
+ platformFailures = {
+ "value": {},
+ "select": {
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-1-dM": true,
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-1-body": true,
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-1-div": true,
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-2-dM": true,
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-2-body": true,
+ "S-Proposed-SM:m.f.w_TEXT-th_SC-2-div": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-1-dM": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-1-body": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-1-div": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-dM": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-body": true,
+ "S-Proposed-SM:m.b.w_TEXT-th_SC-2-div": true
+ }
+ }
+} else {
+ platformFailures = {
+ "value": {},
+ "select": {}
+ }
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/__init__.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/__init__.py
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/common.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/common.py
new file mode 100644
index 0000000000..345f9bbb00
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/common.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2010 Google Inc.
+#
+# 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.
+
+"""Common constants"""
+
+__author__ = 'rolandsteiner@google.com (Roland Steiner)'
+
+CATEGORY = 'richtext2'
+
+TEST_ID_PREFIX = 'RTE2'
+
+CLASSES = ['Finalized', 'RFC', 'Proposed'] \ No newline at end of file
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/handlers.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/handlers.py
new file mode 100644
index 0000000000..2ee1e79ad3
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/handlers.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2010 Google Inc.
+#
+# 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.
+
+"""Handlers for New Rich Text Tests"""
+
+__author__ = 'rolandsteiner@google.com (Roland Steiner)'
+
+from google.appengine.api import users
+from google.appengine.ext import db
+from google.appengine.api import memcache
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+import django
+from django import http
+from django import shortcuts
+
+from django.template import add_to_builtins
+add_to_builtins('base.custom_filters')
+
+# Shared stuff
+from categories import all_test_sets
+from base import decorators
+from base import util
+
+# common to the RichText2 suite
+from categories.richtext2 import common
+
+# tests
+from categories.richtext2.tests.apply import APPLY_TESTS
+from categories.richtext2.tests.applyCSS import APPLY_TESTS_CSS
+from categories.richtext2.tests.change import CHANGE_TESTS
+from categories.richtext2.tests.changeCSS import CHANGE_TESTS_CSS
+from categories.richtext2.tests.delete import DELETE_TESTS
+from categories.richtext2.tests.forwarddelete import FORWARDDELETE_TESTS
+from categories.richtext2.tests.insert import INSERT_TESTS
+from categories.richtext2.tests.selection import SELECTION_TESTS
+from categories.richtext2.tests.unapply import UNAPPLY_TESTS
+from categories.richtext2.tests.unapplyCSS import UNAPPLY_TESTS_CSS
+
+from categories.richtext2.tests.querySupported import QUERYSUPPORTED_TESTS
+from categories.richtext2.tests.queryEnabled import QUERYENABLED_TESTS
+from categories.richtext2.tests.queryIndeterm import QUERYINDETERM_TESTS
+from categories.richtext2.tests.queryState import QUERYSTATE_TESTS, QUERYSTATE_TESTS_CSS
+from categories.richtext2.tests.queryValue import QUERYVALUE_TESTS, QUERYVALUE_TESTS_CSS
+
+
+def About(request):
+ """About page."""
+ overview = """These tests cover browers' implementations of
+ <a href="http://blog.whatwg.org/the-road-to-html-5-contenteditable">contenteditable</a>
+ for basic rich text formatting commands. Most browser implementations do very
+ well at editing the HTML which is generated by their own execCommands. But a
+ big problem happens when developers try to make cross-browser web
+ applications using contenteditable - most browsers are not able to correctly
+ change formatting generated by other browsers. On top of that, most browsers
+ allow users to to paste arbitrary HTML from other webpages into a
+ contenteditable region, which is even harder for browsers to properly
+ format. These tests check how well the execCommand, queryCommandState,
+ and queryCommandValue functions work with different types of HTML."""
+ return util.About(request, common.CATEGORY, category_title='Rich Text',
+ overview=overview, show_hidden=False)
+
+
+def RunRichText2Tests(request):
+ params = {
+ 'classes': common.CLASSES,
+ 'commonIDPrefix': common.TEST_ID_PREFIX,
+ 'strict': False,
+ 'suites': [
+ SELECTION_TESTS,
+ APPLY_TESTS,
+ APPLY_TESTS_CSS,
+ CHANGE_TESTS,
+ CHANGE_TESTS_CSS,
+ UNAPPLY_TESTS,
+ UNAPPLY_TESTS_CSS,
+ DELETE_TESTS,
+ FORWARDDELETE_TESTS,
+ INSERT_TESTS,
+
+ QUERYSUPPORTED_TESTS,
+ QUERYENABLED_TESTS,
+ QUERYINDETERM_TESTS,
+ QUERYSTATE_TESTS,
+ QUERYSTATE_TESTS_CSS,
+ QUERYVALUE_TESTS,
+ QUERYVALUE_TESTS_CSS
+ ]
+ }
+ return shortcuts.render_to_response('%s/templates/richtext2.html' % common.CATEGORY, params)
+
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/common.css b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/common.css
new file mode 100644
index 0000000000..77c6bb8726
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/common.css
@@ -0,0 +1,116 @@
+.framed {
+ vertical-align: top;
+ margin: 8px;
+ border: 1px solid black;
+}
+
+.legend {
+ padding: 12px;
+ background-color: #f8f8ff;
+}
+
+.legendHdr {
+ font-size: large;
+ text-decoration: underline;
+}
+
+table.legend {
+ display: inline-table;
+}
+
+.suite-thead {
+ text-align: left;
+}
+
+.lo {
+ background-color: #dddddd;
+}
+.hi {
+ background-color: #eeeeee;
+}
+
+.lo .grey {
+ background-color: #dddddd;
+}
+.lo .na {
+ background-color: #dddddd;
+}
+.lo .pass {
+ background-color: #d4ffc0;
+}
+.lo .canary {
+ background-color: #ffcccc;
+}
+.lo .fail {
+ background-color: #ffcccc;
+}
+.lo .accept {
+ background-color: #ffffc0;
+}
+.lo .exception {
+ background-color: #f0d0f4;
+}
+.lo .unsupported {
+ background-color: #f0d0f4;
+}
+
+.hi .grey {
+ background-color: #eeeeee;
+}
+.hi .na {
+ background-color: #eeeeee;
+}
+.hi .pass {
+ background-color: #e0ffdc;
+}
+.hi .canary {
+ background-color: #ffd8d8;
+}
+.hi .fail {
+ background-color: #ffd8d8;
+}
+.hi .accept {
+ background-color: #ffffd8;
+}
+.hi .exception {
+ background-color: #f4dcf8;
+}
+.hi .unsupported {
+ background-color: #f4dcf8;
+}
+
+
+.sel {
+ color: blue;
+}
+
+.txt {
+ padding: 1px;
+ margin: 1px;
+ border: 1px solid #b0b0b0;
+}
+
+.idLabel {
+ font-size: small;
+}
+
+.fade {
+ color: grey;
+}
+.accexp {
+ color: #606070;
+}
+.comment {
+ color: grey;
+}
+
+.score {
+ color: #666666;
+}
+
+.fatalerror {
+ color: red;
+ font-size: large;
+ font-weight: bold;
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-body.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-body.html
new file mode 100644
index 0000000000..a254adc03e
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-body.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
+ <link rel="stylesheet" href="editable.css" type="text/css">
+</head>
+<body contentEditable="true">
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-dM.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-dM.html
new file mode 100644
index 0000000000..e16de3ab9f
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-dM.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
+ <link rel="stylesheet" href="editable.css" type="text/css">
+
+ <script>
+ function setDesignMode() {
+ window.document.designMode = "On";
+ }
+ </script>
+</head>
+<body onload="setDesignMode()">
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-div.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-div.html
new file mode 100644
index 0000000000..7dd600dbd8
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable-div.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
+ <link rel="stylesheet" href="editable.css" type="text/css">
+</head>
+<body>
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable.css b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable.css
new file mode 100644
index 0000000000..99fec49506
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/editable.css
@@ -0,0 +1,66 @@
+.b, myb {
+ font-weight: bold;
+}
+
+.i, myi {
+ font-style: italic;
+}
+
+.s, mys {
+ text-decoration: line-through;
+}
+
+.u, myu {
+ text-decoration: underline;
+}
+
+.sub, mysub {
+ vertical-align: sub;
+}
+
+.sup, mysup {
+ vertical-align: super;
+}
+
+.jc, myjc {
+ text-align: center;
+}
+
+.jf, myjf {
+ text-align: justify;
+}
+
+.jl, myjl {
+ text-align: left;
+}
+
+.jr, myjr {
+ text-align: right;
+}
+
+.red, myred {
+ color: red;
+}
+
+.bcred, mybcred {
+ background-color: red;
+}
+
+.large, mylarge {
+ font-size: large;
+}
+
+.fs18px, myfs18px {
+ font-size: 18px;
+}
+
+.courier, mycourier {
+ font-family: courier;
+}
+
+gen::before {
+ content: "[BEFORE]";
+}
+gen::after {
+ content: "[AFTER]";
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/canonicalize.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/canonicalize.js
new file mode 100644
index 0000000000..2236d9dfc5
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/canonicalize.js
@@ -0,0 +1,436 @@
+/**
+ * @fileoverview
+ * Canonicalization functions used in the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * Canonicalize HTML entities to their actual character
+ *
+ * @param str {String} the HTML string to be canonicalized
+ * @return {String} the canonicalized string
+ */
+
+function canonicalizeEntities(str) {
+ // TODO(rolandsteiner): this function is very much not optimized, but that shouldn't
+ // theoretically matter too much - look into it at some point.
+ var match;
+ while (match = str.match(/&#x([0-9A-F]+);/i)) {
+ str = str.replace('&#x' + match[1] + ';', String.fromCharCode(parseInt(match[1], 16)));
+ }
+ while (match = str.match(/&#([0-9]+);/)) {
+ str = str.replace('&#' + match[1] + ';', String.fromCharCode(Number(match[1])));
+ }
+ return str;
+}
+
+/**
+ * Canonicalize the contents of the HTML 'style' attribute.
+ * I.e. sorts the CSS attributes alphabetically and canonicalizes the values
+ * CSS attributes where necessary.
+ *
+ * If this would return an empty string, return null instead to suppress the
+ * whole 'style' attribute.
+ *
+ * Avoid tests that contain {, } or : within CSS values!
+ *
+ * Note that this function relies on the spaces of the input string already
+ * having been normalized by canonicalizeSpaces!
+ *
+ * FIXME: does not canonicalize the contents of compound attributes
+ * (e.g., 'border').
+ *
+ * @param str {String} contents of the 'style' attribute
+ * @param emitFlags {Object} flags used for this output
+ * @return {String/null} canonicalized string, null instead of the empty string
+ */
+function canonicalizeStyle(str, emitFlags) {
+ // Remove any enclosing curly brackets
+ str = str.replace(/ ?[\{\}] ?/g, '');
+
+ var attributes = str.split(';');
+ var count = attributes.length;
+ var resultArr = [];
+
+ for (var a = 0; a < count; ++a) {
+ // Retrieve "name: value" pair
+ // Note: may expectedly fail if the last pair was terminated with ';'
+ var avPair = attributes[a].match(/ ?([^ :]+) ?: ?(.+)/);
+ if (!avPair)
+ continue;
+
+ var name = avPair[1];
+ var value = avPair[2].replace(/ $/, ''); // Remove any trailing space.
+
+ switch (name) {
+ case 'color':
+ case 'background-color':
+ case 'border-color':
+ if (emitFlags.canonicalizeUnits) {
+ resultArr.push(name + ': #' + new Color(value).toHexString());
+ } else {
+ resultArr.push(name + ': ' + value);
+ }
+ break;
+
+ case 'font-family':
+ if (emitFlags.canonicalizeUnits) {
+ resultArr.push(name + ': ' + new FontName(value).toString());
+ } else {
+ resultArr.push(name + ': ' + value);
+ }
+ break;
+
+ case 'font-size':
+ if (emitFlags.canonicalizeUnits) {
+ resultArr.push(name + ': ' + new FontSize(value).toString());
+ } else {
+ resultArr.push(name + ': ' + value);
+ }
+ break;
+
+ default:
+ resultArr.push(name + ': ' + value);
+ }
+ }
+
+ // Sort by name, assuming no duplicate CSS attribute names.
+ resultArr.sort();
+
+ return resultArr.join('; ') || null;
+}
+
+/**
+ * Canonicalize a single attribute value.
+ *
+ * Note that this function relies on the spaces of the input string already
+ * having been normalized by canonicalizeSpaces!
+ *
+ * @param elemName {String} the name of the element
+ * @param attrName {String} the name of the attribute
+ * @param attrValue {String} the value of the attribute
+ * @param emitFlags {Object} flags used for this output
+ * @return {String/null} the canonicalized value, or null if the attribute should be skipped.
+ */
+function canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags) {
+ // We emit attributes as name="value", so change any contained apostrophes
+ // to quote marks.
+ attrValue = attrValue.replace(/\x22/, '\x27');
+
+ switch (attrName) {
+ case 'class':
+ return emitFlags.emitClass ? attrValue : null;
+
+ case 'id':
+ if (!emitFlags.emitID) {
+ return null;
+ }
+ if (attrValue && attrValue.substr(0, 7) == 'editor-') {
+ return null;
+ }
+ return attrValue;
+
+ // Remove empty style attributes, canonicalize the contents otherwise,
+ // provided the test cares for styles.
+ case 'style':
+ return (emitFlags.emitStyle && attrValue)
+ ? canonicalizeStyle(attrValue, emitFlags)
+ : null;
+
+ // Never output onload handlers as they are set by the test environment.
+ case 'onload':
+ return null;
+
+ // Canonicalize colors.
+ case 'bgcolor':
+ case 'color':
+ if (!attrValue) {
+ return null;
+ }
+ return emitFlags.canonicalizeUnits ? new Color(attrValue).toString() : attrValue;
+
+ // Canonicalize font names.
+ case 'face':
+ return emitFlags.canonicalizeUnits ? new FontName(attrValue).toString() : attrValue;
+
+ // Canonicalize font sizes (leave other 'size' attributes as-is).
+ case 'size':
+ if (!attrValue) {
+ return null;
+ }
+ switch (elemName) {
+ case 'basefont':
+ case 'font':
+ return emitFlags.canonicalizeUnits ? new FontSize(attrValue).toString() : attrValue;
+ }
+ return attrValue;
+
+ // Remove spans with value 1. Retain spans with other values, even if
+ // empty or with a value 0, since those indicate a flawed implementation.
+ case 'colspan':
+ case 'rowspan':
+ case 'span':
+ return (attrValue == '1' || attrValue === '') ? null : attrValue;
+
+ // Boolean attributes: presence equals true. If present, the value must be
+ // the empty string or the attribute's canonical name.
+ // (http://www.whatwg.org/specs/web-apps/current-work/#boolean-attributes)
+ // Below we only normalize empty string to the canonical name for
+ // comparison purposes. All other values are not touched and will therefore
+ // in all likelihood result in a failed test (even if they may be accepted
+ // by the UA).
+ case 'async':
+ case 'autofocus':
+ case 'checked':
+ case 'compact':
+ case 'declare':
+ case 'defer':
+ case 'disabled':
+ case 'formnovalidate':
+ case 'frameborder':
+ case 'ismap':
+ case 'loop':
+ case 'multiple':
+ case 'nohref':
+ case 'nosize':
+ case 'noshade':
+ case 'novalidate':
+ case 'nowrap':
+ case 'open':
+ case 'readonly':
+ case 'required':
+ case 'reversed':
+ case 'seamless':
+ case 'selected':
+ return attrValue ? attrValue : attrName;
+
+ default:
+ return attrValue;
+ }
+}
+
+/**
+ * Canonicalize the contents of an element tag.
+ *
+ * I.e. sorts the attributes alphabetically and canonicalizes their
+ * values where necessary. Also removes attributes we're not interested in.
+ *
+ * Note that this function relies on the spaces of the input string already
+ * having been normalized by canonicalizeSpaces!
+ *
+ * @param str {String} the contens of the element tag, excluding < and >.
+ * @param emitFlags {Object} flags used for this output
+ * @return {String} the canonicalized contents.
+ */
+function canonicalizeElementTag(str, emitFlags) {
+ // FIXME: lowercase only if emitFlags.lowercase is set
+ str = str.toLowerCase();
+
+ var pos = str.search(' ');
+
+ // element name only
+ if (pos == -1) {
+ return str;
+ }
+
+ var elemName = str.substr(0, pos);
+ str = str.substr(pos + 1);
+
+ // Even if emitFlags.emitAttrs is not set, we must iterate over the
+ // attributes to catch the special selection attribute and/or selection
+ // markers. :(
+
+ // Iterate over attributes, add them to an array, canonicalize their
+ // contents, and finally output the (remaining) attributes in sorted order.
+ // Note: We can't do a simple split on space here, because the value of,
+ // e.g., 'style' attributes may also contain spaces.
+ var attrs = [];
+ var selStartInTag = false;
+ var selEndInTag = false;
+
+ while (str) {
+ var attrName;
+ var attrValue = '';
+
+ pos = str.search(/[ =]/);
+ if (pos >= 0) {
+ attrName = str.substr(0, pos);
+ if (str.charAt(pos) == ' ') {
+ ++pos;
+ }
+ if (str.charAt(pos) == '=') {
+ ++pos;
+ if (str.charAt(pos) == ' ') {
+ ++pos;
+ }
+ str = str.substr(pos);
+ switch (str.charAt(0)) {
+ case '"':
+ case "'":
+ pos = str.indexOf(str.charAt(0), 1);
+ pos = (pos < 0) ? str.length : pos;
+ attrValue = str.substring(1, pos);
+ ++pos;
+ break;
+
+ default:
+ pos = str.indexOf(' ', 0);
+ pos = (pos < 0) ? str.length : pos;
+ attrValue = (pos == -1) ? str : str.substr(0, pos);
+ break;
+ }
+ attrValue = attrValue.replace(/^ /, '');
+ attrValue = attrValue.replace(/ $/, '');
+ }
+ } else {
+ attrName = str;
+ }
+ str = (pos == -1 || pos >= str.length) ? '' : str.substr(pos + 1);
+
+ // Remove special selection attributes.
+ switch (attrName) {
+ case ATTRNAME_SEL_START:
+ selStartInTag = true;
+ continue;
+
+ case ATTRNAME_SEL_END:
+ selEndInTag = true;
+ continue;
+ }
+
+ switch (attrName) {
+ case '':
+ case 'onload':
+ case 'xmlns':
+ break;
+
+ default:
+ if (!emitFlags.emitAttrs) {
+ break;
+ }
+ // >>> fall through >>>
+
+ case 'contenteditable':
+ attrValue = canonicalizeEntities(attrValue);
+ attrValue = canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags);
+ if (attrValue !== null) {
+ attrs.push(attrName + '="' + attrValue + '"');
+ }
+ }
+ }
+
+ var result = elemName;
+
+ // Sort alphabetically (on full string rather than just attribute value for
+ // simplicity. Also, attribute names will differ when encountering the '=').
+ if (attrs.length > 0) {
+ attrs.sort();
+ result += ' ' + attrs.join(' ');
+ }
+
+ // Add intra-tag selection marker(s) or attribute(s), if any, at the end.
+ if (selStartInTag && selEndInTag) {
+ result += ' |';
+ } else if (selStartInTag) {
+ result += ' {';
+ } else if (selEndInTag) {
+ result += ' }';
+ }
+
+ return result;
+}
+
+/**
+ * Canonicalize elements and attributes to facilitate comparison to the
+ * expectation string: sort attributes, canonicalize values and remove chaff.
+ *
+ * Note that this function relies on the spaces of the input string already
+ * having been normalized by canonicalizeSpaces!
+ *
+ * @param str {String} the HTML string to be canonicalized
+ * @param emitFlags {Object} flags used for this output
+ * @return {String} the canonicalized string
+ */
+function canonicalizeElementsAndAttributes(str, emitFlags) {
+ var tagStart = str.indexOf('<');
+ var tagEnd = 0;
+ var result = '';
+
+ while (tagStart >= 0) {
+ ++tagStart;
+ if (str.charAt(tagStart) == '/') {
+ ++tagStart;
+ }
+ result = result + canonicalizeEntities(str.substring(tagEnd, tagStart));
+ tagEnd = str.indexOf('>', tagStart);
+ if (tagEnd < 0) {
+ tagEnd = str.length - 1;
+ }
+ if (str.charAt(tagEnd - 1) == '/') {
+ --tagEnd;
+ }
+ var elemStr = str.substring(tagStart, tagEnd);
+ elemStr = canonicalizeElementTag(elemStr, emitFlags);
+ result = result + elemStr;
+ tagStart = str.indexOf('<', tagEnd);
+ }
+ return result + canonicalizeEntities(str.substring(tagEnd));
+}
+
+/**
+ * Canonicalize an innerHTML string to uniform single whitespaces.
+ *
+ * FIXME: running this prevents testing for pre-formatted content
+ * and the CSS 'white-space' attribute.
+ *
+ * @param str {String} the HTML string to be canonicalized
+ * @return {String} the canonicalized string
+ */
+function canonicalizeSpaces(str) {
+ // Collapse sequential whitespace.
+ str = str.replace(/\s+/g, ' ');
+
+ // Remove spaces immediately inside angle brackets <, >, </ and />.
+ // While doing this also canonicalize <.../> to <...>.
+ str = str.replace(/\< ?/g, '<');
+ str = str.replace(/\<\/ ?/g, '</');
+ str = str.replace(/ ?\/?\>/g, '>');
+
+ return str;
+}
+
+/**
+ * Canonicalize an innerHTML string to uniform single whitespaces.
+ * Also remove comments to retain only embedded selection markers, and
+ * remove </br> and </hr> if present.
+ *
+ * FIXME: running this prevents testing for pre-formatted content
+ * and the CSS 'white-space' attribute.
+ *
+ * @param str {String} the HTML string to be canonicalized
+ * @return {String} the canonicalized string
+ */
+function initialCanonicalizationOf(str) {
+ str = canonicalizeSpaces(str);
+ str = str.replace(/ ?<!-- ?/g, '');
+ str = str.replace(/ ?--> ?/g, '');
+ str = str.replace(/<\/[bh]r>/g, '');
+
+ return str;
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js
new file mode 100644
index 0000000000..be059cfc86
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/compare.js
@@ -0,0 +1,489 @@
+/**
+ * @fileoverview
+ * Comparison functions used in the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * constants used only in the compare functions.
+ */
+var RESULT_DIFF = 0; // actual result doesn't match expectation
+var RESULT_SEL = 1; // actual result matches expectation in HTML only
+var RESULT_EQUAL = 2; // actual result matches expectation in both HTML and selection
+
+/**
+ * Gets the test expectations as an array from the passed-in field.
+ *
+ * @param {Array|String} the test expectation(s) as string or array.
+ * @return {Array} test expectations as an array.
+ */
+function getExpectationArray(expected) {
+ if (expected === undefined) {
+ return [];
+ }
+ if (expected === null) {
+ return [null];
+ }
+ switch (typeof expected) {
+ case 'string':
+ case 'boolean':
+ case 'number':
+ return [expected];
+ }
+ // Assume it's already an array.
+ return expected;
+}
+
+/**
+ * Compare a test result to a single expectation string.
+ *
+ * FIXME: add support for optional elements/attributes.
+ *
+ * @param expected {String} the already canonicalized (with the exception of selection marks) expectation string
+ * @param actual {String} the already canonicalized (with the exception of selection marks) actual result
+ * @return {Integer} one of the RESULT_... return values
+ * @see variables.js for return values
+ */
+function compareHTMLToSingleExpectation(expected, actual) {
+ // If the test checks the selection, then the actual string must match the
+ // expectation exactly.
+ if (expected == actual) {
+ return RESULT_EQUAL;
+ }
+
+ // Remove selection markers and see if strings match then.
+ expected = expected.replace(/ [{}\|]>/g, '>'); // intra-tag
+ expected = expected.replace(/[\[\]\^{}\|]/g, ''); // outside tag
+ actual = actual.replace(/ [{}\|]>/g, '>'); // intra-tag
+ actual = actual.replace(/[\[\]\^{}\|]/g, ''); // outside tag
+
+ return (expected == actual) ? RESULT_SEL : RESULT_DIFF;
+}
+
+/**
+ * Compare the current HTMLtest result to the expectation string(s).
+ *
+ * @param actual {String/Boolean} actual value
+ * @param expected {String/Array} expectation(s)
+ * @param emitFlags {Object} flags to use for canonicalization
+ * @return {Integer} one of the RESULT_... return values
+ * @see variables.js for return values
+ */
+function compareHTMLToExpectation(actual, expected, emitFlags) {
+ // Find the most favorable result among the possible expectation strings.
+ var expectedArr = getExpectationArray(expected);
+ var count = expectedArr ? expectedArr.length : 0;
+ var best = RESULT_DIFF;
+
+ for (var idx = 0; idx < count && best < RESULT_EQUAL; ++idx) {
+ var expected = expectedArr[idx];
+ expected = canonicalizeSpaces(expected);
+ expected = canonicalizeElementsAndAttributes(expected, emitFlags);
+
+ var singleResult = compareHTMLToSingleExpectation(expected, actual);
+
+ best = Math.max(best, singleResult);
+ }
+ return best;
+}
+
+/**
+ * Compare the current HTMLtest result to expected and acceptable results
+ *
+ * @param expected {String/Array} expected result(s)
+ * @param accepted {String/Array} accepted result(s)
+ * @param actual {String} actual result
+ * @param emitFlags {Object} how to canonicalize the HTML strings
+ * @param result {Object} [out] object recieving the result of the comparison.
+ */
+function compareHTMLTestResultTo(expected, accepted, actual, emitFlags, result) {
+ actual = actual.replace(/[\x60\xb4]/g, '');
+ actual = canonicalizeElementsAndAttributes(actual, emitFlags);
+
+ var bestExpected = compareHTMLToExpectation(actual, expected, emitFlags);
+
+ if (bestExpected == RESULT_EQUAL) {
+ // Shortcut - it doesn't get any better
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_EQUAL;
+ return;
+ }
+
+ var bestAccepted = compareHTMLToExpectation(actual, accepted, emitFlags);
+
+ switch (bestExpected) {
+ case RESULT_SEL:
+ switch (bestAccepted) {
+ case RESULT_EQUAL:
+ // The HTML was equal to the/an expected HTML result as well
+ // (just not the selection there), therefore the difference
+ // between expected and accepted can only lie in the selection.
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_ACCEPT;
+ return;
+
+ case RESULT_SEL:
+ case RESULT_DIFF:
+ // The acceptable expectations did not yield a better result
+ // -> stay with the original (i.e., comparison to 'expected') result.
+ result.valresult = VALRESULT_EQUAL;
+ result.selresult = SELRESULT_DIFF;
+ return;
+ }
+ break;
+
+ case RESULT_DIFF:
+ switch (bestAccepted) {
+ case RESULT_EQUAL:
+ result.valresult = VALRESULT_ACCEPT;
+ result.selresult = SELRESULT_EQUAL;
+ return;
+
+ case RESULT_SEL:
+ result.valresult = VALRESULT_ACCEPT;
+ result.selresult = SELRESULT_DIFF;
+ return;
+
+ case RESULT_DIFF:
+ result.valresult = VALRESULT_DIFF;
+ result.selresult = SELRESULT_NA;
+ return;
+ }
+ break;
+ }
+
+ throw INTERNAL_ERR + HTML_COMPARISON;
+}
+
+/**
+ * Verify that the canaries are unviolated.
+ *
+ * @param container {Object} the test container descriptor as object reference
+ * @param result {Object} object reference that contains the result data
+ * @return {Boolean} whether the canaries' HTML is OK (selection flagged, but not fatal)
+ */
+function verifyCanaries(container, result) {
+ if (!container.canary) {
+ return true;
+ }
+
+ var str = canonicalizeElementsAndAttributes(result.bodyInnerHTML, emitFlagsForCanary);
+
+ if (str.length < 2 * container.canary.length) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ var strBefore = str.substr(0, container.canary.length);
+ var strAfter = str.substr(str.length - container.canary.length);
+
+ // Verify that the canary stretch doesn't contain any selection markers
+ if (SELECTION_MARKERS.test(strBefore) || SELECTION_MARKERS.test(strAfter)) {
+ str = str.replace(SELECTION_MARKERS, '');
+ if (str.length < 2 * container.canary.length) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ // Selection escaped contentEditable element, but HTML may still be ok.
+ result.selresult = SELRESULT_CANARY;
+ strBefore = str.substr(0, container.canary.length);
+ strAfter = str.substr(str.length - container.canary.length);
+ }
+
+ if (strBefore !== container.canary || strAfter !== container.canary) {
+ result.valresult = VALRESULT_CANARY;
+ result.selresult = SELRESULT_NA;
+ result.output = result.bodyOuterHTML;
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Compare the current HTMLtest result to the expectation string(s).
+ * Sets the global result variables.
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param container {Object} the test container description
+ * @param result {Object} [in/out] the result description, incl. HTML strings
+ * @see variables.js for result values
+ */
+function compareHTMLTestResult(suite, group, test, container, result) {
+ if (!verifyCanaries(container, result)) {
+ return;
+ }
+
+ var emitFlags = {
+ emitAttrs: getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES),
+ emitStyle: getTestParameter(suite, group, test, PARAM_CHECK_STYLE),
+ emitClass: getTestParameter(suite, group, test, PARAM_CHECK_CLASS),
+ emitID: getTestParameter(suite, group, test, PARAM_CHECK_ID),
+ lowercase: true,
+ canonicalizeUnits: true
+ };
+
+ // 2a.) Compare opening tag -
+ // decide whether to compare vs. outer or inner HTML based on this.
+ var openingTagEnd = result.outerHTML.indexOf('>') + 1;
+ var openingTag = result.outerHTML.substr(0, openingTagEnd);
+
+ openingTag = canonicalizeElementsAndAttributes(openingTag, emitFlags);
+ var tagCmp = compareHTMLToExpectation(openingTag, container.tagOpen, emitFlags);
+
+ if (tagCmp == RESULT_EQUAL) {
+ result.output = result.innerHTML;
+ compareHTMLTestResultTo(
+ getTestParameter(suite, group, test, PARAM_EXPECTED),
+ getTestParameter(suite, group, test, PARAM_ACCEPT),
+ result.innerHTML,
+ emitFlags,
+ result)
+ } else {
+ result.output = result.outerHTML;
+ compareHTMLTestResultTo(
+ getContainerParameter(suite, group, test, container, PARAM_EXPECTED_OUTER),
+ getContainerParameter(suite, group, test, container, PARAM_ACCEPT_OUTER),
+ result.outerHTML,
+ emitFlags,
+ result)
+ }
+}
+
+/**
+ * Insert a selection position indicator.
+ *
+ * @param node {DOMNode} the node where to insert the selection indicator
+ * @param offs {Integer} the offset of the selection indicator
+ * @param textInd {String} the indicator to use if the node is a text node
+ * @param elemInd {String} the indicator to use if the node is an element node
+ */
+function insertSelectionIndicator(node, offs, textInd, elemInd) {
+ switch (node.nodeType) {
+ case DOM_NODE_TYPE_TEXT:
+ // Insert selection marker for text node into text content.
+ var text = node.data;
+ node.data = text.substring(0, offs) + textInd + text.substring(offs);
+ break;
+
+ case DOM_NODE_TYPE_ELEMENT:
+ var child = node.firstChild;
+ try {
+ // node has other children: insert marker as comment node
+ var comment = document.createComment(elemInd);
+ while (child && offs) {
+ --offs;
+ child = child.nextSibling;
+ }
+ if (child) {
+ node.insertBefore(comment, child);
+ } else {
+ node.appendChild(comment);
+ }
+ } catch (ex) {
+ // can't append child comment -> insert as special attribute(s)
+ switch (elemInd) {
+ case '|':
+ node.setAttribute(ATTRNAME_SEL_START, '1');
+ node.setAttribute(ATTRNAME_SEL_END, '1');
+ break;
+
+ case '{':
+ node.setAttribute(ATTRNAME_SEL_START, '1');
+ break;
+
+ case '}':
+ node.setAttribute(ATTRNAME_SEL_END, '1');
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ * Adds quotes around all text nodes to show cases with non-normalized
+ * text nodes. Those are not a bug, but may still be usefil in helping to
+ * debug erroneous cases.
+ *
+ * @param node {DOMNode} root node from which to descend
+ */
+function encloseTextNodesWithQuotes(node) {
+ switch (node.nodeType) {
+ case DOM_NODE_TYPE_ELEMENT:
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ encloseTextNodesWithQuotes(node.childNodes[i]);
+ }
+ break;
+
+ case DOM_NODE_TYPE_TEXT:
+ node.data = '\x60' + node.data + '\xb4';
+ break;
+ }
+}
+
+/**
+ * Retrieve the result of a test run and do some preliminary canonicalization.
+ *
+ * @param container {Object} the container where to retrieve the result from as object reference
+ * @param result {Object} object reference that contains the result data
+ * @return {String} a preliminarily canonicalized innerHTML with selection markers
+ */
+function prepareHTMLTestResult(container, result) {
+ // Start with empty strings in case any of the below throws.
+ result.innerHTML = '';
+ result.outerHTML = '';
+
+ // 1.) insert selection markers
+ var selRange = createFromWindow(container.win);
+ if (selRange) {
+ // save values, since range object gets auto-modified
+ var node1 = selRange.getAnchorNode();
+ var offs1 = selRange.getAnchorOffset();
+ var node2 = selRange.getFocusNode();
+ var offs2 = selRange.getFocusOffset();
+
+ // add markers
+ if (node1 && node1 == node2 && offs1 == offs2) {
+ // collapsed selection
+ insertSelectionIndicator(node1, offs1, '^', '|');
+ } else {
+ // Start point and end point are different
+ if (node1) {
+ insertSelectionIndicator(node1, offs1, '[', '{');
+ }
+
+ if (node2) {
+ if (node1 == node2 && offs1 < offs2) {
+ // Anchor indicator was inserted under the same node, so we need
+ // to shift the offset by 1
+ ++offs2;
+ }
+ insertSelectionIndicator(node2, offs2, ']', '}');
+ }
+ }
+ }
+
+ // 2.) insert markers for text node boundaries;
+ encloseTextNodesWithQuotes(container.editor);
+
+ // 3.) retrieve inner and outer HTML
+ result.innerHTML = initialCanonicalizationOf(container.editor.innerHTML);
+ result.bodyInnerHTML = initialCanonicalizationOf(container.body.innerHTML);
+ if (goog.userAgent.IE) {
+ result.outerHTML = initialCanonicalizationOf(container.editor.outerHTML);
+ result.bodyOuterHTML = initialCanonicalizationOf(container.body.outerHTML);
+ result.outerHTML = result.outerHTML.replace(/^\s+/, '');
+ result.outerHTML = result.outerHTML.replace(/\s+$/, '');
+ result.bodyOuterHTML = result.bodyOuterHTML.replace(/^\s+/, '');
+ result.bodyOuterHTML = result.bodyOuterHTML.replace(/\s+$/, '');
+ } else {
+ result.outerHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.editor));
+ result.bodyOuterHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.body));
+ }
+}
+
+/**
+ * Compare a text test result to the expectation string(s).
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param actual {String/Boolean} actual value
+ * @param expected {String/Array} expectation(s)
+ * @return {Boolean} whether we found a match
+ */
+function compareTextTestResultWith(suite, group, test, actual, expected) {
+ var expectedArr = getExpectationArray(expected);
+ // Find the most favorable result among the possible expectation strings.
+ var count = expectedArr.length;
+
+ // If the value matches the expectation exactly, then we're fine.
+ for (var idx = 0; idx < count; ++idx) {
+ if (actual === expectedArr[idx])
+ return true;
+ }
+
+ // Otherwise see if we should canonicalize specific value types.
+ //
+ // We only need to look at font name, color and size units if the originating
+ // test was both a) queryCommandValue and b) querying a font name/color/size
+ // specific criterion.
+ //
+ // TODO(rolandsteiner): This is ugly! Refactor!
+ switch (getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
+ case 'backcolor':
+ case 'forecolor':
+ case 'hilitecolor':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new Color(actual).compare(new Color(expectedArr[idx])))
+ return true;
+ }
+ return false;
+
+ case 'fontname':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new FontName(actual).compare(new FontName(expectedArr[idx])))
+ return true;
+ }
+ return false;
+
+ case 'fontsize':
+ for (var idx = 0; idx < count; ++idx) {
+ if (new FontSize(actual).compare(new FontSize(expectedArr[idx])))
+ return true;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Compare the passed-in text test result to the expectation string(s).
+ * Sets the global result variables.
+ *
+ * @param suite {Object} the test suite as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test as object reference
+ * @param actual {String/Boolean} actual value
+ * @return {Integer} a RESUTLHTML... result value
+ * @see variables.js for result values
+ */
+function compareTextTestResult(suite, group, test, result) {
+ var expected = getTestParameter(suite, group, test, PARAM_EXPECTED);
+ if (compareTextTestResultWith(suite, group, test, result.output, expected)) {
+ result.valresult = VALRESULT_EQUAL;
+ return;
+ }
+ var accepted = getTestParameter(suite, group, test, PARAM_ACCEPT);
+ if (accepted && compareTextTestResultWith(suite, group, test, result.output, accepted)) {
+ result.valresult = VALRESULT_ACCEPT;
+ return;
+ }
+ result.valresult = VALRESULT_DIFF;
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/output.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/output.js
new file mode 100644
index 0000000000..897efa0112
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/output.js
@@ -0,0 +1,456 @@
+/**
+ * @fileoverview
+ * Functions used to format the test result output.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * Writes a fatal error to the output (replaces alert box)
+ *
+ * @param text {String} text to output
+ */
+function writeFatalError(text) {
+ var errorsStart = document.getElementById('errors');
+ var divider = document.getElementById('divider');
+ if (!errorsStart) {
+ errorsStart = document.createElement('hr');
+ errorsStart.id = 'errors';
+ divider.parentNode.insertBefore(errorsStart, divider);
+ }
+ var error = document.createElement('div');
+ error.className = 'fatalerror';
+ error.innerHTML = 'FATAL ERROR: ' + escapeOutput(text);
+ errorsStart.parentNode.insertBefore(error, divider);
+}
+
+/**
+ * Generates a unique ID for a given single test out of the suite ID and
+ * test ID.
+ *
+ * @param suiteID {string} ID string of the suite
+ * @param testID {string} ID string of the individual tests
+ * @return {string} globally unique ID
+ */
+function generateOutputID(suiteID, testID) {
+ return commonIDPrefix + '-' + suiteID + '_' + testID;
+}
+
+/**
+ * Function to highlight the selection markers
+ *
+ * @param str {String} a HTML string containing selection markers
+ * @return {String} the HTML string with highlighting tags around the markers
+ */
+function highlightSelectionMarkers(str) {
+ str = str.replace(/\[/g, '<span class="sel">[</span>');
+ str = str.replace(/\]/g, '<span class="sel">]</span>');
+ str = str.replace(/\^/g, '<span class="sel">^</span>');
+ str = str.replace(/{/g, '<span class="sel">{</span>');
+ str = str.replace(/}/g, '<span class="sel">}</span>');
+ str = str.replace(/\|/g, '<b class="sel">|</b>');
+ return str;
+}
+
+/**
+ * Function to highlight the selection markers
+ *
+ * @param str {String} a HTML string containing selection markers
+ * @return {String} the HTML string with highlighting tags around the markers
+ */
+function highlightSelectionMarkersAndTextNodes(str) {
+ str = highlightSelectionMarkers(str);
+ str = str.replace(/\x60/g, '<span class="txt">');
+ str = str.replace(/\xb4/g, '</span>');
+ return str;
+}
+
+/**
+ * Function to format output according to type
+ *
+ * @param value {String/Boolean} string or value to format
+ * @return {String} HTML-formatted string
+ */
+function formatValueOrString(value) {
+ if (value === undefined)
+ return '<i>undefined</i>';
+ if (value === null)
+ return '<i>null</i>';
+
+ switch (typeof value) {
+ case 'boolean':
+ return '<i>' + value.toString() + '</i>';
+
+ case 'number':
+ return value.toString();
+
+ case 'string':
+ return "'" + escapeOutput(value) + "'";
+
+ default:
+ return '<i>(' + escapeOutput(value.toString()) + ')</i>';
+ }
+}
+
+/**
+ * Function to highlight text nodes
+ *
+ * @param suite {Object} the suite the test belongs to
+ * @param group {Object} the group within the suite the test belongs to
+ * @param test {Object} the test description as object reference
+ * @param actual {String} a HTML string containing text nodes with markers
+ * @return {String} string with highlighting tags around the text node parts
+ */
+function formatActualResult(suite, group, test, actual) {
+ if (typeof actual != 'string')
+ return formatValueOrString(actual);
+
+ actual = escapeOutput(actual);
+
+ // Fade attributes (or just style) if not actually tested for
+ if (!getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES)) {
+ actual = actual.replace(/([^ =]+)=\x22([^\x22]*)\x22/g, '<span class="fade">$1="$2"</span>');
+ } else {
+ // NOTE: convert 'class="..."' first, before adding other <span class="fade">...</span> !!!
+ if (!getTestParameter(suite, group, test, PARAM_CHECK_CLASS)) {
+ actual = actual.replace(/class=\x22([^\x22]*)\x22/g, '<span class="fade">class="$1"</span>');
+ }
+ if (!getTestParameter(suite, group, test, PARAM_CHECK_STYLE)) {
+ actual = actual.replace(/style=\x22([^\x22]*)\x22/g, '<span class="fade">style="$1"</span>');
+ }
+ if (!getTestParameter(suite, group, test, PARAM_CHECK_ID)) {
+ actual = actual.replace(/id=\x22([^\x22]*)\x22/g, '<span class="fade">id="$1"</span>');
+ } else {
+ // fade out contenteditable host element's 'editor-<xyz>' ID.
+ actual = actual.replace(/id=\x22editor-([^\x22]*)\x22/g, '<span class="fade">id="editor-$1"</span>');
+ }
+ // grey out 'xmlns'
+ actual = actual.replace(/xmlns=\x22([^\x22]*)\x22/g, '<span class="fade">xmlns="$1"</span>');
+ // remove 'onload'
+ actual = actual.replace(/onload=\x22[^\x22]*\x22 ?/g, '');
+ }
+ // Highlight selection markers and text nodes.
+ actual = highlightSelectionMarkersAndTextNodes(actual);
+
+ return actual;
+}
+
+/**
+ * Escape text content for use with .innerHTML.
+ *
+ * @param str {String} HTML text to displayed
+ * @return {String} the escaped HTML
+ */
+function escapeOutput(str) {
+ return str ? str.replace(/\</g, '&lt;').replace(/\>/g, '&gt;') : '';
+}
+
+/**
+ * Fills in a single output table cell
+ *
+ * @param id {String} ID of the table cell
+ * @param val {String} inner HTML to set
+ * @param ttl {String, optional} value of the 'title' attribute
+ * @param cls {String, optional} class name for the cell
+ */
+function setTD(id, val, ttl, cls) {
+ var td = document.getElementById(id);
+ if (td) {
+ td.innerHTML = val;
+ if (ttl) {
+ td.title = ttl;
+ }
+ if (cls) {
+ td.className = cls;
+ }
+ }
+}
+
+/**
+ * Outputs the results of a single test suite
+ *
+ * @param suite {Object} test suite as object reference
+ * @param clsID {String} test class ID ('Proposed', 'RFC', 'Final')
+ * @param group {Object} the group of tests within the suite the test belongs to
+ * @param testIdx {Object} the test as object reference
+ */
+function outputTestResults(suite, clsID, group, test) {
+ var suiteID = suite.id;
+ var cls = suite[clsID];
+ var trID = generateOutputID(suiteID, test.id);
+ var testResult = results[suiteID][clsID][test.id];
+ var testValOut = VALOUTPUT[testResult.valresult];
+ var testSelOut = SELOUTPUT[testResult.selresult];
+
+ var suiteChecksSelOnly = !suiteChecksHTMLOrText(suite);
+ var testUsesHTML = !!getTestParameter(suite, group, test, PARAM_EXECCOMMAND) ||
+ !!getTestParameter(suite, group, test, PARAM_FUNCTION);
+
+ // Set background color for test ID
+ var td = document.getElementById(trID + IDOUT_TESTID);
+ if (td) {
+ td.className = (suiteChecksSelOnly && testResult.selresult != SELRESULT_NA) ? testSelOut.css : testValOut.css;
+ }
+
+ // Fill in "Command" and "Value" cells
+ var cmd;
+ var cmdOutput = '&nbsp;';
+ var valOutput = '&nbsp;';
+
+ if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
+ cmdOutput = escapeOutput(cmd);
+ var val = getTestParameter(suite, group, test, PARAM_VALUE);
+ if (val !== undefined) {
+ valOutput = formatValueOrString(val);
+ }
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
+ cmdOutput = '<i>' + escapeOutput(cmd) + '</i>';
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
+ cmdOutput = '<i>queryCommandSupported</i>';
+ valOutput = escapeOutput(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
+ cmdOutput = '<i>queryCommandEnabled</i>';
+ valOutput = escapeOutput(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
+ cmdOutput = '<i>queryCommandIndeterm</i>';
+ valOutput = escapeOutput(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
+ cmdOutput = '<i>queryCommandState</i>';
+ valOutput = escapeOutput(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
+ cmdOutput = '<i>queryCommandValue</i>';
+ valOutput = escapeOutput(cmd);
+ } else {
+ cmdOutput = '<i>(none)</i>';
+ }
+ setTD(trID + IDOUT_COMMAND, cmdOutput);
+ setTD(trID + IDOUT_VALUE, valOutput);
+
+ // Fill in "Attribute checked?" and "Style checked?" cells
+ if (testUsesHTML) {
+ var checkAttrs = getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES);
+ var checkStyle = getTestParameter(suite, group, test, PARAM_CHECK_STYLE);
+
+ setTD(trID + IDOUT_CHECKATTRS,
+ checkAttrs ? OUTSTR_YES : OUTSTR_NO,
+ checkAttrs ? 'attributes must match' : 'attributes are ignored');
+
+ if (checkAttrs && checkStyle) {
+ setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_YES, 'style attribute contents must match');
+ } else if (checkAttrs) {
+ setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'style attribute contents is ignored');
+ } else {
+ setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'all attributes (incl. style) are ignored');
+ }
+ } else {
+ setTD(trID + IDOUT_CHECKATTRS, OUTSTR_NA, 'attributes not applicable');
+ setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NA, 'style not applicable');
+ }
+
+ // Fill in test pad specification cell (initial HTML + selection markers)
+ setTD(trID + IDOUT_PAD, highlightSelectionMarkers(escapeOutput(getTestParameter(suite, group, test, PARAM_PAD))));
+
+ // Fill in expected result(s) cell
+ var expectedOutput = '';
+ var expectedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_EXPECTED));
+ for (var idx = 0; idx < expectedArr.length; ++idx) {
+ if (expectedOutput) {
+ expectedOutput += '\xA0\xA0\xA0<i>or</i><br>';
+ }
+ expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
+ : formatValueOrString(expectedArr[idx]);
+ }
+ var acceptedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_ACCEPT));
+ for (var idx = 0; idx < acceptedArr.length; ++idx) {
+ expectedOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br><span class="accexp">';
+ expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
+ : formatValueOrString(acceptedArr[idx]);
+ expectedOutput += '</span>';
+ }
+ // TODO(rolandsteiner): THIS IS UGLY, relying on 'div' container being index 2,
+ // AND not allowing other containers to have 'outer' results - change!!!
+ var outerOutput = '';
+ expectedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_EXPECTED_OUTER));
+ for (var idx = 0; idx < expectedArr.length; ++idx) {
+ if (outerOutput) {
+ outerOutput += '\xA0\xA0\xA0<i>or</i><br>';
+ }
+ outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
+ : formatValueOrString(expectedArr[idx]);
+ }
+ acceptedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_ACCEPT_OUTER));
+ for (var idx = 0; idx < acceptedArr.length; ++idx) {
+ if (outerOutput) {
+ outerOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br>';
+ }
+ outerOutput += '<span class="accexp">';
+ outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
+ : formatValueOrString(acceptedArr[idx]);
+ outerOutput += '</span>';
+ }
+ if (outerOutput) {
+ expectedOutput += '<hr>' + outerOutput;
+ }
+ setTD(trID + IDOUT_EXPECTED, expectedOutput);
+
+ // Iterate over the individual container results
+ for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
+ var cntID = containers[cntIdx].id;
+ var cntTD = document.getElementById(trID + IDOUT_CONTAINER + cntID);
+ var cntResult = testResult[cntID];
+ var cntValOut = VALOUTPUT[cntResult.valresult];
+ var cntSelOut = SELOUTPUT[cntResult.selresult];
+ var cssVal = cntValOut.css;
+ var cssSel = (!suiteChecksSelOnly || cntResult.selresult != SELRESULT_NA) ? cntSelOut.css : cssVal;
+ var cssCnt = cssVal;
+
+ // Fill in result status cell ("PASS", "ACC.", "FAIL", "EXC.", etc.)
+ setTD(trID + IDOUT_STATUSVAL + cntID, cntValOut.output, cntValOut.title, cssVal);
+
+ // Fill in selection status cell ("PASS", "ACC.", "FAIL", "N/A")
+ setTD(trID + IDOUT_STATUSSEL + cntID, cntSelOut.output, cntSelOut.title, cssSel);
+
+ // Fill in actual result
+ switch (cntResult.valresult) {
+ case VALRESULT_SETUP_EXCEPTION:
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ SETUP_EXCEPTION + '(mouseover)',
+ escapeOutput(cntResult.output),
+ cssVal);
+ break;
+
+ case VALRESULT_EXECUTION_EXCEPTION:
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ EXECUTION_EXCEPTION + '(mouseover)',
+ escapeOutput(cntResult.output.toString()),
+ cssVal);
+ break;
+
+ case VALRESULT_VERIFICATION_EXCEPTION:
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ VERIFICATION_EXCEPTION + '(mouseover)',
+ escapeOutput(cntResult.output.toString()),
+ cssVal);
+ break;
+
+ case VALRESULT_UNSUPPORTED:
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ escapeOutput(cntResult.output),
+ '',
+ cssVal);
+ break;
+
+ case VALRESULT_CANARY:
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
+ '',
+ cssVal);
+ break;
+
+ case VALRESULT_DIFF:
+ case VALRESULT_ACCEPT:
+ case VALRESULT_EQUAL:
+ if (!testUsesHTML) {
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ formatValueOrString(cntResult.output),
+ '',
+ cssVal);
+ } else if (cntResult.selresult == SELRESULT_CANARY) {
+ cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
+ '',
+ cssCnt);
+ } else {
+ cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ formatActualResult(suite, group, test, cntResult.output),
+ '',
+ cssCnt);
+ }
+ break;
+
+ default:
+ cssCnt = 'exception';
+ setTD(trID + IDOUT_ACTUAL + cntID,
+ INTERNAL_ERR + 'UNKNOWN RESULT VALUE',
+ '',
+ cssCnt);
+ }
+
+ if (cntTD) {
+ cntTD.className = cssCnt;
+ }
+ }
+}
+
+/**
+ * Outputs the results of a single test suite
+ *
+ * @param {Object} suite as object reference
+ */
+function outputTestSuiteResults(suite) {
+ var suiteID = suite.id;
+ var span;
+
+ span = document.getElementById(suiteID + '-score');
+ if (span) {
+ span.innerHTML = results[suiteID].valscore + '/' + results[suiteID].count;
+ }
+ span = document.getElementById(suiteID + '-selscore');
+ if (span) {
+ span.innerHTML = results[suiteID].selscore + '/' + results[suiteID].count;
+ }
+ span = document.getElementById(suiteID + '-time');
+ if (span) {
+ span.innerHTML = results[suiteID].time;
+ }
+ span = document.getElementById(suiteID + '-progress');
+ if (span) {
+ span.style.color = 'green';
+ }
+
+ for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
+ var clsID = testClassIDs[clsIdx];
+ var cls = suite[clsID];
+ if (!cls)
+ continue;
+
+ span = document.getElementById(suiteID + '-' + clsID + '-score');
+ if (span) {
+ span.innerHTML = results[suiteID][clsID].valscore + '/' + results[suiteID][clsID].count;
+ }
+ span = document.getElementById(suiteID + '-' + clsID + '-selscore');
+ if (span) {
+ span.innerHTML = results[suiteID][clsID].selscore + '/' + results[suiteID][clsID].count;
+ }
+
+ var groupCount = cls.length;
+
+ for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
+ var group = cls[groupIdx];
+ var testCount = group.tests.length;
+
+ for (var testIdx = 0; testIdx < testCount; ++testIdx) {
+ var test = group.tests[testIdx];
+
+ outputTestResults(suite, clsID, group, test);
+ }
+ }
+ }
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/pad.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/pad.js
new file mode 100644
index 0000000000..282f0d907d
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/pad.js
@@ -0,0 +1,269 @@
+/**
+ * @fileoverview
+ * Functions used to handle test and expectation strings.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * Normalize text selection indicators and convert inter-element selection
+ * indicators to comments.
+ *
+ * Note that this function relies on the spaces of the input string already
+ * having been normalized by canonicalizeSpaces!
+ *
+ * @param pad {String} HTML string that includes selection marker characters
+ * @return {String} the HTML string with the selection markers converted
+ */
+function convertSelectionIndicators(pad) {
+ // Sanity check: Markers { } | only directly before or after an element,
+ // or just before a closing > (i.e., not within a text node).
+ // Note that intra-tag selection markers have already been converted to the
+ // special selection attribute(s) above.
+ if (/[^>][{}\|][^<>]/.test(pad) ||
+ /^[{}\|][^<]/.test(pad) ||
+ /[^>][{}\|]$/.test(pad) ||
+ /^[{}\|]*$/.test(pad)) {
+ throw SETUP_BAD_SELECTION_SPEC;
+ }
+
+ // Convert intra-tag selection markers to special attributes.
+ pad = pad.replace(/\{\>/g, ATTRNAME_SEL_START + '="1">');
+ pad = pad.replace(/\}\>/g, ATTRNAME_SEL_END + '="1">');
+ pad = pad.replace(/\|\>/g, ATTRNAME_SEL_START + '="1" ' +
+ ATTRNAME_SEL_END + '="1">');
+
+ // Convert remaining {, }, | to comments with '[' and ']' data.
+ pad = pad.replace('{', '<!--[-->');
+ pad = pad.replace('}', '<!--]-->');
+ pad = pad.replace('|', '<!--[--><!--]-->');
+
+ // Convert caret indicator ^ to empty selection indicator []
+ // (this simplifies further processing).
+ pad = pad.replace(/\^/, '[]');
+
+ return pad;
+}
+
+/**
+ * Derives one point of the selection from the indicators with the HTML tree:
+ * '[' or ']' within a text or comment node, or the special selection
+ * attributes within an element node.
+ *
+ * @param root {DOMNode} root node of the recursive search
+ * @param marker {String} which marker to look for: '[' or ']'
+ * @return {Object} a pair object: {node: {DOMNode}/null, offset: {Integer}}
+ */
+function deriveSelectionPoint(root, marker) {
+ switch (root.nodeType) {
+ case DOM_NODE_TYPE_ELEMENT:
+ if (root.attributes) {
+ // Note: getAttribute() is necessary for this to work on all browsers!
+ if (marker == '[' && root.getAttribute(ATTRNAME_SEL_START)) {
+ root.removeAttribute(ATTRNAME_SEL_START);
+ return {node: root, offs: 0};
+ }
+ if (marker == ']' && root.getAttribute(ATTRNAME_SEL_END)) {
+ root.removeAttribute(ATTRNAME_SEL_END);
+ return {node: root, offs: 0};
+ }
+ }
+ for (var i = 0; i < root.childNodes.length; ++i) {
+ var pair = deriveSelectionPoint(root.childNodes[i], marker);
+ if (pair.node) {
+ return pair;
+ }
+ }
+ break;
+
+ case DOM_NODE_TYPE_TEXT:
+ var pos = root.data.indexOf(marker);
+ if (pos != -1) {
+ // Remove selection marker from text.
+ var nodeText = root.data;
+ root.data = nodeText.substr(0, pos) + nodeText.substr(pos + 1);
+ return {node: root, offs: pos };
+ }
+ break;
+
+ case DOM_NODE_TYPE_COMMENT:
+ var pos = root.data.indexOf(marker);
+ if (pos != -1) {
+ // Remove comment node from parent.
+ var helper = root.previousSibling;
+
+ for (pos = 0; helper; ++pos ) {
+ helper = helper.previousSibling;
+ }
+ helper = root;
+ root = root.parentNode;
+ root.removeChild(helper);
+ return {node: root, offs: pos };
+ }
+ break;
+ }
+
+ return {node: null, offs: 0 };
+}
+
+/**
+ * Initialize the test HTML with the starting state specified in the test.
+ *
+ * The selection is specified "inline", using special characters:
+ * ^ a collapsed text caret selection (same as [])
+ * [ the selection start within a text node
+ * ] the selection end within a text node
+ * | collapsed selection between elements (same as {})
+ * { selection starting with the following element
+ * } selection ending with the preceding element
+ * {, } and | can also be used within an element tag, just before the closing
+ * angle bracket > to specify a selection [element, 0] where the element
+ * doesn't otherwise have any children. Ex.: <hr {>foobarbaz<hr }>
+ *
+ * Explicit and implicit specification can also be mixed between the 2 points.
+ *
+ * A pad string must only contain at most ONE of the above that is suitable for
+ * that start or end point, respectively, and must contain either both or none.
+ *
+ * @param suite {Object} suite that test originates in as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} test to be run as object reference
+ * @param container {Object} container descriptor as object reference
+ */
+function initContainer(suite, group, test, container) {
+ var pad = getTestParameter(suite, group, test, PARAM_PAD);
+ pad = canonicalizeSpaces(pad);
+ pad = convertSelectionIndicators(pad);
+
+ if (container.editorID) {
+ container.body.innerHTML = container.canary + container.tagOpen + pad + container.tagClose + container.canary;
+ container.editor = container.doc.getElementById(container.editorID);
+ } else {
+ container.body.innerHTML = pad;
+ container.editor = container.body;
+ }
+
+ win = container.win;
+ doc = container.doc;
+ body = container.body;
+ editor = container.editor;
+ sel = null;
+
+ if (!editor) {
+ throw SETUP_CONTAINER;
+ }
+
+ if (getTestParameter(suite, group, test, PARAM_STYLE_WITH_CSS)) {
+ try {
+ container.doc.execCommand('styleWithCSS', false, true);
+ } catch (ex) {
+ // ignore exception if unsupported
+ }
+ }
+
+ var selAnchor = deriveSelectionPoint(editor, '[');
+ var selFocus = deriveSelectionPoint(editor, ']');
+
+ // sanity check
+ if (!selAnchor || !selFocus) {
+ throw SETUP_SELECTION;
+ }
+
+ if (!selAnchor.node || !selFocus.node) {
+ if (selAnchor.node || selFocus.node) {
+ // Broken test: only one selection point was specified
+ throw SETUP_BAD_SELECTION_SPEC;
+ }
+ sel = null;
+ return;
+ }
+
+ if (selAnchor.node === selFocus.node) {
+ if (selAnchor.offs > selFocus.offs) {
+ // Both selection points are within the same node, the selection was
+ // specified inline (thus the end indicator element or character was
+ // removed), and the end point is before the start (reversed selection).
+ // Start offset that was derived is now off by 1 and needs adjustment.
+ --selAnchor.offs;
+ }
+
+ if (selAnchor.offs === selFocus.offs) {
+ createCaret(selAnchor.node, selAnchor.offs).select();
+ try {
+ sel = win.getSelection();
+ } catch (ex) {
+ sel = undefined;
+ }
+ return;
+ }
+ }
+
+ createFromNodes(selAnchor.node, selAnchor.offs, selFocus.node, selFocus.offs).select();
+
+ try {
+ sel = win.getSelection();
+ } catch (ex) {
+ sel = undefined;
+ }
+}
+
+/**
+ * Reset the editor element after a test is run.
+ *
+ * @param container {Object} container descriptor as object reference
+ */
+function resetContainer(container) {
+ // Remove errant styles and attributes that may have been set on the <body>.
+ container.body.removeAttribute('style');
+ container.body.removeAttribute('color');
+ container.body.removeAttribute('bgcolor');
+
+ try {
+ container.doc.execCommand('styleWithCSS', false, false);
+ } catch (ex) {
+ // Ignore exception if unsupported.
+ }
+}
+
+/**
+ * Initialize the editor document.
+ */
+function initEditorDocs() {
+ for (var c = 0; c < containers.length; ++c) {
+ var container = containers[c];
+
+ container.iframe = document.getElementById('iframe-' + container.id);
+ container.win = container.iframe.contentWindow;
+ container.doc = container.win.document;
+ container.body = container.doc.body;
+ // container.editor is set per test (changes on embedded editor elements).
+
+ // Some browsers require a selection to go with their 'styleWithCSS'.
+ try {
+ container.win.getSelection().selectAllChildren(editor);
+ } catch (ex) {
+ // ignore exception if unsupported
+ }
+ // Default styleWithCSS to false.
+ try {
+ container.doc.execCommand('styleWithCSS', false, false);
+ } catch (ex) {
+ // ignore exception if unsupported
+ }
+ }
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range-bootstrap.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range-bootstrap.js
new file mode 100644
index 0000000000..24aef7ae9c
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range-bootstrap.js
@@ -0,0 +1,5 @@
+goog.require('goog.dom.Range');
+
+window.createFromWindow = goog.dom.Range.createFromWindow;
+window.createFromNodes = goog.dom.Range.createFromNodes;
+window.createCaret = goog.dom.Range.createCaret;
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range.js
new file mode 100644
index 0000000000..3266761115
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/range.js
@@ -0,0 +1,6184 @@
+var COMPILED = false;
+var goog = goog || {};
+goog.global = this;
+goog.DEBUG = true;
+goog.LOCALE = "en";
+goog.evalWorksForGlobals_ = null;
+goog.provide = function(name) {
+ if(!COMPILED) {
+ if(goog.getObjectByName(name) && !goog.implicitNamespaces_[name]) {
+ throw Error('Namespace "' + name + '" already declared.');
+ }
+ var namespace = name;
+ while(namespace = namespace.substring(0, namespace.lastIndexOf("."))) {
+ goog.implicitNamespaces_[namespace] = true
+ }
+ }
+ goog.exportPath_(name)
+};
+if(!COMPILED) {
+ goog.implicitNamespaces_ = {}
+}
+goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split(".");
+ var cur = opt_objectToExportTo || goog.global;
+ if(!(parts[0] in cur) && cur.execScript) {
+ cur.execScript("var " + parts[0])
+ }
+ for(var part;parts.length && (part = parts.shift());) {
+ if(!parts.length && goog.isDef(opt_object)) {
+ cur[part] = opt_object
+ }else {
+ if(cur[part]) {
+ cur = cur[part]
+ }else {
+ cur = cur[part] = {}
+ }
+ }
+ }
+};
+goog.getObjectByName = function(name, opt_obj) {
+ var parts = name.split(".");
+ var cur = opt_obj || goog.global;
+ for(var part;part = parts.shift();) {
+ if(cur[part]) {
+ cur = cur[part]
+ }else {
+ return null
+ }
+ }
+ return cur
+};
+goog.globalize = function(obj, opt_global) {
+ var global = opt_global || goog.global;
+ for(var x in obj) {
+ global[x] = obj[x]
+ }
+};
+goog.addDependency = function(relPath, provides, requires) {
+ if(!COMPILED) {
+ var provide, require;
+ var path = relPath.replace(/\\/g, "/");
+ var deps = goog.dependencies_;
+ for(var i = 0;provide = provides[i];i++) {
+ deps.nameToPath[provide] = path;
+ if(!(path in deps.pathToNames)) {
+ deps.pathToNames[path] = {}
+ }
+ deps.pathToNames[path][provide] = true
+ }
+ for(var j = 0;require = requires[j];j++) {
+ if(!(path in deps.requires)) {
+ deps.requires[path] = {}
+ }
+ deps.requires[path][require] = true
+ }
+ }
+};
+goog.require = function(rule) {
+ if(!COMPILED) {
+ if(goog.getObjectByName(rule)) {
+ return
+ }
+ var path = goog.getPathFromDeps_(rule);
+ if(path) {
+ goog.included_[path] = true;
+ goog.writeScripts_()
+ }else {
+ var errorMessage = "goog.require could not find: " + rule;
+ if(goog.global.console) {
+ goog.global.console["error"](errorMessage)
+ }
+ throw Error(errorMessage);
+ }
+ }
+};
+goog.basePath = "";
+goog.global.CLOSURE_BASE_PATH;
+goog.nullFunction = function() {
+};
+goog.identityFunction = function(var_args) {
+ return arguments[0]
+};
+goog.abstractMethod = function() {
+ throw Error("unimplemented abstract method");
+};
+goog.addSingletonGetter = function(ctor) {
+ ctor.getInstance = function() {
+ return ctor.instance_ || (ctor.instance_ = new ctor)
+ }
+};
+if(!COMPILED) {
+ goog.included_ = {};
+ goog.dependencies_ = {pathToNames:{}, nameToPath:{}, requires:{}, visited:{}, written:{}};
+ goog.inHtmlDocument_ = function() {
+ var doc = goog.global.document;
+ return typeof doc != "undefined" && "write" in doc
+ };
+ goog.findBasePath_ = function() {
+ if(!goog.inHtmlDocument_()) {
+ return
+ }
+ var doc = goog.global.document;
+ if(goog.global.CLOSURE_BASE_PATH) {
+ goog.basePath = goog.global.CLOSURE_BASE_PATH;
+ return
+ }
+ var scripts = doc.getElementsByTagName("script");
+ for(var i = scripts.length - 1;i >= 0;--i) {
+ var src = scripts[i].src;
+ var l = src.length;
+ if(src.substr(l - 7) == "base.js") {
+ goog.basePath = src.substr(0, l - 7);
+ return
+ }
+ }
+ };
+ goog.writeScriptTag_ = function(src) {
+ if(goog.inHtmlDocument_() && !goog.dependencies_.written[src]) {
+ goog.dependencies_.written[src] = true;
+ var doc = goog.global.document;
+ doc.write('<script type="text/javascript" src="' + src + '"></' + "script>")
+ }
+ };
+ goog.writeScripts_ = function() {
+ var scripts = [];
+ var seenScript = {};
+ var deps = goog.dependencies_;
+ function visitNode(path) {
+ if(path in deps.written) {
+ return
+ }
+ if(path in deps.visited) {
+ if(!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path)
+ }
+ return
+ }
+ deps.visited[path] = true;
+ if(path in deps.requires) {
+ for(var requireName in deps.requires[path]) {
+ if(requireName in deps.nameToPath) {
+ visitNode(deps.nameToPath[requireName])
+ }else {
+ if(!goog.getObjectByName(requireName)) {
+ throw Error("Undefined nameToPath for " + requireName);
+ }
+ }
+ }
+ }
+ if(!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path)
+ }
+ }
+ for(var path in goog.included_) {
+ if(!deps.written[path]) {
+ visitNode(path)
+ }
+ }
+ for(var i = 0;i < scripts.length;i++) {
+ if(scripts[i]) {
+ goog.writeScriptTag_(goog.basePath + scripts[i])
+ }else {
+ throw Error("Undefined script input");
+ }
+ }
+ };
+ goog.getPathFromDeps_ = function(rule) {
+ if(rule in goog.dependencies_.nameToPath) {
+ return goog.dependencies_.nameToPath[rule]
+ }else {
+ return null
+ }
+ };
+ goog.findBasePath_();
+}
+goog.typeOf = function(value) {
+ var s = typeof value;
+ if(s == "object") {
+ if(value) {
+ if(value instanceof Array || !(value instanceof Object) && Object.prototype.toString.call(value) == "[object Array]" || typeof value.length == "number" && typeof value.splice != "undefined" && typeof value.propertyIsEnumerable != "undefined" && !value.propertyIsEnumerable("splice")) {
+ return"array"
+ }
+ if(!(value instanceof Object) && (Object.prototype.toString.call(value) == "[object Function]" || typeof value.call != "undefined" && typeof value.propertyIsEnumerable != "undefined" && !value.propertyIsEnumerable("call"))) {
+ return"function"
+ }
+ }else {
+ return"null"
+ }
+ }else {
+ if(s == "function" && typeof value.call == "undefined") {
+ return"object"
+ }
+ }
+ return s
+};
+goog.propertyIsEnumerableCustom_ = function(object, propName) {
+ if(propName in object) {
+ for(var key in object) {
+ if(key == propName && Object.prototype.hasOwnProperty.call(object, propName)) {
+ return true
+ }
+ }
+ }
+ return false
+};
+goog.propertyIsEnumerable_ = function(object, propName) {
+ if(object instanceof Object) {
+ return Object.prototype.propertyIsEnumerable.call(object, propName)
+ }else {
+ return goog.propertyIsEnumerableCustom_(object, propName)
+ }
+};
+goog.isDef = function(val) {
+ return val !== undefined
+};
+goog.isNull = function(val) {
+ return val === null
+};
+goog.isDefAndNotNull = function(val) {
+ return val != null
+};
+goog.isArray = function(val) {
+ return goog.typeOf(val) == "array"
+};
+goog.isArrayLike = function(val) {
+ var type = goog.typeOf(val);
+ return type == "array" || type == "object" && typeof val.length == "number"
+};
+goog.isDateLike = function(val) {
+ return goog.isObject(val) && typeof val.getFullYear == "function"
+};
+goog.isString = function(val) {
+ return typeof val == "string"
+};
+goog.isBoolean = function(val) {
+ return typeof val == "boolean"
+};
+goog.isNumber = function(val) {
+ return typeof val == "number"
+};
+goog.isFunction = function(val) {
+ return goog.typeOf(val) == "function"
+};
+goog.isObject = function(val) {
+ var type = goog.typeOf(val);
+ return type == "object" || type == "array" || type == "function"
+};
+goog.getUid = function(obj) {
+ return obj[goog.UID_PROPERTY_] || (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_)
+};
+goog.removeUid = function(obj) {
+ if("removeAttribute" in obj) {
+ obj.removeAttribute(goog.UID_PROPERTY_)
+ }
+ try {
+ delete obj[goog.UID_PROPERTY_]
+ }catch(ex) {
+ }
+};
+goog.UID_PROPERTY_ = "closure_uid_" + Math.floor(Math.random() * 2147483648).toString(36);
+goog.uidCounter_ = 0;
+goog.getHashCode = goog.getUid;
+goog.removeHashCode = goog.removeUid;
+goog.cloneObject = function(obj) {
+ var type = goog.typeOf(obj);
+ if(type == "object" || type == "array") {
+ if(obj.clone) {
+ return obj.clone()
+ }
+ var clone = type == "array" ? [] : {};
+ for(var key in obj) {
+ clone[key] = goog.cloneObject(obj[key])
+ }
+ return clone
+ }
+ return obj
+};
+Object.prototype.clone;
+goog.bind = function(fn, selfObj, var_args) {
+ var context = selfObj || goog.global;
+ if(arguments.length > 2) {
+ var boundArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ var newArgs = Array.prototype.slice.call(arguments);
+ Array.prototype.unshift.apply(newArgs, boundArgs);
+ return fn.apply(context, newArgs)
+ }
+ }else {
+ return function() {
+ return fn.apply(context, arguments)
+ }
+ }
+};
+goog.partial = function(fn, var_args) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ var newArgs = Array.prototype.slice.call(arguments);
+ newArgs.unshift.apply(newArgs, args);
+ return fn.apply(this, newArgs)
+ }
+};
+goog.mixin = function(target, source) {
+ for(var x in source) {
+ target[x] = source[x]
+ }
+};
+goog.now = Date.now || function() {
+ return+new Date
+};
+goog.globalEval = function(script) {
+ if(goog.global.execScript) {
+ goog.global.execScript(script, "JavaScript")
+ }else {
+ if(goog.global.eval) {
+ if(goog.evalWorksForGlobals_ == null) {
+ goog.global.eval("var _et_ = 1;");
+ if(typeof goog.global["_et_"] != "undefined") {
+ delete goog.global["_et_"];
+ goog.evalWorksForGlobals_ = true
+ }else {
+ goog.evalWorksForGlobals_ = false
+ }
+ }
+ if(goog.evalWorksForGlobals_) {
+ goog.global.eval(script)
+ }else {
+ var doc = goog.global.document;
+ var scriptElt = doc.createElement("script");
+ scriptElt.type = "text/javascript";
+ scriptElt.defer = false;
+ scriptElt.appendChild(doc.createTextNode(script));
+ doc.body.appendChild(scriptElt);
+ doc.body.removeChild(scriptElt)
+ }
+ }else {
+ throw Error("goog.globalEval not available");
+ }
+ }
+};
+goog.typedef = true;
+goog.cssNameMapping_;
+goog.getCssName = function(className, opt_modifier) {
+ var cssName = className + (opt_modifier ? "-" + opt_modifier : "");
+ return goog.cssNameMapping_ && cssName in goog.cssNameMapping_ ? goog.cssNameMapping_[cssName] : cssName
+};
+goog.setCssNameMapping = function(mapping) {
+ goog.cssNameMapping_ = mapping
+};
+goog.getMsg = function(str, opt_values) {
+ var values = opt_values || {};
+ for(var key in values) {
+ var value = ("" + values[key]).replace(/\$/g, "$$$$");
+ str = str.replace(new RegExp("\\{\\$" + key + "\\}", "gi"), value)
+ }
+ return str
+};
+goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
+ goog.exportPath_(publicPath, object, opt_objectToExportTo)
+};
+goog.exportProperty = function(object, publicName, symbol) {
+ object[publicName] = symbol
+};
+goog.inherits = function(childCtor, parentCtor) {
+ function tempCtor() {
+ }
+ tempCtor.prototype = parentCtor.prototype;
+ childCtor.superClass_ = parentCtor.prototype;
+ childCtor.prototype = new tempCtor;
+ childCtor.prototype.constructor = childCtor
+};
+goog.base = function(me, opt_methodName, var_args) {
+ var caller = arguments.callee.caller;
+ if(caller.superClass_) {
+ return caller.superClass_.constructor.apply(me, Array.prototype.slice.call(arguments, 1))
+ }
+ var args = Array.prototype.slice.call(arguments, 2);
+ var foundCaller = false;
+ for(var ctor = me.constructor;ctor;ctor = ctor.superClass_ && ctor.superClass_.constructor) {
+ if(ctor.prototype[opt_methodName] === caller) {
+ foundCaller = true
+ }else {
+ if(foundCaller) {
+ return ctor.prototype[opt_methodName].apply(me, args)
+ }
+ }
+ }
+ if(me[opt_methodName] === caller) {
+ return me.constructor.prototype[opt_methodName].apply(me, args)
+ }else {
+ throw Error("goog.base called from a method of one name " + "to a method of a different name");
+ }
+};
+goog.scope = function(fn) {
+ fn.call(goog.global)
+};
+goog.provide("goog.debug.Error");
+goog.debug.Error = function(opt_msg) {
+ this.stack = (new Error).stack || "";
+ if(opt_msg) {
+ this.message = String(opt_msg)
+ }
+};
+goog.inherits(goog.debug.Error, Error);
+goog.debug.Error.prototype.name = "CustomError";
+goog.provide("goog.string");
+goog.provide("goog.string.Unicode");
+goog.string.Unicode = {NBSP:"\u00a0"};
+goog.string.startsWith = function(str, prefix) {
+ return str.lastIndexOf(prefix, 0) == 0
+};
+goog.string.endsWith = function(str, suffix) {
+ var l = str.length - suffix.length;
+ return l >= 0 && str.indexOf(suffix, l) == l
+};
+goog.string.caseInsensitiveStartsWith = function(str, prefix) {
+ return goog.string.caseInsensitiveCompare(prefix, str.substr(0, prefix.length)) == 0
+};
+goog.string.caseInsensitiveEndsWith = function(str, suffix) {
+ return goog.string.caseInsensitiveCompare(suffix, str.substr(str.length - suffix.length, suffix.length)) == 0
+};
+goog.string.subs = function(str, var_args) {
+ for(var i = 1;i < arguments.length;i++) {
+ var replacement = String(arguments[i]).replace(/\$/g, "$$$$");
+ str = str.replace(/\%s/, replacement)
+ }
+ return str
+};
+goog.string.collapseWhitespace = function(str) {
+ return str.replace(/[\s\xa0]+/g, " ").replace(/^\s+|\s+$/g, "")
+};
+goog.string.isEmpty = function(str) {
+ return/^[\s\xa0]*$/.test(str)
+};
+goog.string.isEmptySafe = function(str) {
+ return goog.string.isEmpty(goog.string.makeSafe(str))
+};
+goog.string.isBreakingWhitespace = function(str) {
+ return!/[^\t\n\r ]/.test(str)
+};
+goog.string.isAlpha = function(str) {
+ return!/[^a-zA-Z]/.test(str)
+};
+goog.string.isNumeric = function(str) {
+ return!/[^0-9]/.test(str)
+};
+goog.string.isAlphaNumeric = function(str) {
+ return!/[^a-zA-Z0-9]/.test(str)
+};
+goog.string.isSpace = function(ch) {
+ return ch == " "
+};
+goog.string.isUnicodeChar = function(ch) {
+ return ch.length == 1 && ch >= " " && ch <= "~" || ch >= "\u0080" && ch <= "\ufffd"
+};
+goog.string.stripNewlines = function(str) {
+ return str.replace(/(\r\n|\r|\n)+/g, " ")
+};
+goog.string.canonicalizeNewlines = function(str) {
+ return str.replace(/(\r\n|\r|\n)/g, "\n")
+};
+goog.string.normalizeWhitespace = function(str) {
+ return str.replace(/\xa0|\s/g, " ")
+};
+goog.string.normalizeSpaces = function(str) {
+ return str.replace(/\xa0|[ \t]+/g, " ")
+};
+goog.string.trim = function(str) {
+ return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, "")
+};
+goog.string.trimLeft = function(str) {
+ return str.replace(/^[\s\xa0]+/, "")
+};
+goog.string.trimRight = function(str) {
+ return str.replace(/[\s\xa0]+$/, "")
+};
+goog.string.caseInsensitiveCompare = function(str1, str2) {
+ var test1 = String(str1).toLowerCase();
+ var test2 = String(str2).toLowerCase();
+ if(test1 < test2) {
+ return-1
+ }else {
+ if(test1 == test2) {
+ return 0
+ }else {
+ return 1
+ }
+ }
+};
+goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
+goog.string.numerateCompare = function(str1, str2) {
+ if(str1 == str2) {
+ return 0
+ }
+ if(!str1) {
+ return-1
+ }
+ if(!str2) {
+ return 1
+ }
+ var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
+ var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
+ var count = Math.min(tokens1.length, tokens2.length);
+ for(var i = 0;i < count;i++) {
+ var a = tokens1[i];
+ var b = tokens2[i];
+ if(a != b) {
+ var num1 = parseInt(a, 10);
+ if(!isNaN(num1)) {
+ var num2 = parseInt(b, 10);
+ if(!isNaN(num2) && num1 - num2) {
+ return num1 - num2
+ }
+ }
+ return a < b ? -1 : 1
+ }
+ }
+ if(tokens1.length != tokens2.length) {
+ return tokens1.length - tokens2.length
+ }
+ return str1 < str2 ? -1 : 1
+};
+goog.string.encodeUriRegExp_ = /^[a-zA-Z0-9\-_.!~*'()]*$/;
+goog.string.urlEncode = function(str) {
+ str = String(str);
+ if(!goog.string.encodeUriRegExp_.test(str)) {
+ return encodeURIComponent(str)
+ }
+ return str
+};
+goog.string.urlDecode = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "))
+};
+goog.string.newLineToBr = function(str, opt_xml) {
+ return str.replace(/(\r\n|\r|\n)/g, opt_xml ? "<br />" : "<br>")
+};
+goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
+ if(opt_isLikelyToContainHtmlChars) {
+ return str.replace(goog.string.amperRe_, "&amp;").replace(goog.string.ltRe_, "&lt;").replace(goog.string.gtRe_, "&gt;").replace(goog.string.quotRe_, "&quot;")
+ }else {
+ if(!goog.string.allRe_.test(str)) {
+ return str
+ }
+ if(str.includes("&")) {
+ str = str.replace(goog.string.amperRe_, "&amp;")
+ }
+ if(str.includes("<")) {
+ str = str.replace(goog.string.ltRe_, "&lt;")
+ }
+ if(str.includes(">")) {
+ str = str.replace(goog.string.gtRe_, "&gt;")
+ }
+ if(str.includes('"')) {
+ str = str.replace(goog.string.quotRe_, "&quot;")
+ }
+ return str
+ }
+};
+goog.string.amperRe_ = /&/g;
+goog.string.ltRe_ = /</g;
+goog.string.gtRe_ = />/g;
+goog.string.quotRe_ = /\"/g;
+goog.string.allRe_ = /[&<>\"]/;
+goog.string.unescapeEntities = function(str) {
+ if(goog.string.contains(str, "&")) {
+ if("document" in goog.global && !goog.string.contains(str, "<")) {
+ return goog.string.unescapeEntitiesUsingDom_(str)
+ }else {
+ return goog.string.unescapePureXmlEntities_(str)
+ }
+ }
+ return str
+};
+goog.string.unescapeEntitiesUsingDom_ = function(str) {
+ var el = goog.global["document"]["createElement"]("a");
+ el["innerHTML"] = str;
+ if(el[goog.string.NORMALIZE_FN_]) {
+ el[goog.string.NORMALIZE_FN_]()
+ }
+ str = el["firstChild"]["nodeValue"];
+ el["innerHTML"] = "";
+ return str
+};
+goog.string.unescapePureXmlEntities_ = function(str) {
+ return str.replace(/&([^;]+);/g, function(s, entity) {
+ switch(entity) {
+ case "amp":
+ return"&";
+ case "lt":
+ return"<";
+ case "gt":
+ return">";
+ case "quot":
+ return'"';
+ default:
+ if(entity.charAt(0) == "#") {
+ var n = Number("0" + entity.substr(1));
+ if(!isNaN(n)) {
+ return String.fromCharCode(n)
+ }
+ }
+ return s
+ }
+ })
+};
+goog.string.NORMALIZE_FN_ = "normalize";
+goog.string.whitespaceEscape = function(str, opt_xml) {
+ return goog.string.newLineToBr(str.replace(/ /g, " &#160;"), opt_xml)
+};
+goog.string.stripQuotes = function(str, quoteChars) {
+ var length = quoteChars.length;
+ for(var i = 0;i < length;i++) {
+ var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
+ if(str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
+ return str.substring(1, str.length - 1)
+ }
+ }
+ return str
+};
+goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
+ if(opt_protectEscapedCharacters) {
+ str = goog.string.unescapeEntities(str)
+ }
+ if(str.length > chars) {
+ str = str.substring(0, chars - 3) + "..."
+ }
+ if(opt_protectEscapedCharacters) {
+ str = goog.string.htmlEscape(str)
+ }
+ return str
+};
+goog.string.truncateMiddle = function(str, chars, opt_protectEscapedCharacters) {
+ if(opt_protectEscapedCharacters) {
+ str = goog.string.unescapeEntities(str)
+ }
+ if(str.length > chars) {
+ var half = Math.floor(chars / 2);
+ var endPos = str.length - half;
+ half += chars % 2;
+ str = str.substring(0, half) + "..." + str.substring(endPos)
+ }
+ if(opt_protectEscapedCharacters) {
+ str = goog.string.htmlEscape(str)
+ }
+ return str
+};
+goog.string.specialEscapeChars_ = {"\u0000":"\\0", "\u0008":"\\b", "\u000c":"\\f", "\n":"\\n", "\r":"\\r", "\t":"\\t", "\u000b":"\\x0B", '"':'\\"', "\\":"\\\\"};
+goog.string.jsEscapeCache_ = {"'":"\\'"};
+goog.string.quote = function(s) {
+ s = String(s);
+ if(s.quote) {
+ return s.quote()
+ }else {
+ var sb = ['"'];
+ for(var i = 0;i < s.length;i++) {
+ var ch = s.charAt(i);
+ var cc = ch.charCodeAt(0);
+ sb[i + 1] = goog.string.specialEscapeChars_[ch] || (cc > 31 && cc < 127 ? ch : goog.string.escapeChar(ch))
+ }
+ sb.push('"');
+ return sb.join("")
+ }
+};
+goog.string.escapeString = function(str) {
+ var sb = [];
+ for(var i = 0;i < str.length;i++) {
+ sb[i] = goog.string.escapeChar(str.charAt(i))
+ }
+ return sb.join("")
+};
+goog.string.escapeChar = function(c) {
+ if(c in goog.string.jsEscapeCache_) {
+ return goog.string.jsEscapeCache_[c]
+ }
+ if(c in goog.string.specialEscapeChars_) {
+ return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c]
+ }
+ var rv = c;
+ var cc = c.charCodeAt(0);
+ if(cc > 31 && cc < 127) {
+ rv = c
+ }else {
+ if(cc < 256) {
+ rv = "\\x";
+ if(cc < 16 || cc > 256) {
+ rv += "0"
+ }
+ }else {
+ rv = "\\u";
+ if(cc < 4096) {
+ rv += "0"
+ }
+ }
+ rv += cc.toString(16).toUpperCase()
+ }
+ return goog.string.jsEscapeCache_[c] = rv
+};
+goog.string.toMap = function(s) {
+ var rv = {};
+ for(var i = 0;i < s.length;i++) {
+ rv[s.charAt(i)] = true
+ }
+ return rv
+};
+goog.string.contains = function(s, ss) {
+ return s.includes(ss)
+};
+goog.string.removeAt = function(s, index, stringLength) {
+ var resultStr = s;
+ if(index >= 0 && index < s.length && stringLength > 0) {
+ resultStr = s.substr(0, index) + s.substr(index + stringLength, s.length - index - stringLength)
+ }
+ return resultStr
+};
+goog.string.remove = function(s, ss) {
+ var re = new RegExp(goog.string.regExpEscape(ss), "");
+ return s.replace(re, "")
+};
+goog.string.removeAll = function(s, ss) {
+ var re = new RegExp(goog.string.regExpEscape(ss), "g");
+ return s.replace(re, "")
+};
+goog.string.regExpEscape = function(s) {
+ return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, "\\$1").replace(/\x08/g, "\\x08")
+};
+goog.string.repeat = function(string, length) {
+ return(new Array(length + 1)).join(string)
+};
+goog.string.padNumber = function(num, length, opt_precision) {
+ var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
+ var index = s.indexOf(".");
+ if(index == -1) {
+ index = s.length
+ }
+ return goog.string.repeat("0", Math.max(0, length - index)) + s
+};
+goog.string.makeSafe = function(obj) {
+ return obj == null ? "" : String(obj)
+};
+goog.string.buildString = function(var_args) {
+ return Array.prototype.join.call(arguments, "")
+};
+goog.string.getRandomString = function() {
+ return Math.floor(Math.random() * 2147483648).toString(36) + (Math.floor(Math.random() * 2147483648) ^ goog.now()).toString(36)
+};
+goog.string.compareVersions = function(version1, version2) {
+ var order = 0;
+ var v1Subs = goog.string.trim(String(version1)).split(".");
+ var v2Subs = goog.string.trim(String(version2)).split(".");
+ var subCount = Math.max(v1Subs.length, v2Subs.length);
+ for(var subIdx = 0;order == 0 && subIdx < subCount;subIdx++) {
+ var v1Sub = v1Subs[subIdx] || "";
+ var v2Sub = v2Subs[subIdx] || "";
+ var v1CompParser = new RegExp("(\\d*)(\\D*)", "g");
+ var v2CompParser = new RegExp("(\\d*)(\\D*)", "g");
+ do {
+ var v1Comp = v1CompParser.exec(v1Sub) || ["", "", ""];
+ var 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);
+ var 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.string.HASHCODE_MAX_ = 4294967296;
+goog.string.hashCode = function(str) {
+ var result = 0;
+ for(var i = 0;i < str.length;++i) {
+ result = 31 * result + str.charCodeAt(i);
+ result %= goog.string.HASHCODE_MAX_
+ }
+ return result
+};
+goog.string.uniqueStringCounter_ = Math.random() * 2147483648 | 0;
+goog.string.createUniqueString = function() {
+ return"goog_" + goog.string.uniqueStringCounter_++
+};
+goog.string.toNumber = function(str) {
+ var num = Number(str);
+ if(num == 0 && goog.string.isEmpty(str)) {
+ return NaN
+ }
+ return num
+};
+goog.provide("goog.asserts");
+goog.provide("goog.asserts.AssertionError");
+goog.require("goog.debug.Error");
+goog.require("goog.string");
+goog.asserts.ENABLE_ASSERTS = goog.DEBUG;
+goog.asserts.AssertionError = function(messagePattern, messageArgs) {
+ messageArgs.unshift(messagePattern);
+ goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
+ messageArgs.shift();
+ this.messagePattern = messagePattern
+};
+goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
+goog.asserts.AssertionError.prototype.name = "AssertionError";
+goog.asserts.doAssertFailure_ = function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
+ var message = "Assertion failed";
+ if(givenMessage) {
+ message += ": " + givenMessage;
+ var args = givenArgs
+ }else {
+ if(defaultMessage) {
+ message += ": " + defaultMessage;
+ args = defaultArgs
+ }
+ }
+ throw new goog.asserts.AssertionError("" + message, args || []);
+};
+goog.asserts.assert = function(condition, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !condition) {
+ goog.asserts.doAssertFailure_("", null, opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return condition
+};
+goog.asserts.fail = function(opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS) {
+ throw new goog.asserts.AssertionError("Failure" + (opt_message ? ": " + opt_message : ""), Array.prototype.slice.call(arguments, 1));
+ }
+};
+goog.asserts.assertNumber = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
+ goog.asserts.doAssertFailure_("Expected number but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertString = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
+ goog.asserts.doAssertFailure_("Expected string but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertFunction = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
+ goog.asserts.doAssertFailure_("Expected function but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertObject = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
+ goog.asserts.doAssertFailure_("Expected object but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertArray = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
+ goog.asserts.doAssertFailure_("Expected array but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertBoolean = function(value, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
+ goog.asserts.doAssertFailure_("Expected boolean but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2))
+ }
+ return value
+};
+goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
+ if(goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
+ goog.asserts.doAssertFailure_("instanceof check failed.", null, opt_message, Array.prototype.slice.call(arguments, 3))
+ }
+};
+goog.provide("goog.array");
+goog.require("goog.asserts");
+goog.array.ArrayLike;
+goog.array.peek = function(array) {
+ return array[array.length - 1]
+};
+goog.array.ARRAY_PROTOTYPE_ = Array.prototype;
+goog.array.indexOf = goog.array.ARRAY_PROTOTYPE_.indexOf ? function(arr, obj, opt_fromIndex) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex)
+} : function(arr, obj, opt_fromIndex) {
+ var fromIndex = opt_fromIndex == null ? 0 : opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex;
+ if(goog.isString(arr)) {
+ if(!goog.isString(obj) || obj.length != 1) {
+ return-1
+ }
+ return arr.indexOf(obj, fromIndex)
+ }
+ for(var i = fromIndex;i < arr.length;i++) {
+ if(i in arr && arr[i] === obj) {
+ return i
+ }
+ }
+ return-1
+};
+goog.array.lastIndexOf = goog.array.ARRAY_PROTOTYPE_.lastIndexOf ? function(arr, obj, opt_fromIndex) {
+ goog.asserts.assert(arr.length != null);
+ var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
+ return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex)
+} : function(arr, obj, opt_fromIndex) {
+ var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
+ if(fromIndex < 0) {
+ fromIndex = Math.max(0, arr.length + fromIndex)
+ }
+ if(goog.isString(arr)) {
+ if(!goog.isString(obj) || obj.length != 1) {
+ return-1
+ }
+ return arr.lastIndexOf(obj, fromIndex)
+ }
+ for(var i = fromIndex;i >= 0;i--) {
+ if(i in arr && arr[i] === obj) {
+ return i
+ }
+ }
+ return-1
+};
+goog.array.forEach = goog.array.ARRAY_PROTOTYPE_.forEach ? function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj)
+} : function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = 0;i < l;i++) {
+ if(i in arr2) {
+ f.call(opt_obj, arr2[i], i, arr)
+ }
+ }
+};
+goog.array.forEachRight = function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = l - 1;i >= 0;--i) {
+ if(i in arr2) {
+ f.call(opt_obj, arr2[i], i, arr)
+ }
+ }
+};
+goog.array.filter = goog.array.ARRAY_PROTOTYPE_.filter ? function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj)
+} : function(arr, f, opt_obj) {
+ var l = arr.length;
+ var res = [];
+ var resLength = 0;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = 0;i < l;i++) {
+ if(i in arr2) {
+ var val = arr2[i];
+ if(f.call(opt_obj, val, i, arr)) {
+ res[resLength++] = val
+ }
+ }
+ }
+ return res
+};
+goog.array.map = goog.array.ARRAY_PROTOTYPE_.map ? function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj)
+} : function(arr, f, opt_obj) {
+ var l = arr.length;
+ var res = new Array(l);
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = 0;i < l;i++) {
+ if(i in arr2) {
+ res[i] = f.call(opt_obj, arr2[i], i, arr)
+ }
+ }
+ return res
+};
+goog.array.reduce = function(arr, f, val, opt_obj) {
+ if(arr.reduce) {
+ if(opt_obj) {
+ return arr.reduce(goog.bind(f, opt_obj), val)
+ }else {
+ return arr.reduce(f, val)
+ }
+ }
+ var rval = val;
+ goog.array.forEach(arr, function(val, index) {
+ rval = f.call(opt_obj, rval, val, index, arr)
+ });
+ return rval
+};
+goog.array.reduceRight = function(arr, f, val, opt_obj) {
+ if(arr.reduceRight) {
+ if(opt_obj) {
+ return arr.reduceRight(goog.bind(f, opt_obj), val)
+ }else {
+ return arr.reduceRight(f, val)
+ }
+ }
+ var rval = val;
+ goog.array.forEachRight(arr, function(val, index) {
+ rval = f.call(opt_obj, rval, val, index, arr)
+ });
+ return rval
+};
+goog.array.some = goog.array.ARRAY_PROTOTYPE_.some ? function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj)
+} : function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = 0;i < l;i++) {
+ if(i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
+ return true
+ }
+ }
+ return false
+};
+goog.array.every = goog.array.ARRAY_PROTOTYPE_.every ? function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj)
+} : function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var 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 = goog.array.findIndex(arr, f, opt_obj);
+ return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]
+};
+goog.array.findIndex = function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = 0;i < l;i++) {
+ if(i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
+ return i
+ }
+ }
+ return-1
+};
+goog.array.findRight = function(arr, f, opt_obj) {
+ var i = goog.array.findIndexRight(arr, f, opt_obj);
+ return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]
+};
+goog.array.findIndexRight = function(arr, f, opt_obj) {
+ var l = arr.length;
+ var arr2 = goog.isString(arr) ? arr.split("") : arr;
+ for(var i = l - 1;i >= 0;i--) {
+ if(i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
+ return i
+ }
+ }
+ return-1
+};
+goog.array.contains = function(arr, obj) {
+ return goog.array.includes(arr, obj)
+};
+goog.array.isEmpty = function(arr) {
+ return arr.length == 0
+};
+goog.array.clear = function(arr) {
+ if(!goog.isArray(arr)) {
+ for(var i = arr.length - 1;i >= 0;i--) {
+ delete arr[i]
+ }
+ }
+ arr.length = 0
+};
+goog.array.insert = function(arr, obj) {
+ if(!goog.array.contains(arr, obj)) {
+ arr.push(obj)
+ }
+};
+goog.array.insertAt = function(arr, obj, opt_i) {
+ goog.array.splice(arr, opt_i, 0, obj)
+};
+goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
+ goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd)
+};
+goog.array.insertBefore = function(arr, obj, opt_obj2) {
+ var i;
+ if(arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
+ arr.push(obj)
+ }else {
+ goog.array.insertAt(arr, obj, i)
+ }
+};
+goog.array.remove = function(arr, obj) {
+ var i = goog.array.indexOf(arr, obj);
+ var rv;
+ if(rv = i >= 0) {
+ goog.array.removeAt(arr, i)
+ }
+ return rv
+};
+goog.array.removeAt = function(arr, i) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1
+};
+goog.array.removeIf = function(arr, f, opt_obj) {
+ var i = goog.array.findIndex(arr, f, opt_obj);
+ if(i >= 0) {
+ goog.array.removeAt(arr, i);
+ return true
+ }
+ return false
+};
+goog.array.concat = function(var_args) {
+ return goog.array.ARRAY_PROTOTYPE_.concat.apply(goog.array.ARRAY_PROTOTYPE_, arguments)
+};
+goog.array.clone = function(arr) {
+ if(goog.isArray(arr)) {
+ return goog.array.concat(arr)
+ }else {
+ var rv = [];
+ for(var i = 0, len = arr.length;i < len;i++) {
+ rv[i] = arr[i]
+ }
+ return rv
+ }
+};
+goog.array.toArray = function(object) {
+ if(goog.isArray(object)) {
+ return goog.array.concat(object)
+ }
+ return goog.array.clone(object)
+};
+goog.array.extend = function(arr1, var_args) {
+ for(var i = 1;i < arguments.length;i++) {
+ var arr2 = arguments[i];
+ var isArrayLike;
+ if(goog.isArray(arr2) || (isArrayLike = goog.isArrayLike(arr2)) && arr2.hasOwnProperty("callee")) {
+ arr1.push.apply(arr1, arr2)
+ }else {
+ if(isArrayLike) {
+ var len1 = arr1.length;
+ var len2 = arr2.length;
+ for(var j = 0;j < len2;j++) {
+ arr1[len1 + j] = arr2[j]
+ }
+ }else {
+ arr1.push(arr2)
+ }
+ }
+ }
+};
+goog.array.splice = function(arr, index, howMany, var_args) {
+ goog.asserts.assert(arr.length != null);
+ return goog.array.ARRAY_PROTOTYPE_.splice.apply(arr, goog.array.slice(arguments, 1))
+};
+goog.array.slice = function(arr, start, opt_end) {
+ goog.asserts.assert(arr.length != null);
+ if(arguments.length <= 2) {
+ return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start)
+ }else {
+ return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end)
+ }
+};
+goog.array.removeDuplicates = function(arr, opt_rv) {
+ var rv = opt_rv || arr;
+ var seen = {}, cursorInsert = 0, cursorRead = 0;
+ while(cursorRead < arr.length) {
+ var current = arr[cursorRead++];
+ var uid = goog.isObject(current) ? goog.getUid(current) : current;
+ if(!Object.prototype.hasOwnProperty.call(seen, uid)) {
+ seen[uid] = true;
+ rv[cursorInsert++] = current
+ }
+ }
+ rv.length = cursorInsert
+};
+goog.array.binarySearch = function(arr, target, opt_compareFn) {
+ return goog.array.binarySearch_(arr, opt_compareFn || goog.array.defaultCompare, false, target)
+};
+goog.array.binarySelect = function(arr, evaluator, opt_obj) {
+ return goog.array.binarySearch_(arr, evaluator, true, undefined, opt_obj)
+};
+goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, opt_selfObj) {
+ var left = 0;
+ var right = arr.length;
+ var found;
+ while(left < right) {
+ var middle = left + right >> 1;
+ var compareResult;
+ if(isEvaluator) {
+ compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr)
+ }else {
+ compareResult = compareFn(opt_target, arr[middle])
+ }
+ if(compareResult > 0) {
+ left = middle + 1
+ }else {
+ right = middle;
+ found = !compareResult
+ }
+ }
+ return found ? left : ~left
+};
+goog.array.sort = function(arr, opt_compareFn) {
+ goog.asserts.assert(arr.length != null);
+ goog.array.ARRAY_PROTOTYPE_.sort.call(arr, opt_compareFn || goog.array.defaultCompare)
+};
+goog.array.stableSort = function(arr, opt_compareFn) {
+ for(var i = 0;i < arr.length;i++) {
+ arr[i] = {index:i, value:arr[i]}
+ }
+ var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
+ function stableCompareFn(obj1, obj2) {
+ return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index
+ }
+ goog.array.sort(arr, stableCompareFn);
+ for(var i = 0;i < arr.length;i++) {
+ arr[i] = arr[i].value
+ }
+};
+goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
+ var compare = opt_compareFn || goog.array.defaultCompare;
+ goog.array.sort(arr, function(a, b) {
+ return compare(a[key], b[key])
+ })
+};
+goog.array.equals = function(arr1, arr2, opt_equalsFn) {
+ if(!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) || arr1.length != arr2.length) {
+ return false
+ }
+ var l = arr1.length;
+ var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
+ for(var i = 0;i < l;i++) {
+ if(!equalsFn(arr1[i], arr2[i])) {
+ return false
+ }
+ }
+ return true
+};
+goog.array.compare = function(arr1, arr2, opt_equalsFn) {
+ return goog.array.equals(arr1, arr2, opt_equalsFn)
+};
+goog.array.defaultCompare = function(a, b) {
+ return a > b ? 1 : a < b ? -1 : 0
+};
+goog.array.defaultCompareEquality = function(a, b) {
+ return a === b
+};
+goog.array.binaryInsert = function(array, value, opt_compareFn) {
+ var index = goog.array.binarySearch(array, value, opt_compareFn);
+ if(index < 0) {
+ goog.array.insertAt(array, value, -(index + 1));
+ return true
+ }
+ return false
+};
+goog.array.binaryRemove = function(array, value, opt_compareFn) {
+ var index = goog.array.binarySearch(array, value, opt_compareFn);
+ return index >= 0 ? goog.array.removeAt(array, index) : false
+};
+goog.array.bucket = function(array, sorter) {
+ var buckets = {};
+ for(var i = 0;i < array.length;i++) {
+ var value = array[i];
+ var key = sorter(value, i, array);
+ if(goog.isDef(key)) {
+ var bucket = buckets[key] || (buckets[key] = []);
+ bucket.push(value)
+ }
+ }
+ return buckets
+};
+goog.array.repeat = function(value, n) {
+ var array = [];
+ for(var i = 0;i < n;i++) {
+ array[i] = value
+ }
+ return array
+};
+goog.array.flatten = function(var_args) {
+ var result = [];
+ for(var i = 0;i < arguments.length;i++) {
+ var element = arguments[i];
+ if(goog.isArray(element)) {
+ result.push.apply(result, goog.array.flatten.apply(null, element))
+ }else {
+ result.push(element)
+ }
+ }
+ return result
+};
+goog.array.rotate = function(array, n) {
+ goog.asserts.assert(array.length != null);
+ if(array.length) {
+ n %= array.length;
+ if(n > 0) {
+ goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n))
+ }else {
+ if(n < 0) {
+ goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n))
+ }
+ }
+ }
+ return array
+};
+goog.array.zip = function(var_args) {
+ if(!arguments.length) {
+ return[]
+ }
+ var result = [];
+ for(var i = 0;true;i++) {
+ var value = [];
+ for(var j = 0;j < arguments.length;j++) {
+ var arr = arguments[j];
+ if(i >= arr.length) {
+ return result
+ }
+ value.push(arr[i])
+ }
+ result.push(value)
+ }
+};
+goog.provide("goog.userAgent");
+goog.require("goog.string");
+goog.userAgent.ASSUME_IE = false;
+goog.userAgent.ASSUME_GECKO = false;
+goog.userAgent.ASSUME_WEBKIT = false;
+goog.userAgent.ASSUME_MOBILE_WEBKIT = false;
+goog.userAgent.ASSUME_OPERA = false;
+goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE || goog.userAgent.ASSUME_GECKO || goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_OPERA;
+goog.userAgent.getUserAgentString = function() {
+ return goog.global["navigator"] ? goog.global["navigator"].userAgent : null
+};
+goog.userAgent.getNavigator = function() {
+ return goog.global["navigator"]
+};
+goog.userAgent.init_ = function() {
+ goog.userAgent.detectedOpera_ = false;
+ goog.userAgent.detectedIe_ = false;
+ goog.userAgent.detectedWebkit_ = false;
+ goog.userAgent.detectedMobile_ = false;
+ goog.userAgent.detectedGecko_ = false;
+ var ua;
+ if(!goog.userAgent.BROWSER_KNOWN_ && (ua = goog.userAgent.getUserAgentString())) {
+ var navigator = goog.userAgent.getNavigator();
+ goog.userAgent.detectedOpera_ = ua.indexOf("Opera") == 0;
+ goog.userAgent.detectedIe_ = !goog.userAgent.detectedOpera_ && ua.includes("MSIE");
+ goog.userAgent.detectedWebkit_ = !goog.userAgent.detectedOpera_ && ua.includes("WebKit");
+ goog.userAgent.detectedMobile_ = goog.userAgent.detectedWebkit_ && ua.includes("Mobile");
+ goog.userAgent.detectedGecko_ = !goog.userAgent.detectedOpera_ && !goog.userAgent.detectedWebkit_ && navigator.product == "Gecko"
+ }
+};
+if(!goog.userAgent.BROWSER_KNOWN_) {
+ goog.userAgent.init_()
+}
+goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_OPERA : goog.userAgent.detectedOpera_;
+goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;
+goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_GECKO : goog.userAgent.detectedGecko_;
+goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT : goog.userAgent.detectedWebkit_;
+goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.detectedMobile_;
+goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
+goog.userAgent.determinePlatform_ = function() {
+ var navigator = goog.userAgent.getNavigator();
+ return navigator && navigator.platform || ""
+};
+goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
+goog.userAgent.ASSUME_MAC = false;
+goog.userAgent.ASSUME_WINDOWS = false;
+goog.userAgent.ASSUME_LINUX = false;
+goog.userAgent.ASSUME_X11 = false;
+goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC || goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX || goog.userAgent.ASSUME_X11;
+goog.userAgent.initPlatform_ = function() {
+ 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")
+};
+if(!goog.userAgent.PLATFORM_KNOWN_) {
+ goog.userAgent.initPlatform_()
+}
+goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;
+goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;
+goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;
+goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ? goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;
+goog.userAgent.determineVersion_ = function() {
+ var version = "", re;
+ if(goog.userAgent.OPERA && goog.global["opera"]) {
+ var operaVersion = goog.global["opera"].version;
+ version = typeof operaVersion == "function" ? operaVersion() : operaVersion
+ }else {
+ if(goog.userAgent.GECKO) {
+ re = /rv\:([^\);]+)(\)|;)/
+ }else {
+ if(goog.userAgent.IE) {
+ re = /MSIE\s+([^\);]+)(\)|;)/
+ }else {
+ if(goog.userAgent.WEBKIT) {
+ re = /WebKit\/(\S+)/
+ }
+ }
+ }
+ if(re) {
+ var arr = re.exec(goog.userAgent.getUserAgentString());
+ version = arr ? arr[1] : ""
+ }
+ }
+ if(goog.userAgent.IE) {
+ var docMode = goog.userAgent.getDocumentMode_();
+ if(docMode > parseFloat(version)) {
+ return String(docMode)
+ }
+ }
+ return version
+};
+goog.userAgent.getDocumentMode_ = function() {
+ var doc = goog.global["document"];
+ return doc ? doc["documentMode"] : undefined
+};
+goog.userAgent.VERSION = goog.userAgent.determineVersion_();
+goog.userAgent.compare = function(v1, v2) {
+ return goog.string.compareVersions(v1, v2)
+};
+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)
+};
+goog.provide("goog.dom.BrowserFeature");
+goog.require("goog.userAgent");
+goog.dom.BrowserFeature = {
+ CAN_ADD_NAME_OR_TYPE_ATTRIBUTES: !goog.userAgent.IE || goog.userAgent.isVersion("9"),
+ CAN_USE_INNER_TEXT: goog.userAgent.IE && !goog.userAgent.isVersion("9"),
+ INNER_HTML_NEEDS_SCOPED_ELEMENT: goog.userAgent.IE
+};
+goog.provide("goog.dom.TagName");
+goog.dom.TagName = {A:"A", ABBR:"ABBR", ACRONYM:"ACRONYM", ADDRESS:"ADDRESS", APPLET:"APPLET", AREA:"AREA", B:"B", BASE:"BASE", BASEFONT:"BASEFONT", BDO:"BDO", BIG:"BIG", BLOCKQUOTE:"BLOCKQUOTE", BODY:"BODY", BR:"BR", BUTTON:"BUTTON", CANVAS:"CANVAS", CAPTION:"CAPTION", CENTER:"CENTER", CITE:"CITE", CODE:"CODE", COL:"COL", COLGROUP:"COLGROUP", DD:"DD", DEL:"DEL", DFN:"DFN", DIR:"DIR", DIV:"DIV", DL:"DL", DT:"DT", EM:"EM", FIELDSET:"FIELDSET", FONT:"FONT", FORM:"FORM", FRAME:"FRAME", FRAMESET:"FRAMESET",
+H1:"H1", H2:"H2", H3:"H3", H4:"H4", H5:"H5", H6:"H6", HEAD:"HEAD", HR:"HR", HTML:"HTML", I:"I", IFRAME:"IFRAME", IMG:"IMG", INPUT:"INPUT", INS:"INS", ISINDEX:"ISINDEX", KBD:"KBD", LABEL:"LABEL", LEGEND:"LEGEND", LI:"LI", LINK:"LINK", MAP:"MAP", MENU:"MENU", META:"META", NOFRAMES:"NOFRAMES", NOSCRIPT:"NOSCRIPT", OBJECT:"OBJECT", OL:"OL", OPTGROUP:"OPTGROUP", OPTION:"OPTION", P:"P", PARAM:"PARAM", PRE:"PRE", Q:"Q", S:"S", SAMP:"SAMP", SCRIPT:"SCRIPT", SELECT:"SELECT", SMALL:"SMALL", SPAN:"SPAN", STRIKE:"STRIKE",
+STRONG:"STRONG", STYLE:"STYLE", SUB:"SUB", SUP:"SUP", TABLE:"TABLE", TBODY:"TBODY", TD:"TD", TEXTAREA:"TEXTAREA", TFOOT:"TFOOT", TH:"TH", THEAD:"THEAD", TITLE:"TITLE", TR:"TR", TT:"TT", U:"U", UL:"UL", VAR:"VAR"};
+goog.provide("goog.dom.classes");
+goog.require("goog.array");
+goog.dom.classes.set = function(element, className) {
+ element.className = className
+};
+goog.dom.classes.get = function(element) {
+ var className = element.className;
+ return className && typeof className.split == "function" ? className.split(/\s+/) : []
+};
+goog.dom.classes.add = function(element, var_args) {
+ var classes = goog.dom.classes.get(element);
+ var args = goog.array.slice(arguments, 1);
+ var b = goog.dom.classes.add_(classes, args);
+ element.className = classes.join(" ");
+ return b
+};
+goog.dom.classes.remove = function(element, var_args) {
+ var classes = goog.dom.classes.get(element);
+ var args = goog.array.slice(arguments, 1);
+ var b = goog.dom.classes.remove_(classes, args);
+ element.className = classes.join(" ");
+ return b
+};
+goog.dom.classes.add_ = function(classes, args) {
+ var rv = 0;
+ for(var i = 0;i < args.length;i++) {
+ if(!goog.array.contains(classes, args[i])) {
+ classes.push(args[i]);
+ rv++
+ }
+ }
+ return rv == args.length
+};
+goog.dom.classes.remove_ = function(classes, args) {
+ var rv = 0;
+ for(var i = 0;i < classes.length;i++) {
+ if(goog.array.contains(args, classes[i])) {
+ goog.array.splice(classes, i--, 1);
+ rv++
+ }
+ }
+ return rv == args.length
+};
+goog.dom.classes.swap = function(element, fromClass, toClass) {
+ var classes = goog.dom.classes.get(element);
+ var removed = false;
+ for(var i = 0;i < classes.length;i++) {
+ if(classes[i] == fromClass) {
+ goog.array.splice(classes, i--, 1);
+ removed = true
+ }
+ }
+ if(removed) {
+ classes.push(toClass);
+ element.className = classes.join(" ")
+ }
+ return removed
+};
+goog.dom.classes.addRemove = function(element, classesToRemove, classesToAdd) {
+ var classes = goog.dom.classes.get(element);
+ if(goog.isString(classesToRemove)) {
+ goog.array.remove(classes, classesToRemove)
+ }else {
+ if(goog.isArray(classesToRemove)) {
+ goog.dom.classes.remove_(classes, classesToRemove)
+ }
+ }
+ if(goog.isString(classesToAdd) && !goog.array.contains(classes, classesToAdd)) {
+ classes.push(classesToAdd)
+ }else {
+ if(goog.isArray(classesToAdd)) {
+ goog.dom.classes.add_(classes, classesToAdd)
+ }
+ }
+ element.className = classes.join(" ")
+};
+goog.dom.classes.has = function(element, className) {
+ return goog.array.contains(goog.dom.classes.get(element), className)
+};
+goog.dom.classes.enable = function(element, className, enabled) {
+ if(enabled) {
+ goog.dom.classes.add(element, className)
+ }else {
+ goog.dom.classes.remove(element, className)
+ }
+};
+goog.dom.classes.toggle = function(element, className) {
+ var add = !goog.dom.classes.has(element, className);
+ goog.dom.classes.enable(element, className, add);
+ return add
+};
+goog.provide("goog.math.Coordinate");
+goog.math.Coordinate = function(opt_x, opt_y) {
+ this.x = goog.isDef(opt_x) ? opt_x : 0;
+ this.y = goog.isDef(opt_y) ? opt_y : 0
+};
+goog.math.Coordinate.prototype.clone = function() {
+ return new goog.math.Coordinate(this.x, this.y)
+};
+if(goog.DEBUG) {
+ goog.math.Coordinate.prototype.toString = function() {
+ return"(" + this.x + ", " + this.y + ")"
+ }
+}
+goog.math.Coordinate.equals = function(a, b) {
+ if(a == b) {
+ return true
+ }
+ if(!a || !b) {
+ return false
+ }
+ return a.x == b.x && a.y == b.y
+};
+goog.math.Coordinate.distance = function(a, b) {
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ return Math.sqrt(dx * dx + dy * dy)
+};
+goog.math.Coordinate.squaredDistance = function(a, b) {
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ return dx * dx + dy * dy
+};
+goog.math.Coordinate.difference = function(a, b) {
+ return new goog.math.Coordinate(a.x - b.x, a.y - b.y)
+};
+goog.math.Coordinate.sum = function(a, b) {
+ return new goog.math.Coordinate(a.x + b.x, a.y + b.y)
+};
+goog.provide("goog.math.Size");
+goog.math.Size = function(width, height) {
+ this.width = width;
+ this.height = height
+};
+goog.math.Size.equals = function(a, b) {
+ if(a == b) {
+ return true
+ }
+ if(!a || !b) {
+ return false
+ }
+ return a.width == b.width && a.height == b.height
+};
+goog.math.Size.prototype.clone = function() {
+ return new goog.math.Size(this.width, this.height)
+};
+if(goog.DEBUG) {
+ goog.math.Size.prototype.toString = function() {
+ return"(" + this.width + " x " + this.height + ")"
+ }
+}
+goog.math.Size.prototype.getLongest = function() {
+ return Math.max(this.width, this.height)
+};
+goog.math.Size.prototype.getShortest = function() {
+ return Math.min(this.width, this.height)
+};
+goog.math.Size.prototype.area = function() {
+ return this.width * this.height
+};
+goog.math.Size.prototype.perimeter = function() {
+ return(this.width + this.height) * 2
+};
+goog.math.Size.prototype.aspectRatio = function() {
+ return this.width / this.height
+};
+goog.math.Size.prototype.isEmpty = function() {
+ return!this.area()
+};
+goog.math.Size.prototype.ceil = function() {
+ this.width = Math.ceil(this.width);
+ this.height = Math.ceil(this.height);
+ return this
+};
+goog.math.Size.prototype.fitsInside = function(target) {
+ return this.width <= target.width && this.height <= target.height
+};
+goog.math.Size.prototype.floor = function() {
+ this.width = Math.floor(this.width);
+ this.height = Math.floor(this.height);
+ return this
+};
+goog.math.Size.prototype.round = function() {
+ this.width = Math.round(this.width);
+ this.height = Math.round(this.height);
+ return this
+};
+goog.math.Size.prototype.scale = function(s) {
+ this.width *= s;
+ this.height *= s;
+ return this
+};
+goog.math.Size.prototype.scaleToFit = function(target) {
+ var s = this.aspectRatio() > target.aspectRatio() ? target.width / this.width : target.height / this.height;
+ return this.scale(s)
+};
+goog.provide("goog.object");
+goog.object.forEach = function(obj, f, opt_obj) {
+ for(var key in obj) {
+ f.call(opt_obj, obj[key], key, obj)
+ }
+};
+goog.object.filter = function(obj, f, opt_obj) {
+ var res = {};
+ for(var key in obj) {
+ if(f.call(opt_obj, obj[key], key, obj)) {
+ res[key] = obj[key]
+ }
+ }
+ return res
+};
+goog.object.map = function(obj, f, opt_obj) {
+ var res = {};
+ for(var key in obj) {
+ res[key] = f.call(opt_obj, obj[key], key, obj)
+ }
+ return res
+};
+goog.object.some = function(obj, f, opt_obj) {
+ for(var key in obj) {
+ if(f.call(opt_obj, obj[key], key, obj)) {
+ return true
+ }
+ }
+ return false
+};
+goog.object.every = function(obj, f, opt_obj) {
+ for(var key in obj) {
+ if(!f.call(opt_obj, obj[key], key, obj)) {
+ return false
+ }
+ }
+ return true
+};
+goog.object.getCount = function(obj) {
+ var rv = 0;
+ for(var key in obj) {
+ rv++
+ }
+ return rv
+};
+goog.object.getAnyKey = function(obj) {
+ for(var key in obj) {
+ return key
+ }
+};
+goog.object.getAnyValue = function(obj) {
+ for(var key in obj) {
+ return obj[key]
+ }
+};
+goog.object.contains = function(obj, val) {
+ return goog.object.containsValue(obj, val)
+};
+goog.object.getValues = function(obj) {
+ var res = [];
+ var i = 0;
+ for(var key in obj) {
+ res[i++] = obj[key]
+ }
+ return res
+};
+goog.object.getKeys = function(obj) {
+ var res = [];
+ var i = 0;
+ for(var key in obj) {
+ res[i++] = key
+ }
+ return res
+};
+goog.object.containsKey = function(obj, key) {
+ return key in obj
+};
+goog.object.containsValue = function(obj, val) {
+ for(var key in obj) {
+ if(obj[key] == val) {
+ return true
+ }
+ }
+ return false
+};
+goog.object.findKey = function(obj, f, opt_this) {
+ for(var key in obj) {
+ if(f.call(opt_this, obj[key], key, obj)) {
+ return key
+ }
+ }
+ return undefined
+};
+goog.object.findValue = function(obj, f, opt_this) {
+ var key = goog.object.findKey(obj, f, opt_this);
+ return key && obj[key]
+};
+goog.object.isEmpty = function(obj) {
+ for(var key in obj) {
+ return false
+ }
+ return true
+};
+goog.object.clear = function(obj) {
+ var keys = goog.object.getKeys(obj);
+ for(var i = keys.length - 1;i >= 0;i--) {
+ goog.object.remove(obj, keys[i])
+ }
+};
+goog.object.remove = function(obj, key) {
+ var rv;
+ if(rv = key in obj) {
+ delete obj[key]
+ }
+ return rv
+};
+goog.object.add = function(obj, key, val) {
+ if(key in obj) {
+ throw Error('The object already contains the key "' + key + '"');
+ }
+ goog.object.set(obj, key, val)
+};
+goog.object.get = function(obj, key, opt_val) {
+ if(key in obj) {
+ return obj[key]
+ }
+ return opt_val
+};
+goog.object.set = function(obj, key, value) {
+ obj[key] = value
+};
+goog.object.setIfUndefined = function(obj, key, value) {
+ return key in obj ? obj[key] : obj[key] = value
+};
+goog.object.clone = function(obj) {
+ var res = {};
+ for(var key in obj) {
+ res[key] = obj[key]
+ }
+ return res
+};
+goog.object.transpose = function(obj) {
+ var transposed = {};
+ for(var key in obj) {
+ transposed[obj[key]] = key
+ }
+ return transposed
+};
+goog.object.PROTOTYPE_FIELDS_ = ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"];
+goog.object.extend = function(target, var_args) {
+ var key, source;
+ for(var i = 1;i < arguments.length;i++) {
+ source = arguments[i];
+ for(key in source) {
+ target[key] = source[key]
+ }
+ for(var j = 0;j < goog.object.PROTOTYPE_FIELDS_.length;j++) {
+ key = goog.object.PROTOTYPE_FIELDS_[j];
+ if(Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key]
+ }
+ }
+ }
+};
+goog.object.create = function(var_args) {
+ var argLength = arguments.length;
+ if(argLength == 1 && goog.isArray(arguments[0])) {
+ return goog.object.create.apply(null, arguments[0])
+ }
+ if(argLength % 2) {
+ throw Error("Uneven number of arguments");
+ }
+ var rv = {};
+ for(var i = 0;i < argLength;i += 2) {
+ rv[arguments[i]] = arguments[i + 1]
+ }
+ return rv
+};
+goog.object.createSet = function(var_args) {
+ var argLength = arguments.length;
+ if(argLength == 1 && goog.isArray(arguments[0])) {
+ return goog.object.createSet.apply(null, arguments[0])
+ }
+ var rv = {};
+ for(var i = 0;i < argLength;i++) {
+ rv[arguments[i]] = true
+ }
+ return rv
+};
+goog.provide("goog.dom");
+goog.provide("goog.dom.DomHelper");
+goog.provide("goog.dom.NodeType");
+goog.require("goog.array");
+goog.require("goog.dom.BrowserFeature");
+goog.require("goog.dom.TagName");
+goog.require("goog.dom.classes");
+goog.require("goog.math.Coordinate");
+goog.require("goog.math.Size");
+goog.require("goog.object");
+goog.require("goog.string");
+goog.require("goog.userAgent");
+goog.dom.ASSUME_QUIRKS_MODE = false;
+goog.dom.ASSUME_STANDARDS_MODE = false;
+goog.dom.COMPAT_MODE_KNOWN_ = goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
+goog.dom.NodeType = {ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, ENTITY_REFERENCE:5, ENTITY:6, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9, DOCUMENT_TYPE:10, DOCUMENT_FRAGMENT:11, NOTATION:12};
+goog.dom.getDomHelper = function(opt_element) {
+ return opt_element ? new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) : goog.dom.defaultDomHelper_ || (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper)
+};
+goog.dom.defaultDomHelper_;
+goog.dom.getDocument = function() {
+ return document
+};
+goog.dom.getElement = function(element) {
+ return goog.isString(element) ? document.getElementById(element) : element
+};
+goog.dom.$ = goog.dom.getElement;
+goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementsByTagNameAndClass_(document, opt_tag, opt_class, opt_el)
+};
+goog.dom.getElementsByClass = function(className, opt_el) {
+ var parent = opt_el || document;
+ if(goog.dom.canUseQuerySelector_(parent)) {
+ return parent.querySelectorAll("." + className)
+ }else {
+ if(parent.getElementsByClassName) {
+ return parent.getElementsByClassName(className)
+ }
+ }
+ return goog.dom.getElementsByTagNameAndClass_(document, "*", className, opt_el)
+};
+goog.dom.getElementByClass = function(className, opt_el) {
+ var parent = opt_el || document;
+ var retVal = null;
+ if(goog.dom.canUseQuerySelector_(parent)) {
+ retVal = parent.querySelector("." + className)
+ }else {
+ retVal = goog.dom.getElementsByClass(className, opt_el)[0]
+ }
+ return retVal || null
+};
+goog.dom.canUseQuerySelector_ = function(parent) {
+ return parent.querySelectorAll && parent.querySelector && (!goog.userAgent.WEBKIT || goog.dom.isCss1CompatMode_(document) || goog.userAgent.isVersion("528"))
+};
+goog.dom.getElementsByTagNameAndClass_ = function(doc, opt_tag, opt_class, opt_el) {
+ var parent = opt_el || doc;
+ var tagName = opt_tag && opt_tag != "*" ? opt_tag.toUpperCase() : "";
+ if(goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {
+ var query = tagName + (opt_class ? "." + opt_class : "");
+ return parent.querySelectorAll(query)
+ }
+ if(opt_class && parent.getElementsByClassName) {
+ var els = parent.getElementsByClassName(opt_class);
+ if(tagName) {
+ var arrayLike = {};
+ var len = 0;
+ for(var i = 0, el;el = els[i];i++) {
+ if(tagName == el.nodeName) {
+ arrayLike[len++] = el
+ }
+ }
+ arrayLike.length = len;
+ return arrayLike
+ }else {
+ return els
+ }
+ }
+ var els = parent.getElementsByTagName(tagName || "*");
+ if(opt_class) {
+ var arrayLike = {};
+ var len = 0;
+ for(var i = 0, el;el = els[i];i++) {
+ var className = el.className;
+ if(typeof className.split == "function" && goog.array.contains(className.split(/\s+/), opt_class)) {
+ arrayLike[len++] = el
+ }
+ }
+ arrayLike.length = len;
+ return arrayLike
+ }else {
+ return els
+ }
+};
+goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;
+goog.dom.setProperties = function(element, properties) {
+ goog.object.forEach(properties, function(val, key) {
+ if(key == "style") {
+ element.style.cssText = val
+ }else {
+ if(key == "class") {
+ element.className = val
+ }else {
+ if(key == "for") {
+ element.htmlFor = val
+ }else {
+ if(key in goog.dom.DIRECT_ATTRIBUTE_MAP_) {
+ element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val)
+ }else {
+ element[key] = val
+ }
+ }
+ }
+ }
+ })
+};
+goog.dom.DIRECT_ATTRIBUTE_MAP_ = {cellpadding:"cellPadding", cellspacing:"cellSpacing", colspan:"colSpan", rowspan:"rowSpan", valign:"vAlign", height:"height", width:"width", usemap:"useMap", frameborder:"frameBorder", type:"type"};
+goog.dom.getViewportSize = function(opt_window) {
+ return goog.dom.getViewportSize_(opt_window || window)
+};
+goog.dom.getViewportSize_ = function(win) {
+ var doc = win.document;
+ if(goog.userAgent.WEBKIT && !goog.userAgent.isVersion("500") && !goog.userAgent.MOBILE) {
+ if(typeof win.innerHeight == "undefined") {
+ win = window
+ }
+ var innerHeight = win.innerHeight;
+ var scrollHeight = win.document.documentElement.scrollHeight;
+ if(win == win.top) {
+ if(scrollHeight < innerHeight) {
+ innerHeight -= 15
+ }
+ }
+ return new goog.math.Size(win.innerWidth, innerHeight)
+ }
+ var readsFromDocumentElement = goog.dom.isCss1CompatMode_(doc);
+ if(goog.userAgent.OPERA && !goog.userAgent.isVersion("9.50")) {
+ readsFromDocumentElement = false
+ }
+ var el = readsFromDocumentElement ? doc.documentElement : doc.body;
+ return new goog.math.Size(el.clientWidth, el.clientHeight)
+};
+goog.dom.getDocumentHeight = function() {
+ return goog.dom.getDocumentHeight_(window)
+};
+goog.dom.getDocumentHeight_ = function(win) {
+ var doc = win.document;
+ var height = 0;
+ if(doc) {
+ var vh = goog.dom.getViewportSize_(win).height;
+ var body = doc.body;
+ var docEl = doc.documentElement;
+ if(goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {
+ height = docEl.scrollHeight != vh ? docEl.scrollHeight : docEl.offsetHeight
+ }else {
+ var sh = docEl.scrollHeight;
+ var oh = docEl.offsetHeight;
+ if(docEl.clientHeight != oh) {
+ sh = body.scrollHeight;
+ oh = body.offsetHeight
+ }
+ if(sh > vh) {
+ height = sh > oh ? sh : oh
+ }else {
+ height = sh < oh ? sh : oh
+ }
+ }
+ }
+ return height
+};
+goog.dom.getPageScroll = function(opt_window) {
+ var win = opt_window || goog.global || window;
+ return goog.dom.getDomHelper(win.document).getDocumentScroll()
+};
+goog.dom.getDocumentScroll = function() {
+ return goog.dom.getDocumentScroll_(document)
+};
+goog.dom.getDocumentScroll_ = function(doc) {
+ var el = goog.dom.getDocumentScrollElement_(doc);
+ return new goog.math.Coordinate(el.scrollLeft, el.scrollTop)
+};
+goog.dom.getDocumentScrollElement = function() {
+ return goog.dom.getDocumentScrollElement_(document)
+};
+goog.dom.getDocumentScrollElement_ = function(doc) {
+ return!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body
+};
+goog.dom.getWindow = function(opt_doc) {
+ return opt_doc ? goog.dom.getWindow_(opt_doc) : window
+};
+goog.dom.getWindow_ = function(doc) {
+ return doc.parentWindow || doc.defaultView
+};
+goog.dom.createDom = function(tagName, opt_attributes, var_args) {
+ return goog.dom.createDom_(document, arguments)
+};
+goog.dom.createDom_ = function(doc, args) {
+ var tagName = args[0];
+ var attributes = args[1];
+ if(!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes && (attributes.name || attributes.type)) {
+ var tagNameArr = ["<", tagName];
+ if(attributes.name) {
+ tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name), '"')
+ }
+ if(attributes.type) {
+ tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type), '"');
+ var clone = {};
+ goog.object.extend(clone, attributes);
+ attributes = clone;
+ delete attributes.type
+ }
+ tagNameArr.push(">");
+ tagName = tagNameArr.join("")
+ }
+ var element = doc.createElement(tagName);
+ if(attributes) {
+ if(goog.isString(attributes)) {
+ element.className = attributes
+ }else {
+ if(goog.isArray(attributes)) {
+ goog.dom.classes.add.apply(null, [element].concat(attributes))
+ }else {
+ goog.dom.setProperties(element, attributes)
+ }
+ }
+ }
+ if(args.length > 2) {
+ goog.dom.append_(doc, element, args, 2)
+ }
+ return element
+};
+goog.dom.append_ = function(doc, parent, args, startIndex) {
+ function childHandler(child) {
+ if(child) {
+ parent.appendChild(goog.isString(child) ? doc.createTextNode(child) : child)
+ }
+ }
+ for(var i = startIndex;i < args.length;i++) {
+ var arg = args[i];
+ if(goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
+ goog.array.forEach(goog.dom.isNodeList(arg) ? goog.array.clone(arg) : arg, childHandler)
+ }else {
+ childHandler(arg)
+ }
+ }
+};
+goog.dom.$dom = goog.dom.createDom;
+goog.dom.createElement = function(name) {
+ return document.createElement(name)
+};
+goog.dom.createTextNode = function(content) {
+ return document.createTextNode(content)
+};
+goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
+ return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp)
+};
+goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
+ var rowHtml = ["<tr>"];
+ for(var i = 0;i < columns;i++) {
+ rowHtml.push(fillWithNbsp ? "<td>&nbsp;</td>" : "<td></td>")
+ }
+ rowHtml.push("</tr>");
+ rowHtml = rowHtml.join("");
+ var totalHtml = ["<table>"];
+ for(i = 0;i < rows;i++) {
+ totalHtml.push(rowHtml)
+ }
+ totalHtml.push("</table>");
+ var elem = doc.createElement(goog.dom.TagName.DIV);
+ elem.innerHTML = totalHtml.join("");
+ return elem.firstChild.remove()
+};
+goog.dom.htmlToDocumentFragment = function(htmlString) {
+ return goog.dom.htmlToDocumentFragment_(document, htmlString)
+};
+goog.dom.htmlToDocumentFragment_ = function(doc, htmlString) {
+ var tempDiv = doc.createElement("div");
+ if(goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
+ tempDiv.innerHTML = "<br>" + htmlString;
+ tempDiv.firstChild.remove()
+ }else {
+ tempDiv.innerHTML = htmlString
+ }
+ if(tempDiv.childNodes.length == 1) {
+ return tempDiv.firstChild.remove()
+ }else {
+ var fragment = doc.createDocumentFragment();
+ while(tempDiv.firstChild) {
+ fragment.appendChild(tempDiv.firstChild)
+ }
+ return fragment
+ }
+};
+goog.dom.getCompatMode = function() {
+ return goog.dom.isCss1CompatMode() ? "CSS1Compat" : "BackCompat"
+};
+goog.dom.isCss1CompatMode = function() {
+ return goog.dom.isCss1CompatMode_(document)
+};
+goog.dom.isCss1CompatMode_ = function(doc) {
+ if(goog.dom.COMPAT_MODE_KNOWN_) {
+ return goog.dom.ASSUME_STANDARDS_MODE
+ }
+ return doc.compatMode == "CSS1Compat"
+};
+goog.dom.canHaveChildren = function(node) {
+ if(node.nodeType != goog.dom.NodeType.ELEMENT) {
+ return false
+ }
+ switch(node.tagName) {
+ case goog.dom.TagName.APPLET:
+ ;
+ case goog.dom.TagName.AREA:
+ ;
+ case goog.dom.TagName.BASE:
+ ;
+ case goog.dom.TagName.BR:
+ ;
+ case goog.dom.TagName.COL:
+ ;
+ case goog.dom.TagName.FRAME:
+ ;
+ case goog.dom.TagName.HR:
+ ;
+ case goog.dom.TagName.IMG:
+ ;
+ case goog.dom.TagName.INPUT:
+ ;
+ case goog.dom.TagName.IFRAME:
+ ;
+ case goog.dom.TagName.ISINDEX:
+ ;
+ case goog.dom.TagName.LINK:
+ ;
+ case goog.dom.TagName.NOFRAMES:
+ ;
+ case goog.dom.TagName.NOSCRIPT:
+ ;
+ case goog.dom.TagName.META:
+ ;
+ case goog.dom.TagName.OBJECT:
+ ;
+ case goog.dom.TagName.PARAM:
+ ;
+ case goog.dom.TagName.SCRIPT:
+ ;
+ case goog.dom.TagName.STYLE:
+ return false
+ }
+ return true
+};
+goog.dom.appendChild = function(parent, child) {
+ parent.appendChild(child)
+};
+goog.dom.append = function(parent, var_args) {
+ goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1)
+};
+goog.dom.removeChildren = function(node) {
+ var child;
+ while(child = node.firstChild) {
+ node.removeChild(child)
+ }
+};
+goog.dom.insertSiblingBefore = function(newNode, refNode) {
+ if(refNode.parentNode) {
+ refNode.parentNode.insertBefore(newNode, refNode)
+ }
+};
+goog.dom.insertSiblingAfter = function(newNode, refNode) {
+ if(refNode.parentNode) {
+ refNode.parentNode.insertBefore(newNode, refNode.nextSibling)
+ }
+};
+goog.dom.removeNode = function(node) {
+ return node && node.parentNode ? node.remove() : null
+};
+goog.dom.replaceNode = function(newNode, oldNode) {
+ var parent = oldNode.parentNode;
+ if(parent) {
+ parent.replaceChild(newNode, oldNode)
+ }
+};
+goog.dom.flattenElement = function(element) {
+ var child, parent = element.parentNode;
+ if(parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
+ if(element.removeNode) {
+ return element.removeNode(false)
+ }else {
+ while(child = element.firstChild) {
+ parent.insertBefore(child, element)
+ }
+ return goog.dom.removeNode(element)
+ }
+ }
+};
+goog.dom.getFirstElementChild = function(node) {
+ return goog.dom.getNextElementNode_(node.firstChild, true)
+};
+goog.dom.getLastElementChild = function(node) {
+ return goog.dom.getNextElementNode_(node.lastChild, false)
+};
+goog.dom.getNextElementSibling = function(node) {
+ return goog.dom.getNextElementNode_(node.nextSibling, true)
+};
+goog.dom.getPreviousElementSibling = function(node) {
+ return goog.dom.getNextElementNode_(node.previousSibling, false)
+};
+goog.dom.getNextElementNode_ = function(node, forward) {
+ while(node && node.nodeType != goog.dom.NodeType.ELEMENT) {
+ node = forward ? node.nextSibling : node.previousSibling
+ }
+ return node
+};
+goog.dom.getNextNode = function(node) {
+ if(!node) {
+ return null
+ }
+ if(node.firstChild) {
+ return node.firstChild
+ }
+ while(node && !node.nextSibling) {
+ node = node.parentNode
+ }
+ return node ? node.nextSibling : null
+};
+goog.dom.getPreviousNode = function(node) {
+ if(!node) {
+ return null
+ }
+ if(!node.previousSibling) {
+ return node.parentNode
+ }
+ node = node.previousSibling;
+ while(node && node.lastChild) {
+ node = node.lastChild
+ }
+ return node
+};
+goog.dom.isNodeLike = function(obj) {
+ return goog.isObject(obj) && obj.nodeType > 0
+};
+goog.dom.contains = function(parent, descendant) {
+ if(parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
+ return parent == descendant || parent.contains(descendant)
+ }
+ if(typeof parent.compareDocumentPosition != "undefined") {
+ return parent == descendant || Boolean(parent.compareDocumentPosition(descendant) & 16)
+ }
+ while(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 == goog.dom.NodeType.ELEMENT;
+ var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;
+ if(isElement1 && isElement2) {
+ return node1.sourceIndex - node2.sourceIndex
+ }else {
+ var parent1 = node1.parentNode;
+ var 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);
+ var 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
+ }
+ var sibling = node;
+ while(sibling.parentNode != parent) {
+ sibling = sibling.parentNode
+ }
+ return goog.dom.compareSiblingOrder_(sibling, textNode)
+};
+goog.dom.compareSiblingOrder_ = function(node1, node2) {
+ var s = node2;
+ while(s = s.previousSibling) {
+ if(s == node1) {
+ return-1
+ }
+ }
+ return 1
+};
+goog.dom.findCommonAncestor = function(var_args) {
+ var i, count = arguments.length;
+ if(!count) {
+ return null
+ }else {
+ if(count == 1) {
+ return arguments[0]
+ }
+ }
+ var paths = [];
+ var minLength = Infinity;
+ for(i = 0;i < count;i++) {
+ var ancestors = [];
+ var node = arguments[i];
+ while(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++) {
+ var first = paths[0][i];
+ for(var j = 1;j < count;j++) {
+ if(first != paths[j][i]) {
+ return output
+ }
+ }
+ output = first
+ }
+ return output
+};
+goog.dom.getOwnerDocument = function(node) {
+ return node.nodeType == goog.dom.NodeType.DOCUMENT ? node : node.ownerDocument || node.document
+};
+goog.dom.getFrameContentDocument = function(frame) {
+ var doc;
+ if(goog.userAgent.WEBKIT) {
+ doc = frame.document || frame.contentWindow.document
+ }else {
+ doc = frame.contentDocument || frame.contentWindow.document
+ }
+ return doc
+};
+goog.dom.getFrameContentWindow = function(frame) {
+ return frame.contentWindow || goog.dom.getWindow_(goog.dom.getFrameContentDocument(frame))
+};
+goog.dom.setTextContent = function(element, text) {
+ if("textContent" in element) {
+ element.textContent = text
+ }else {
+ if(element.firstChild && element.firstChild.nodeType == goog.dom.NodeType.TEXT) {
+ while(element.lastChild != element.firstChild) {
+ element.removeChild(element.lastChild)
+ }
+ element.firstChild.data = text
+ }else {
+ goog.dom.removeChildren(element);
+ var doc = goog.dom.getOwnerDocument(element);
+ element.appendChild(doc.createTextNode(text))
+ }
+ }
+};
+goog.dom.getOuterHtml = function(element) {
+ if("outerHTML" in element) {
+ return element.outerHTML
+ }else {
+ var doc = goog.dom.getOwnerDocument(element);
+ var div = doc.createElement("div");
+ div.appendChild(element.cloneNode(true));
+ return div.innerHTML
+ }
+};
+goog.dom.findNode = function(root, p) {
+ var rv = [];
+ var found = goog.dom.findNodes_(root, p, rv, true);
+ return found ? rv[0] : undefined
+};
+goog.dom.findNodes = function(root, p) {
+ var rv = [];
+ goog.dom.findNodes_(root, p, rv, false);
+ return rv
+};
+goog.dom.findNodes_ = function(root, p, rv, findOne) {
+ if(root != null) {
+ for(var i = 0, child;child = root.childNodes[i];i++) {
+ if(p(child)) {
+ rv.push(child);
+ if(findOne) {
+ return true
+ }
+ }
+ if(goog.dom.findNodes_(child, p, rv, findOne)) {
+ return true
+ }
+ }
+ }
+ return false
+};
+goog.dom.TAGS_TO_IGNORE_ = {SCRIPT:1, STYLE:1, HEAD:1, IFRAME:1, OBJECT:1};
+goog.dom.PREDEFINED_TAG_VALUES_ = {IMG:" ", BR:"\n"};
+goog.dom.isFocusableTabIndex = function(element) {
+ var attrNode = element.getAttributeNode("tabindex");
+ if(attrNode && attrNode.specified) {
+ var index = element.tabIndex;
+ return goog.isNumber(index) && index >= 0
+ }
+ return false
+};
+goog.dom.setFocusableTabIndex = function(element, enable) {
+ if(enable) {
+ element.tabIndex = 0
+ }else {
+ element.removeAttribute("tabIndex")
+ }
+};
+goog.dom.getTextContent = function(node) {
+ var textContent;
+ if(goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && "innerText" in node) {
+ textContent = goog.string.canonicalizeNewlines(node.innerText)
+ }else {
+ var buf = [];
+ goog.dom.getTextContent_(node, buf, true);
+ textContent = buf.join("")
+ }
+ textContent = textContent.replace(/ \xAD /g, " ").replace(/\xAD/g, "");
+ if(!goog.userAgent.IE) {
+ textContent = textContent.replace(/ +/g, " ")
+ }
+ if(textContent != " ") {
+ textContent = textContent.replace(/^\s*/, "")
+ }
+ return textContent
+};
+goog.dom.getRawTextContent = function(node) {
+ var buf = [];
+ goog.dom.getTextContent_(node, buf, false);
+ return buf.join("")
+};
+goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
+ if(node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
+ }else {
+ if(node.nodeType == goog.dom.NodeType.TEXT) {
+ if(normalizeWhitespace) {
+ buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ""))
+ }else {
+ buf.push(node.nodeValue)
+ }
+ }else {
+ if(node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
+ buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName])
+ }else {
+ var child = node.firstChild;
+ while(child) {
+ goog.dom.getTextContent_(child, buf, normalizeWhitespace);
+ child = child.nextSibling
+ }
+ }
+ }
+ }
+};
+goog.dom.getNodeTextLength = function(node) {
+ return goog.dom.getTextContent(node).length
+};
+goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {
+ var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;
+ var buf = [];
+ while(node && node != root) {
+ var cur = node;
+ while(cur = cur.previousSibling) {
+ buf.unshift(goog.dom.getTextContent(cur))
+ }
+ node = node.parentNode
+ }
+ return goog.string.trimLeft(buf.join("")).replace(/ +/g, " ").length
+};
+goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
+ var stack = [parent], pos = 0, cur;
+ while(stack.length > 0 && pos < offset) {
+ cur = stack.pop();
+ if(cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
+ }else {
+ if(cur.nodeType == goog.dom.NodeType.TEXT) {
+ var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, "").replace(/ +/g, " ");
+ pos += text.length
+ }else {
+ if(cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
+ pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length
+ }else {
+ for(var i = cur.childNodes.length - 1;i >= 0;i--) {
+ stack.push(cur.childNodes[i])
+ }
+ }
+ }
+ }
+ }
+ if(goog.isObject(opt_result)) {
+ opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;
+ opt_result.node = cur
+ }
+ return cur
+};
+goog.dom.isNodeList = function(val) {
+ if(val && typeof val.length == "number") {
+ if(goog.isObject(val)) {
+ return typeof val.item == "function" || typeof val.item == "string"
+ }else {
+ if(goog.isFunction(val)) {
+ return typeof val.item == "function"
+ }
+ }
+ }
+ return false
+};
+goog.dom.getAncestorByTagNameAndClass = function(element, opt_tag, opt_class) {
+ var tagName = opt_tag ? opt_tag.toUpperCase() : null;
+ return goog.dom.getAncestor(element, function(node) {
+ return(!tagName || node.nodeName == tagName) && (!opt_class || goog.dom.classes.has(node, opt_class))
+ }, true)
+};
+goog.dom.getAncestor = function(element, matcher, opt_includeNode, opt_maxSearchSteps) {
+ if(!opt_includeNode) {
+ element = element.parentNode
+ }
+ var ignoreSearchSteps = opt_maxSearchSteps == null;
+ var steps = 0;
+ while(element && (ignoreSearchSteps || steps <= opt_maxSearchSteps)) {
+ if(matcher(element)) {
+ return element
+ }
+ element = element.parentNode;
+ steps++
+ }
+ return null
+};
+goog.dom.DomHelper = function(opt_document) {
+ this.document_ = opt_document || goog.global.document || document
+};
+goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
+goog.dom.DomHelper.prototype.setDocument = function(document) {
+ this.document_ = document
+};
+goog.dom.DomHelper.prototype.getDocument = function() {
+ return this.document_
+};
+goog.dom.DomHelper.prototype.getElement = function(element) {
+ if(goog.isString(element)) {
+ return this.document_.getElementById(element)
+ }else {
+ return element
+ }
+};
+goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
+goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementsByTagNameAndClass_(this.document_, opt_tag, opt_class, opt_el)
+};
+goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
+ var doc = opt_el || this.document_;
+ return goog.dom.getElementsByClass(className, doc)
+};
+goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
+ var doc = opt_el || this.document_;
+ return goog.dom.getElementByClass(className, doc)
+};
+goog.dom.DomHelper.prototype.$$ = goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
+goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
+goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
+ return goog.dom.getViewportSize(opt_window || this.getWindow())
+};
+goog.dom.DomHelper.prototype.getDocumentHeight = function() {
+ return goog.dom.getDocumentHeight_(this.getWindow())
+};
+goog.dom.Appendable;
+goog.dom.DomHelper.prototype.createDom = function(tagName, opt_attributes, var_args) {
+ return goog.dom.createDom_(this.document_, arguments)
+};
+goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
+goog.dom.DomHelper.prototype.createElement = function(name) {
+ return this.document_.createElement(name)
+};
+goog.dom.DomHelper.prototype.createTextNode = function(content) {
+ return this.document_.createTextNode(content)
+};
+goog.dom.DomHelper.prototype.createTable = function(rows, columns, opt_fillWithNbsp) {
+ return goog.dom.createTable_(this.document_, rows, columns, !!opt_fillWithNbsp)
+};
+goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) {
+ return goog.dom.htmlToDocumentFragment_(this.document_, htmlString)
+};
+goog.dom.DomHelper.prototype.getCompatMode = function() {
+ return this.isCss1CompatMode() ? "CSS1Compat" : "BackCompat"
+};
+goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
+ return goog.dom.isCss1CompatMode_(this.document_)
+};
+goog.dom.DomHelper.prototype.getWindow = function() {
+ return goog.dom.getWindow_(this.document_)
+};
+goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
+ return goog.dom.getDocumentScrollElement_(this.document_)
+};
+goog.dom.DomHelper.prototype.getDocumentScroll = function() {
+ return goog.dom.getDocumentScroll_(this.document_)
+};
+goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
+goog.dom.DomHelper.prototype.append = goog.dom.append;
+goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
+goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
+goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
+goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
+goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
+goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
+goog.dom.DomHelper.prototype.getFirstElementChild = goog.dom.getFirstElementChild;
+goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
+goog.dom.DomHelper.prototype.getNextElementSibling = goog.dom.getNextElementSibling;
+goog.dom.DomHelper.prototype.getPreviousElementSibling = goog.dom.getPreviousElementSibling;
+goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
+goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
+goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
+goog.dom.DomHelper.prototype.contains = goog.dom.contains;
+goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
+goog.dom.DomHelper.prototype.getFrameContentDocument = goog.dom.getFrameContentDocument;
+goog.dom.DomHelper.prototype.getFrameContentWindow = goog.dom.getFrameContentWindow;
+goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
+goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
+goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
+goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
+goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
+goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
+goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass = goog.dom.getAncestorByTagNameAndClass;
+goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;
+goog.provide("goog.Disposable");
+goog.provide("goog.dispose");
+goog.Disposable = function() {
+};
+goog.Disposable.prototype.disposed_ = false;
+goog.Disposable.prototype.isDisposed = function() {
+ return this.disposed_
+};
+goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
+goog.Disposable.prototype.dispose = function() {
+ if(!this.disposed_) {
+ this.disposed_ = true;
+ this.disposeInternal()
+ }
+};
+goog.Disposable.prototype.disposeInternal = function() {
+};
+goog.dispose = function(obj) {
+ if(obj && typeof obj.dispose == "function") {
+ obj.dispose()
+ }
+};
+goog.provide("goog.structs");
+goog.require("goog.array");
+goog.require("goog.object");
+goog.structs.getCount = function(col) {
+ if(typeof col.getCount == "function") {
+ return col.getCount()
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return col.length
+ }
+ return goog.object.getCount(col)
+};
+goog.structs.getValues = function(col) {
+ if(typeof col.getValues == "function") {
+ return col.getValues()
+ }
+ if(goog.isString(col)) {
+ return col.split("")
+ }
+ if(goog.isArrayLike(col)) {
+ var rv = [];
+ var l = col.length;
+ for(var i = 0;i < l;i++) {
+ rv.push(col[i])
+ }
+ return rv
+ }
+ return goog.object.getValues(col)
+};
+goog.structs.getKeys = function(col) {
+ if(typeof col.getKeys == "function") {
+ return col.getKeys()
+ }
+ if(typeof col.getValues == "function") {
+ return undefined
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ var rv = [];
+ var l = col.length;
+ for(var i = 0;i < l;i++) {
+ rv.push(i)
+ }
+ return rv
+ }
+ return goog.object.getKeys(col)
+};
+goog.structs.contains = function(col, val) {
+ if(typeof col.contains == "function") {
+ return col.contains(val)
+ }
+ if(typeof col.containsValue == "function") {
+ return col.containsValue(val)
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.contains(col, val)
+ }
+ return goog.object.containsValue(col, val)
+};
+goog.structs.isEmpty = function(col) {
+ if(typeof col.isEmpty == "function") {
+ return col.isEmpty()
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.isEmpty(col)
+ }
+ return goog.object.isEmpty(col)
+};
+goog.structs.clear = function(col) {
+ if(typeof col.clear == "function") {
+ col.clear()
+ }else {
+ if(goog.isArrayLike(col)) {
+ goog.array.clear(col)
+ }else {
+ goog.object.clear(col)
+ }
+ }
+};
+goog.structs.forEach = function(col, f, opt_obj) {
+ if(typeof col.forEach == "function") {
+ col.forEach(f, opt_obj)
+ }else {
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ goog.array.forEach(col, f, opt_obj)
+ }else {
+ var keys = goog.structs.getKeys(col);
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ for(var i = 0;i < l;i++) {
+ f.call(opt_obj, values[i], keys && keys[i], col)
+ }
+ }
+ }
+};
+goog.structs.filter = function(col, f, opt_obj) {
+ if(typeof col.filter == "function") {
+ return col.filter(f, opt_obj)
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.filter(col, f, opt_obj)
+ }
+ var rv;
+ var keys = goog.structs.getKeys(col);
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ if(keys) {
+ rv = {};
+ for(var i = 0;i < l;i++) {
+ if(f.call(opt_obj, values[i], keys[i], col)) {
+ rv[keys[i]] = values[i]
+ }
+ }
+ }else {
+ rv = [];
+ for(var i = 0;i < l;i++) {
+ if(f.call(opt_obj, values[i], undefined, col)) {
+ rv.push(values[i])
+ }
+ }
+ }
+ return rv
+};
+goog.structs.map = function(col, f, opt_obj) {
+ if(typeof col.map == "function") {
+ return col.map(f, opt_obj)
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.map(col, f, opt_obj)
+ }
+ var rv;
+ var keys = goog.structs.getKeys(col);
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ if(keys) {
+ rv = {};
+ for(var i = 0;i < l;i++) {
+ rv[keys[i]] = f.call(opt_obj, values[i], keys[i], col)
+ }
+ }else {
+ rv = [];
+ for(var i = 0;i < l;i++) {
+ rv[i] = f.call(opt_obj, values[i], undefined, col)
+ }
+ }
+ return rv
+};
+goog.structs.some = function(col, f, opt_obj) {
+ if(typeof col.some == "function") {
+ return col.some(f, opt_obj)
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.some(col, f, opt_obj)
+ }
+ var keys = goog.structs.getKeys(col);
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ for(var i = 0;i < l;i++) {
+ if(f.call(opt_obj, values[i], keys && keys[i], col)) {
+ return true
+ }
+ }
+ return false
+};
+goog.structs.every = function(col, f, opt_obj) {
+ if(typeof col.every == "function") {
+ return col.every(f, opt_obj)
+ }
+ if(goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.every(col, f, opt_obj)
+ }
+ var keys = goog.structs.getKeys(col);
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ for(var i = 0;i < l;i++) {
+ if(!f.call(opt_obj, values[i], keys && keys[i], col)) {
+ return false
+ }
+ }
+ return true
+};
+goog.provide("goog.iter");
+goog.provide("goog.iter.Iterator");
+goog.provide("goog.iter.StopIteration");
+goog.require("goog.array");
+goog.iter.Iterable;
+if("StopIteration" in goog.global) {
+ goog.iter.StopIteration = goog.global["StopIteration"]
+}else {
+ goog.iter.StopIteration = Error("StopIteration")
+}
+goog.iter.Iterator = function() {
+};
+goog.iter.Iterator.prototype.next = function() {
+ throw goog.iter.StopIteration;
+};
+goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {
+ return this
+};
+goog.iter.toIterator = function(iterable) {
+ if(iterable instanceof goog.iter.Iterator) {
+ return iterable
+ }
+ if(typeof iterable.__iterator__ == "function") {
+ return iterable.__iterator__(false)
+ }
+ if(goog.isArrayLike(iterable)) {
+ var i = 0;
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ while(true) {
+ if(i >= iterable.length) {
+ throw goog.iter.StopIteration;
+ }
+ if(!(i in iterable)) {
+ i++;
+ continue
+ }
+ return iterable[i++]
+ }
+ };
+ return newIter
+ }
+ throw Error("Not implemented");
+};
+goog.iter.forEach = function(iterable, f, opt_obj) {
+ if(goog.isArrayLike(iterable)) {
+ try {
+ goog.array.forEach(iterable, f, opt_obj)
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ }
+ }else {
+ iterable = goog.iter.toIterator(iterable);
+ try {
+ while(true) {
+ f.call(opt_obj, iterable.next(), undefined, iterable)
+ }
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ }
+ }
+};
+goog.iter.filter = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ while(true) {
+ var val = iterable.next();
+ if(f.call(opt_obj, val, undefined, iterable)) {
+ return val
+ }
+ }
+ };
+ return newIter
+};
+goog.iter.range = function(startOrStop, opt_stop, opt_step) {
+ var start = 0;
+ var stop = startOrStop;
+ var step = opt_step || 1;
+ if(arguments.length > 1) {
+ start = startOrStop;
+ stop = opt_stop
+ }
+ if(step == 0) {
+ throw Error("Range step argument must not be zero");
+ }
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ if(step > 0 && start >= stop || step < 0 && start <= stop) {
+ throw goog.iter.StopIteration;
+ }
+ var rv = start;
+ start += step;
+ return rv
+ };
+ return newIter
+};
+goog.iter.join = function(iterable, deliminator) {
+ return goog.iter.toArray(iterable).join(deliminator)
+};
+goog.iter.map = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ while(true) {
+ var val = iterable.next();
+ return f.call(opt_obj, val, undefined, iterable)
+ }
+ };
+ return newIter
+};
+goog.iter.reduce = function(iterable, f, val, opt_obj) {
+ var rval = val;
+ goog.iter.forEach(iterable, function(val) {
+ rval = f.call(opt_obj, rval, val)
+ });
+ return rval
+};
+goog.iter.some = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ try {
+ while(true) {
+ if(f.call(opt_obj, iterable.next(), undefined, iterable)) {
+ return true
+ }
+ }
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ }
+ return false
+};
+goog.iter.every = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ try {
+ while(true) {
+ if(!f.call(opt_obj, iterable.next(), undefined, iterable)) {
+ return false
+ }
+ }
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ }
+ return true
+};
+goog.iter.chain = function(var_args) {
+ var args = arguments;
+ var length = args.length;
+ var i = 0;
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ try {
+ if(i >= length) {
+ throw goog.iter.StopIteration;
+ }
+ var current = goog.iter.toIterator(args[i]);
+ return current.next()
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration || i >= length) {
+ throw ex;
+ }else {
+ i++;
+ return this.next()
+ }
+ }
+ };
+ return newIter
+};
+goog.iter.dropWhile = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ var dropping = true;
+ newIter.next = function() {
+ while(true) {
+ var val = iterable.next();
+ if(dropping && f.call(opt_obj, val, undefined, iterable)) {
+ continue
+ }else {
+ dropping = false
+ }
+ return val
+ }
+ };
+ return newIter
+};
+goog.iter.takeWhile = function(iterable, f, opt_obj) {
+ iterable = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ var taking = true;
+ newIter.next = function() {
+ while(true) {
+ if(taking) {
+ var val = iterable.next();
+ if(f.call(opt_obj, val, undefined, iterable)) {
+ return val
+ }else {
+ taking = false
+ }
+ }else {
+ throw goog.iter.StopIteration;
+ }
+ }
+ };
+ return newIter
+};
+goog.iter.toArray = function(iterable) {
+ if(goog.isArrayLike(iterable)) {
+ return goog.array.toArray(iterable)
+ }
+ iterable = goog.iter.toIterator(iterable);
+ var array = [];
+ goog.iter.forEach(iterable, function(val) {
+ array.push(val)
+ });
+ return array
+};
+goog.iter.equals = function(iterable1, iterable2) {
+ iterable1 = goog.iter.toIterator(iterable1);
+ iterable2 = goog.iter.toIterator(iterable2);
+ var b1, b2;
+ try {
+ while(true) {
+ b1 = b2 = false;
+ var val1 = iterable1.next();
+ b1 = true;
+ var val2 = iterable2.next();
+ b2 = true;
+ if(val1 != val2) {
+ return false
+ }
+ }
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }else {
+ if(b1 && !b2) {
+ return false
+ }
+ if(!b2) {
+ try {
+ val2 = iterable2.next();
+ return false
+ }catch(ex1) {
+ if(ex1 !== goog.iter.StopIteration) {
+ throw ex1;
+ }
+ return true
+ }
+ }
+ }
+ }
+ return false
+};
+goog.iter.nextOrValue = function(iterable, defaultValue) {
+ try {
+ return goog.iter.toIterator(iterable).next()
+ }catch(e) {
+ if(e != goog.iter.StopIteration) {
+ throw e;
+ }
+ return defaultValue
+ }
+};
+goog.provide("goog.structs.Map");
+goog.require("goog.iter.Iterator");
+goog.require("goog.iter.StopIteration");
+goog.require("goog.object");
+goog.require("goog.structs");
+goog.structs.Map = function(opt_map, var_args) {
+ this.map_ = {};
+ this.keys_ = [];
+ var argLength = arguments.length;
+ if(argLength > 1) {
+ if(argLength % 2) {
+ throw Error("Uneven number of arguments");
+ }
+ for(var i = 0;i < argLength;i += 2) {
+ this.set(arguments[i], arguments[i + 1])
+ }
+ }else {
+ if(opt_map) {
+ this.addAll(opt_map)
+ }
+ }
+};
+goog.structs.Map.prototype.count_ = 0;
+goog.structs.Map.prototype.version_ = 0;
+goog.structs.Map.prototype.getCount = function() {
+ return this.count_
+};
+goog.structs.Map.prototype.getValues = function() {
+ this.cleanupKeysArray_();
+ var rv = [];
+ for(var i = 0;i < this.keys_.length;i++) {
+ var key = this.keys_[i];
+ rv.push(this.map_[key])
+ }
+ return rv
+};
+goog.structs.Map.prototype.getKeys = function() {
+ this.cleanupKeysArray_();
+ return this.keys_.concat()
+};
+goog.structs.Map.prototype.containsKey = function(key) {
+ return goog.structs.Map.hasKey_(this.map_, key)
+};
+goog.structs.Map.prototype.containsValue = function(val) {
+ for(var i = 0;i < this.keys_.length;i++) {
+ var key = this.keys_[i];
+ if(goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
+ return true
+ }
+ }
+ return false
+};
+goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
+ if(this === otherMap) {
+ return true
+ }
+ if(this.count_ != otherMap.getCount()) {
+ return false
+ }
+ var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
+ this.cleanupKeysArray_();
+ for(var key, i = 0;key = this.keys_[i];i++) {
+ if(!equalityFn(this.get(key), otherMap.get(key))) {
+ return false
+ }
+ }
+ return true
+};
+goog.structs.Map.defaultEquals = function(a, b) {
+ return a === b
+};
+goog.structs.Map.prototype.isEmpty = function() {
+ return this.count_ == 0
+};
+goog.structs.Map.prototype.clear = function() {
+ this.map_ = {};
+ this.keys_.length = 0;
+ this.count_ = 0;
+ this.version_ = 0
+};
+goog.structs.Map.prototype.remove = function(key) {
+ if(goog.structs.Map.hasKey_(this.map_, key)) {
+ delete this.map_[key];
+ this.count_--;
+ this.version_++;
+ if(this.keys_.length > 2 * this.count_) {
+ this.cleanupKeysArray_()
+ }
+ return true
+ }
+ return false
+};
+goog.structs.Map.prototype.cleanupKeysArray_ = function() {
+ if(this.count_ != this.keys_.length) {
+ var srcIndex = 0;
+ var destIndex = 0;
+ while(srcIndex < this.keys_.length) {
+ var key = this.keys_[srcIndex];
+ if(goog.structs.Map.hasKey_(this.map_, key)) {
+ this.keys_[destIndex++] = key
+ }
+ srcIndex++
+ }
+ this.keys_.length = destIndex
+ }
+ if(this.count_ != this.keys_.length) {
+ var seen = {};
+ var srcIndex = 0;
+ var destIndex = 0;
+ while(srcIndex < this.keys_.length) {
+ var key = this.keys_[srcIndex];
+ if(!goog.structs.Map.hasKey_(seen, key)) {
+ this.keys_[destIndex++] = key;
+ seen[key] = 1
+ }
+ srcIndex++
+ }
+ this.keys_.length = destIndex
+ }
+};
+goog.structs.Map.prototype.get = function(key, opt_val) {
+ if(goog.structs.Map.hasKey_(this.map_, key)) {
+ return this.map_[key]
+ }
+ return opt_val
+};
+goog.structs.Map.prototype.set = function(key, value) {
+ if(!goog.structs.Map.hasKey_(this.map_, key)) {
+ this.count_++;
+ this.keys_.push(key);
+ this.version_++
+ }
+ this.map_[key] = value
+};
+goog.structs.Map.prototype.addAll = function(map) {
+ var keys, values;
+ if(map instanceof goog.structs.Map) {
+ keys = map.getKeys();
+ values = map.getValues()
+ }else {
+ keys = goog.object.getKeys(map);
+ values = goog.object.getValues(map)
+ }
+ for(var i = 0;i < keys.length;i++) {
+ this.set(keys[i], values[i])
+ }
+};
+goog.structs.Map.prototype.clone = function() {
+ return new goog.structs.Map(this)
+};
+goog.structs.Map.prototype.transpose = function() {
+ var transposed = new goog.structs.Map;
+ for(var i = 0;i < this.keys_.length;i++) {
+ var key = this.keys_[i];
+ var value = this.map_[key];
+ transposed.set(value, key)
+ }
+ return transposed
+};
+goog.structs.Map.prototype.toObject = function() {
+ this.cleanupKeysArray_();
+ var obj = {};
+ for(var i = 0;i < this.keys_.length;i++) {
+ var key = this.keys_[i];
+ obj[key] = this.map_[key]
+ }
+ return obj
+};
+goog.structs.Map.prototype.getKeyIterator = function() {
+ return this.__iterator__(true)
+};
+goog.structs.Map.prototype.getValueIterator = function() {
+ return this.__iterator__(false)
+};
+goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
+ this.cleanupKeysArray_();
+ var i = 0;
+ var keys = this.keys_;
+ var map = this.map_;
+ var version = this.version_;
+ var selfObj = this;
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ while(true) {
+ if(version != selfObj.version_) {
+ throw Error("The map has changed since the iterator was created");
+ }
+ if(i >= keys.length) {
+ throw goog.iter.StopIteration;
+ }
+ var key = keys[i++];
+ return opt_keys ? key : map[key]
+ }
+ };
+ return newIter
+};
+goog.structs.Map.hasKey_ = function(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key)
+};
+goog.provide("goog.structs.Set");
+goog.require("goog.structs");
+goog.require("goog.structs.Map");
+goog.structs.Set = function(opt_values) {
+ this.map_ = new goog.structs.Map;
+ if(opt_values) {
+ this.addAll(opt_values)
+ }
+};
+goog.structs.Set.getKey_ = function(val) {
+ var type = typeof val;
+ if(type == "object" && val || type == "function") {
+ return"o" + goog.getUid(val)
+ }else {
+ return type.substr(0, 1) + val
+ }
+};
+goog.structs.Set.prototype.getCount = function() {
+ return this.map_.getCount()
+};
+goog.structs.Set.prototype.add = function(element) {
+ this.map_.set(goog.structs.Set.getKey_(element), element)
+};
+goog.structs.Set.prototype.addAll = function(col) {
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ for(var i = 0;i < l;i++) {
+ this.add(values[i])
+ }
+};
+goog.structs.Set.prototype.removeAll = function(col) {
+ var values = goog.structs.getValues(col);
+ var l = values.length;
+ for(var i = 0;i < l;i++) {
+ this.remove(values[i])
+ }
+};
+goog.structs.Set.prototype.remove = function(element) {
+ return this.map_.remove(goog.structs.Set.getKey_(element))
+};
+goog.structs.Set.prototype.clear = function() {
+ this.map_.clear()
+};
+goog.structs.Set.prototype.isEmpty = function() {
+ return this.map_.isEmpty()
+};
+goog.structs.Set.prototype.contains = function(element) {
+ return this.map_.containsKey(goog.structs.Set.getKey_(element))
+};
+goog.structs.Set.prototype.containsAll = function(col) {
+ return goog.structs.every(col, this.contains, this)
+};
+goog.structs.Set.prototype.intersection = function(col) {
+ var result = new goog.structs.Set;
+ var values = goog.structs.getValues(col);
+ for(var i = 0;i < values.length;i++) {
+ var value = values[i];
+ if(this.contains(value)) {
+ result.add(value)
+ }
+ }
+ return result
+};
+goog.structs.Set.prototype.getValues = function() {
+ return this.map_.getValues()
+};
+goog.structs.Set.prototype.clone = function() {
+ return new goog.structs.Set(this)
+};
+goog.structs.Set.prototype.equals = function(col) {
+ return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col)
+};
+goog.structs.Set.prototype.isSubsetOf = function(col) {
+ var colCount = goog.structs.getCount(col);
+ if(this.getCount() > colCount) {
+ return false
+ }
+ if(!(col instanceof goog.structs.Set) && colCount > 5) {
+ col = new goog.structs.Set(col)
+ }
+ return goog.structs.every(this, function(value) {
+ return goog.structs.contains(col, value)
+ })
+};
+goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
+ return this.map_.__iterator__(false)
+};
+goog.provide("goog.debug");
+goog.require("goog.array");
+goog.require("goog.string");
+goog.require("goog.structs.Set");
+goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
+ var target = opt_target || goog.global;
+ var oldErrorHandler = target.onerror;
+ target.onerror = function(message, url, line) {
+ if(oldErrorHandler) {
+ oldErrorHandler(message, url, line)
+ }
+ logFunc({message:message, fileName:url, line:line});
+ return Boolean(opt_cancel)
+ }
+};
+goog.debug.expose = function(obj, opt_showFn) {
+ if(typeof obj == "undefined") {
+ return"undefined"
+ }
+ if(obj == null) {
+ return"NULL"
+ }
+ var str = [];
+ for(var x in obj) {
+ if(!opt_showFn && goog.isFunction(obj[x])) {
+ continue
+ }
+ var s = x + " = ";
+ try {
+ s += obj[x]
+ }catch(e) {
+ s += "*** " + e + " ***"
+ }
+ str.push(s)
+ }
+ return str.join("\n")
+};
+goog.debug.deepExpose = function(obj, opt_showFn) {
+ var previous = new goog.structs.Set;
+ var str = [];
+ var helper = function(obj, space) {
+ var nestspace = space + " ";
+ var indentMultiline = function(str) {
+ return str.replace(/\n/g, "\n" + space)
+ };
+ try {
+ if(!goog.isDef(obj)) {
+ str.push("undefined")
+ }else {
+ if(goog.isNull(obj)) {
+ str.push("NULL")
+ }else {
+ if(goog.isString(obj)) {
+ str.push('"' + indentMultiline(obj) + '"')
+ }else {
+ if(goog.isFunction(obj)) {
+ str.push(indentMultiline(String(obj)))
+ }else {
+ if(goog.isObject(obj)) {
+ if(previous.contains(obj)) {
+ str.push("*** reference loop detected ***")
+ }else {
+ previous.add(obj);
+ str.push("{");
+ for(var x in obj) {
+ if(!opt_showFn && goog.isFunction(obj[x])) {
+ continue
+ }
+ str.push("\n");
+ str.push(nestspace);
+ str.push(x + " = ");
+ helper(obj[x], nestspace)
+ }
+ str.push("\n" + space + "}")
+ }
+ }else {
+ str.push(obj)
+ }
+ }
+ }
+ }
+ }
+ }catch(e) {
+ str.push("*** " + e + " ***")
+ }
+ };
+ helper(obj, "");
+ return str.join("")
+};
+goog.debug.exposeArray = function(arr) {
+ var str = [];
+ for(var i = 0;i < arr.length;i++) {
+ if(goog.isArray(arr[i])) {
+ str.push(goog.debug.exposeArray(arr[i]))
+ }else {
+ str.push(arr[i])
+ }
+ }
+ return"[ " + str.join(", ") + " ]"
+};
+goog.debug.exposeException = function(err, opt_fn) {
+ try {
+ var e = goog.debug.normalizeErrorObject(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.normalizeErrorObject = function(err) {
+ var href = goog.getObjectByName("window.location.href");
+ return typeof err == "string" ? {message:err, name:"Unknown error", lineNumber:"Not available", fileName:href, 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 || href, stack:err.stack || "Not available"} : err
+};
+goog.debug.enhanceError = function(err, opt_message) {
+ var error = typeof err == "string" ? Error(err) : err;
+ if(!error.stack) {
+ error.stack = goog.debug.getStacktrace(arguments.callee.caller)
+ }
+ if(opt_message) {
+ var x = 0;
+ while(error["message" + x]) {
+ ++x
+ }
+ error["message" + x] = String(opt_message)
+ }
+ return error
+};
+goog.debug.getStacktraceSimple = function(opt_depth) {
+ var sb = [];
+ var fn = arguments.callee.caller;
+ var depth = 0;
+ while(fn && (!opt_depth || depth < opt_depth)) {
+ sb.push(goog.debug.getFunctionName(fn));
+ sb.push("()\n");
+ try {
+ fn = fn.caller
+ }catch(e) {
+ sb.push("[exception trying to get caller]\n");
+ break
+ }
+ depth++;
+ if(depth >= goog.debug.MAX_STACK_DEPTH) {
+ sb.push("[...long stack...]");
+ break
+ }
+ }
+ if(opt_depth && depth >= opt_depth) {
+ sb.push("[...reached max depth limit...]")
+ }else {
+ sb.push("[end]")
+ }
+ return sb.join("")
+};
+goog.debug.MAX_STACK_DEPTH = 50;
+goog.debug.getStacktrace = function(opt_fn) {
+ return goog.debug.getStacktraceHelper_(opt_fn || arguments.callee.caller, [])
+};
+goog.debug.getStacktraceHelper_ = function(fn, visited) {
+ var sb = [];
+ if(goog.array.contains(visited, fn)) {
+ sb.push("[...circular reference...]")
+ }else {
+ if(fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
+ sb.push(goog.debug.getFunctionName(fn) + "(");
+ var args = fn.arguments;
+ for(var i = 0;i < args.length;i++) {
+ if(i > 0) {
+ sb.push(", ")
+ }
+ var argDesc;
+ var 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 = goog.debug.getFunctionName(arg);
+ argDesc = argDesc ? 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 {
+ if(fn) {
+ sb.push("[...long stack...]")
+ }else {
+ 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.makeWhitespaceVisible = function(string) {
+ return string.replace(/ /g, "[_]").replace(/\f/g, "[f]").replace(/\n/g, "[n]\n").replace(/\r/g, "[r]").replace(/\t/g, "[t]")
+};
+goog.debug.fnNameCache_ = {};
+goog.provide("goog.debug.LogRecord");
+goog.debug.LogRecord = function(level, msg, loggerName, opt_time, opt_sequenceNumber) {
+ this.reset(level, msg, loggerName, opt_time, opt_sequenceNumber)
+};
+goog.debug.LogRecord.prototype.time_;
+goog.debug.LogRecord.prototype.level_;
+goog.debug.LogRecord.prototype.msg_;
+goog.debug.LogRecord.prototype.loggerName_;
+goog.debug.LogRecord.prototype.sequenceNumber_ = 0;
+goog.debug.LogRecord.prototype.exception_ = null;
+goog.debug.LogRecord.prototype.exceptionText_ = null;
+goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS = true;
+goog.debug.LogRecord.nextSequenceNumber_ = 0;
+goog.debug.LogRecord.prototype.reset = function(level, msg, loggerName, opt_time, opt_sequenceNumber) {
+ if(goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS) {
+ 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;
+ delete this.exception_;
+ delete this.exceptionText_
+};
+goog.debug.LogRecord.prototype.getLoggerName = function() {
+ return this.loggerName_
+};
+goog.debug.LogRecord.prototype.getException = function() {
+ return this.exception_
+};
+goog.debug.LogRecord.prototype.setException = function(exception) {
+ this.exception_ = exception
+};
+goog.debug.LogRecord.prototype.getExceptionText = function() {
+ return this.exceptionText_
+};
+goog.debug.LogRecord.prototype.setExceptionText = function(text) {
+ this.exceptionText_ = text
+};
+goog.debug.LogRecord.prototype.setLoggerName = function(loggerName) {
+ this.loggerName_ = loggerName
+};
+goog.debug.LogRecord.prototype.getLevel = function() {
+ return this.level_
+};
+goog.debug.LogRecord.prototype.setLevel = function(level) {
+ this.level_ = level
+};
+goog.debug.LogRecord.prototype.getMessage = function() {
+ return this.msg_
+};
+goog.debug.LogRecord.prototype.setMessage = function(msg) {
+ this.msg_ = msg
+};
+goog.debug.LogRecord.prototype.getMillis = function() {
+ return this.time_
+};
+goog.debug.LogRecord.prototype.setMillis = function(time) {
+ this.time_ = time
+};
+goog.debug.LogRecord.prototype.getSequenceNumber = function() {
+ return this.sequenceNumber_
+};
+goog.provide("goog.debug.LogBuffer");
+goog.require("goog.asserts");
+goog.require("goog.debug.LogRecord");
+goog.debug.LogBuffer = function() {
+ goog.asserts.assert(goog.debug.LogBuffer.isBufferingEnabled(), "Cannot use goog.debug.LogBuffer without defining " + "goog.debug.LogBuffer.CAPACITY.");
+ this.clear()
+};
+goog.debug.LogBuffer.getInstance = function() {
+ if(!goog.debug.LogBuffer.instance_) {
+ goog.debug.LogBuffer.instance_ = new goog.debug.LogBuffer
+ }
+ return goog.debug.LogBuffer.instance_
+};
+goog.debug.LogBuffer.CAPACITY = 0;
+goog.debug.LogBuffer.prototype.buffer_;
+goog.debug.LogBuffer.prototype.curIndex_;
+goog.debug.LogBuffer.prototype.isFull_;
+goog.debug.LogBuffer.prototype.addRecord = function(level, msg, loggerName) {
+ var curIndex = (this.curIndex_ + 1) % goog.debug.LogBuffer.CAPACITY;
+ this.curIndex_ = curIndex;
+ if(this.isFull_) {
+ var ret = this.buffer_[curIndex];
+ ret.reset(level, msg, loggerName);
+ return ret
+ }
+ this.isFull_ = curIndex == goog.debug.LogBuffer.CAPACITY - 1;
+ return this.buffer_[curIndex] = new goog.debug.LogRecord(level, msg, loggerName)
+};
+goog.debug.LogBuffer.isBufferingEnabled = function() {
+ return goog.debug.LogBuffer.CAPACITY > 0
+};
+goog.debug.LogBuffer.prototype.clear = function() {
+ this.buffer_ = new Array(goog.debug.LogBuffer.CAPACITY);
+ this.curIndex_ = -1;
+ this.isFull_ = false
+};
+goog.debug.LogBuffer.prototype.forEachRecord = function(func) {
+ var buffer = this.buffer_;
+ if(!buffer[0]) {
+ return
+ }
+ var curIndex = this.curIndex_;
+ var i = this.isFull_ ? curIndex : -1;
+ do {
+ i = (i + 1) % goog.debug.LogBuffer.CAPACITY;
+ func(buffer[i])
+ }while(i != curIndex)
+};
+goog.provide("goog.debug.LogManager");
+goog.provide("goog.debug.Logger");
+goog.provide("goog.debug.Logger.Level");
+goog.require("goog.array");
+goog.require("goog.asserts");
+goog.require("goog.debug");
+goog.require("goog.debug.LogBuffer");
+goog.require("goog.debug.LogRecord");
+goog.debug.Logger = function(name) {
+ this.name_ = name
+};
+goog.debug.Logger.prototype.parent_ = null;
+goog.debug.Logger.prototype.level_ = null;
+goog.debug.Logger.prototype.children_ = null;
+goog.debug.Logger.prototype.handlers_ = null;
+goog.debug.Logger.ENABLE_HIERARCHY = true;
+if(!goog.debug.Logger.ENABLE_HIERARCHY) {
+ goog.debug.Logger.rootHandlers_ = [];
+ goog.debug.Logger.rootLevel_
+}
+goog.debug.Logger.Level = function(name, value) {
+ this.name = name;
+ this.value = value
+};
+goog.debug.Logger.Level.prototype.toString = function() {
+ return this.name
+};
+goog.debug.Logger.Level.OFF = new goog.debug.Logger.Level("OFF", Infinity);
+goog.debug.Logger.Level.SHOUT = new goog.debug.Logger.Level("SHOUT", 1200);
+goog.debug.Logger.Level.SEVERE = new goog.debug.Logger.Level("SEVERE", 1E3);
+goog.debug.Logger.Level.WARNING = new goog.debug.Logger.Level("WARNING", 900);
+goog.debug.Logger.Level.INFO = new goog.debug.Logger.Level("INFO", 800);
+goog.debug.Logger.Level.CONFIG = new goog.debug.Logger.Level("CONFIG", 700);
+goog.debug.Logger.Level.FINE = new goog.debug.Logger.Level("FINE", 500);
+goog.debug.Logger.Level.FINER = new goog.debug.Logger.Level("FINER", 400);
+goog.debug.Logger.Level.FINEST = new goog.debug.Logger.Level("FINEST", 300);
+goog.debug.Logger.Level.ALL = new goog.debug.Logger.Level("ALL", 0);
+goog.debug.Logger.Level.PREDEFINED_LEVELS = [goog.debug.Logger.Level.OFF, goog.debug.Logger.Level.SHOUT, goog.debug.Logger.Level.SEVERE, goog.debug.Logger.Level.WARNING, goog.debug.Logger.Level.INFO, goog.debug.Logger.Level.CONFIG, goog.debug.Logger.Level.FINE, goog.debug.Logger.Level.FINER, goog.debug.Logger.Level.FINEST, goog.debug.Logger.Level.ALL];
+goog.debug.Logger.Level.predefinedLevelsCache_ = null;
+goog.debug.Logger.Level.createPredefinedLevelsCache_ = function() {
+ goog.debug.Logger.Level.predefinedLevelsCache_ = {};
+ for(var i = 0, level;level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];i++) {
+ goog.debug.Logger.Level.predefinedLevelsCache_[level.value] = level;
+ goog.debug.Logger.Level.predefinedLevelsCache_[level.name] = level
+ }
+};
+goog.debug.Logger.Level.getPredefinedLevel = function(name) {
+ if(!goog.debug.Logger.Level.predefinedLevelsCache_) {
+ goog.debug.Logger.Level.createPredefinedLevelsCache_()
+ }
+ return goog.debug.Logger.Level.predefinedLevelsCache_[name] || null
+};
+goog.debug.Logger.Level.getPredefinedLevelByValue = function(value) {
+ if(!goog.debug.Logger.Level.predefinedLevelsCache_) {
+ goog.debug.Logger.Level.createPredefinedLevelsCache_()
+ }
+ if(value in goog.debug.Logger.Level.predefinedLevelsCache_) {
+ return goog.debug.Logger.Level.predefinedLevelsCache_[value]
+ }
+ for(var i = 0;i < goog.debug.Logger.Level.PREDEFINED_LEVELS.length;++i) {
+ var level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];
+ if(level.value <= value) {
+ return level
+ }
+ }
+ return null
+};
+goog.debug.Logger.getLogger = function(name) {
+ return goog.debug.LogManager.getLogger(name)
+};
+goog.debug.Logger.prototype.getName = function() {
+ return this.name_
+};
+goog.debug.Logger.prototype.addHandler = function(handler) {
+ if(goog.debug.Logger.ENABLE_HIERARCHY) {
+ if(!this.handlers_) {
+ this.handlers_ = []
+ }
+ this.handlers_.push(handler)
+ }else {
+ goog.asserts.assert(!this.name_, "Cannot call addHandler on a non-root logger when " + "goog.debug.Logger.ENABLE_HIERARCHY is false.");
+ goog.debug.Logger.rootHandlers_.push(handler)
+ }
+};
+goog.debug.Logger.prototype.removeHandler = function(handler) {
+ var handlers = goog.debug.Logger.ENABLE_HIERARCHY ? this.handlers_ : goog.debug.Logger.rootHandlers_;
+ return!!handlers && goog.array.remove(handlers, handler)
+};
+goog.debug.Logger.prototype.getParent = function() {
+ return this.parent_
+};
+goog.debug.Logger.prototype.getChildren = function() {
+ if(!this.children_) {
+ this.children_ = {}
+ }
+ return this.children_
+};
+goog.debug.Logger.prototype.setLevel = function(level) {
+ if(goog.debug.Logger.ENABLE_HIERARCHY) {
+ this.level_ = level
+ }else {
+ goog.asserts.assert(!this.name_, "Cannot call setLevel() on a non-root logger when " + "goog.debug.Logger.ENABLE_HIERARCHY is false.");
+ goog.debug.Logger.rootLevel_ = level
+ }
+};
+goog.debug.Logger.prototype.getLevel = function() {
+ return this.level_
+};
+goog.debug.Logger.prototype.getEffectiveLevel = function() {
+ if(!goog.debug.Logger.ENABLE_HIERARCHY) {
+ return goog.debug.Logger.rootLevel_
+ }
+ if(this.level_) {
+ return this.level_
+ }
+ if(this.parent_) {
+ return this.parent_.getEffectiveLevel()
+ }
+ goog.asserts.fail("Root logger has no level set.");
+ return null
+};
+goog.debug.Logger.prototype.isLoggable = function(level) {
+ return level.value >= this.getEffectiveLevel().value
+};
+goog.debug.Logger.prototype.log = function(level, msg, opt_exception) {
+ if(this.isLoggable(level)) {
+ this.doLogRecord_(this.getLogRecord(level, msg, opt_exception))
+ }
+};
+goog.debug.Logger.prototype.getLogRecord = function(level, msg, opt_exception) {
+ if(goog.debug.LogBuffer.isBufferingEnabled()) {
+ var logRecord = goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_)
+ }else {
+ 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.shout = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.SHOUT, msg, opt_exception)
+};
+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.info = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.INFO, msg, opt_exception)
+};
+goog.debug.Logger.prototype.config = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.CONFIG, msg, opt_exception)
+};
+goog.debug.Logger.prototype.fine = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.FINE, msg, opt_exception)
+};
+goog.debug.Logger.prototype.finer = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.FINER, msg, opt_exception)
+};
+goog.debug.Logger.prototype.finest = function(msg, opt_exception) {
+ this.log(goog.debug.Logger.Level.FINEST, msg, opt_exception)
+};
+goog.debug.Logger.prototype.logRecord = function(logRecord) {
+ if(this.isLoggable(logRecord.getLevel())) {
+ this.doLogRecord_(logRecord)
+ }
+};
+goog.debug.Logger.prototype.doLogRecord_ = function(logRecord) {
+ if(goog.debug.Logger.ENABLE_HIERARCHY) {
+ var target = this;
+ while(target) {
+ target.callPublish_(logRecord);
+ target = target.getParent()
+ }
+ }else {
+ for(var i = 0, handler;handler = goog.debug.Logger.rootHandlers_[i++];) {
+ handler(logRecord)
+ }
+ }
+};
+goog.debug.Logger.prototype.callPublish_ = function(logRecord) {
+ if(this.handlers_) {
+ for(var i = 0, handler;handler = this.handlers_[i];i++) {
+ handler(logRecord)
+ }
+ }
+};
+goog.debug.Logger.prototype.setParent_ = function(parent) {
+ this.parent_ = parent
+};
+goog.debug.Logger.prototype.addChild_ = function(name, logger) {
+ this.getChildren()[name] = logger
+};
+goog.debug.LogManager = {};
+goog.debug.LogManager.loggers_ = {};
+goog.debug.LogManager.rootLogger_ = null;
+goog.debug.LogManager.initialize = function() {
+ 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)
+ }
+};
+goog.debug.LogManager.getLoggers = function() {
+ return goog.debug.LogManager.loggers_
+};
+goog.debug.LogManager.getRoot = function() {
+ goog.debug.LogManager.initialize();
+ return goog.debug.LogManager.rootLogger_
+};
+goog.debug.LogManager.getLogger = function(name) {
+ goog.debug.LogManager.initialize();
+ var ret = goog.debug.LogManager.loggers_[name];
+ return ret || goog.debug.LogManager.createLogger_(name)
+};
+goog.debug.LogManager.createFunctionForCatchErrors = function(opt_logger) {
+ return function(info) {
+ var logger = opt_logger || goog.debug.LogManager.getRoot();
+ logger.severe("Error: " + info.message + " (" + info.fileName + " @ Line: " + info.line + ")")
+ }
+};
+goog.debug.LogManager.createLogger_ = function(name) {
+ var logger = new goog.debug.Logger(name);
+ if(goog.debug.Logger.ENABLE_HIERARCHY) {
+ var lastDotIndex = name.lastIndexOf(".");
+ var parentName = name.substr(0, lastDotIndex);
+ var leafName = name.substr(lastDotIndex + 1);
+ var parentLogger = goog.debug.LogManager.getLogger(parentName);
+ parentLogger.addChild_(leafName, logger);
+ logger.setParent_(parentLogger)
+ }
+ goog.debug.LogManager.loggers_[name] = logger;
+ return logger
+};
+goog.provide("goog.dom.SavedRange");
+goog.require("goog.Disposable");
+goog.require("goog.debug.Logger");
+goog.dom.SavedRange = function() {
+ goog.Disposable.call(this)
+};
+goog.inherits(goog.dom.SavedRange, goog.Disposable);
+goog.dom.SavedRange.logger_ = goog.debug.Logger.getLogger("goog.dom.SavedRange");
+goog.dom.SavedRange.prototype.restore = function(opt_stayAlive) {
+ if(this.isDisposed()) {
+ goog.dom.SavedRange.logger_.severe("Disposed SavedRange objects cannot be restored.")
+ }
+ var range = this.restoreInternal();
+ if(!opt_stayAlive) {
+ this.dispose()
+ }
+ return range
+};
+goog.dom.SavedRange.prototype.restoreInternal = goog.abstractMethod;
+goog.provide("goog.dom.SavedCaretRange");
+goog.require("goog.array");
+goog.require("goog.dom");
+goog.require("goog.dom.SavedRange");
+goog.require("goog.dom.TagName");
+goog.require("goog.string");
+goog.dom.SavedCaretRange = function(range) {
+ goog.dom.SavedRange.call(this);
+ this.startCaretId_ = goog.string.createUniqueString();
+ this.endCaretId_ = goog.string.createUniqueString();
+ this.dom_ = goog.dom.getDomHelper(range.getDocument());
+ range.surroundWithNodes(this.createCaret_(true), this.createCaret_(false))
+};
+goog.inherits(goog.dom.SavedCaretRange, goog.dom.SavedRange);
+goog.dom.SavedCaretRange.prototype.toAbstractRange = function() {
+ var range = null;
+ var startCaret = this.getCaret(true);
+ var endCaret = this.getCaret(false);
+ if(startCaret && endCaret) {
+ range = goog.dom.Range.createFromNodes(startCaret, 0, endCaret, 0)
+ }
+ return range
+};
+goog.dom.SavedCaretRange.prototype.getCaret = function(start) {
+ return this.dom_.getElement(start ? this.startCaretId_ : this.endCaretId_)
+};
+goog.dom.SavedCaretRange.prototype.removeCarets = function(opt_range) {
+ goog.dom.removeNode(this.getCaret(true));
+ goog.dom.removeNode(this.getCaret(false));
+ return opt_range
+};
+goog.dom.SavedCaretRange.prototype.setRestorationDocument = function(doc) {
+ this.dom_.setDocument(doc)
+};
+goog.dom.SavedCaretRange.prototype.restoreInternal = function() {
+ var range = null;
+ var startCaret = this.getCaret(true);
+ var endCaret = this.getCaret(false);
+ if(startCaret && endCaret) {
+ var startNode = startCaret.parentNode;
+ var startOffset = goog.array.indexOf(startNode.childNodes, startCaret);
+ var endNode = endCaret.parentNode;
+ var endOffset = goog.array.indexOf(endNode.childNodes, endCaret);
+ if(endNode == startNode) {
+ endOffset -= 1
+ }
+ range = goog.dom.Range.createFromNodes(startNode, startOffset, endNode, endOffset);
+ range = this.removeCarets(range);
+ range.select()
+ }else {
+ this.removeCarets()
+ }
+ return range
+};
+goog.dom.SavedCaretRange.prototype.disposeInternal = function() {
+ this.removeCarets();
+ this.dom_ = null
+};
+goog.dom.SavedCaretRange.prototype.createCaret_ = function(start) {
+ return this.dom_.createDom(goog.dom.TagName.SPAN, {id:start ? this.startCaretId_ : this.endCaretId_})
+};
+goog.dom.SavedCaretRange.CARET_REGEX = /<span\s+id="?goog_\d+"?><\/span>/ig;
+goog.dom.SavedCaretRange.htmlEqual = function(str1, str2) {
+ return str1 == str2 || str1.replace(goog.dom.SavedCaretRange.CARET_REGEX, "") == str2.replace(goog.dom.SavedCaretRange.CARET_REGEX, "")
+};
+goog.provide("goog.dom.TagIterator");
+goog.provide("goog.dom.TagWalkType");
+goog.require("goog.dom.NodeType");
+goog.require("goog.iter.Iterator");
+goog.require("goog.iter.StopIteration");
+goog.dom.TagWalkType = {START_TAG:1, OTHER:0, END_TAG:-1};
+goog.dom.TagIterator = function(opt_node, opt_reversed, opt_unconstrained, opt_tagType, opt_depth) {
+ this.reversed = !!opt_reversed;
+ if(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 = goog.dom.TagWalkType.OTHER;
+goog.dom.TagIterator.prototype.depth;
+goog.dom.TagIterator.prototype.reversed;
+goog.dom.TagIterator.prototype.constrained;
+goog.dom.TagIterator.prototype.started_ = false;
+goog.dom.TagIterator.prototype.setPosition = function(node, opt_tagType, opt_depth) {
+ this.node = node;
+ if(node) {
+ if(goog.isNumber(opt_tagType)) {
+ this.tagType = opt_tagType
+ }else {
+ this.tagType = this.node.nodeType != goog.dom.NodeType.ELEMENT ? goog.dom.TagWalkType.OTHER : this.reversed ? goog.dom.TagWalkType.END_TAG : goog.dom.TagWalkType.START_TAG
+ }
+ }
+ if(goog.isNumber(opt_depth)) {
+ this.depth = opt_depth
+ }
+};
+goog.dom.TagIterator.prototype.copyFrom = function(other) {
+ this.node = other.node;
+ this.tagType = other.tagType;
+ this.depth = other.depth;
+ this.reversed = other.reversed;
+ this.constrained = other.constrained
+};
+goog.dom.TagIterator.prototype.clone = function() {
+ return new goog.dom.TagIterator(this.node, this.reversed, !this.constrained, this.tagType, this.depth)
+};
+goog.dom.TagIterator.prototype.skipTag = function() {
+ var check = this.reversed ? goog.dom.TagWalkType.END_TAG : goog.dom.TagWalkType.START_TAG;
+ if(this.tagType == check) {
+ this.tagType = check * -1;
+ this.depth += this.tagType * (this.reversed ? -1 : 1)
+ }
+};
+goog.dom.TagIterator.prototype.restartTag = function() {
+ var check = this.reversed ? goog.dom.TagWalkType.START_TAG : goog.dom.TagWalkType.END_TAG;
+ if(this.tagType == check) {
+ this.tagType = check * -1;
+ this.depth += this.tagType * (this.reversed ? -1 : 1)
+ }
+};
+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 ? goog.dom.TagWalkType.END_TAG : goog.dom.TagWalkType.START_TAG;
+ if(this.tagType == startType) {
+ var child = this.reversed ? node.lastChild : node.firstChild;
+ if(child) {
+ this.setPosition(child)
+ }else {
+ this.setPosition(node, startType * -1)
+ }
+ }else {
+ var sibling = this.reversed ? node.previousSibling : node.nextSibling;
+ if(sibling) {
+ this.setPosition(sibling)
+ }else {
+ 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.isStarted = function() {
+ return this.started_
+};
+goog.dom.TagIterator.prototype.isStartTag = function() {
+ return this.tagType == goog.dom.TagWalkType.START_TAG
+};
+goog.dom.TagIterator.prototype.isEndTag = function() {
+ return this.tagType == goog.dom.TagWalkType.END_TAG
+};
+goog.dom.TagIterator.prototype.isNonElement = function() {
+ return this.tagType == goog.dom.TagWalkType.OTHER
+};
+goog.dom.TagIterator.prototype.equals = function(other) {
+ return other.node == this.node && (!this.node || other.tagType == this.tagType)
+};
+goog.dom.TagIterator.prototype.splice = function(var_args) {
+ var node = this.node;
+ this.restartTag();
+ this.reversed = !this.reversed;
+ goog.dom.TagIterator.prototype.next.call(this);
+ this.reversed = !this.reversed;
+ var arr = goog.isArrayLike(arguments[0]) ? arguments[0] : arguments;
+ for(var i = arr.length - 1;i >= 0;i--) {
+ goog.dom.insertSiblingAfter(arr[i], node)
+ }
+ goog.dom.removeNode(node)
+};
+goog.provide("goog.dom.AbstractRange");
+goog.provide("goog.dom.RangeIterator");
+goog.provide("goog.dom.RangeType");
+goog.require("goog.dom");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.SavedCaretRange");
+goog.require("goog.dom.TagIterator");
+goog.require("goog.userAgent");
+goog.dom.RangeType = {TEXT:"text", CONTROL:"control", MULTI:"mutli"};
+goog.dom.AbstractRange = function() {
+};
+goog.dom.AbstractRange.getBrowserSelectionForWindow = function(win) {
+ if(win.getSelection) {
+ return win.getSelection()
+ }else {
+ var doc = win.document;
+ var sel = doc.selection;
+ if(sel) {
+ try {
+ var range = sel.createRange();
+ if(range.parentElement) {
+ if(range.parentElement().document != doc) {
+ return null
+ }
+ }else {
+ if(!range.length || range.item(0).document != doc) {
+ return null
+ }
+ }
+ }catch(e) {
+ return null
+ }
+ return sel
+ }
+ return null
+ }
+};
+goog.dom.AbstractRange.isNativeControlRange = function(range) {
+ return!!range && !!range.addElement
+};
+goog.dom.AbstractRange.prototype.clone = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getType = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getBrowserRangeObject = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.setBrowserRangeObject = function(nativeRange) {
+ return false
+};
+goog.dom.AbstractRange.prototype.getTextRangeCount = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getTextRange = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getTextRanges = function() {
+ var output = [];
+ for(var i = 0, len = this.getTextRangeCount();i < len;i++) {
+ output.push(this.getTextRange(i))
+ }
+ return output
+};
+goog.dom.AbstractRange.prototype.getContainer = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getContainerElement = function() {
+ var node = this.getContainer();
+ return node.nodeType == goog.dom.NodeType.ELEMENT ? node : node.parentNode
+};
+goog.dom.AbstractRange.prototype.getStartNode = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getStartOffset = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getEndNode = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getEndOffset = goog.abstractMethod;
+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.containsRange = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.containsNode = function(node, opt_allowPartial) {
+ return this.containsRange(goog.dom.Range.createFromNodeContents(node), opt_allowPartial)
+};
+goog.dom.AbstractRange.prototype.isRangeInDocument = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.isCollapsed = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getText = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getHtmlFragment = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getValidHtml = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.getPastableHtml = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.__iterator__ = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.select = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.removeContents = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.insertNode = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.replaceContentsWithNode = function(node) {
+ if(!this.isCollapsed()) {
+ this.removeContents()
+ }
+ return this.insertNode(node, true)
+};
+goog.dom.AbstractRange.prototype.surroundWithNodes = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.saveUsingDom = goog.abstractMethod;
+goog.dom.AbstractRange.prototype.saveUsingCarets = function() {
+ return this.getStartNode() && this.getEndNode() ? new goog.dom.SavedCaretRange(this) : null
+};
+goog.dom.AbstractRange.prototype.collapse = goog.abstractMethod;
+goog.dom.RangeIterator = function(node, opt_reverse) {
+ goog.dom.TagIterator.call(this, node, opt_reverse, true)
+};
+goog.inherits(goog.dom.RangeIterator, goog.dom.TagIterator);
+goog.dom.RangeIterator.prototype.getStartTextOffset = goog.abstractMethod;
+goog.dom.RangeIterator.prototype.getEndTextOffset = goog.abstractMethod;
+goog.dom.RangeIterator.prototype.getStartNode = goog.abstractMethod;
+goog.dom.RangeIterator.prototype.getEndNode = goog.abstractMethod;
+goog.dom.RangeIterator.prototype.isLast = goog.abstractMethod;
+goog.provide("goog.dom.AbstractMultiRange");
+goog.require("goog.array");
+goog.require("goog.dom");
+goog.require("goog.dom.AbstractRange");
+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();
+ var otherRanges = otherRange.getTextRanges();
+ var 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)
+ })
+ })
+};
+goog.dom.AbstractMultiRange.prototype.insertNode = function(node, before) {
+ if(before) {
+ goog.dom.insertSiblingBefore(node, this.getStartNode())
+ }else {
+ goog.dom.insertSiblingAfter(node, this.getEndNode())
+ }
+ return node
+};
+goog.dom.AbstractMultiRange.prototype.surroundWithNodes = function(startNode, endNode) {
+ this.insertNode(startNode, true);
+ this.insertNode(endNode, false)
+};
+goog.provide("goog.dom.TextRangeIterator");
+goog.require("goog.array");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.RangeIterator");
+goog.require("goog.dom.TagName");
+goog.require("goog.iter.StopIteration");
+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 == goog.dom.NodeType.ELEMENT && startNode.tagName != goog.dom.TagName.BR) {
+ var startChildren = startNode.childNodes;
+ var 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 == goog.dom.NodeType.ELEMENT) {
+ this.endNode_ = endNode.childNodes[endOffset];
+ if(this.endNode_) {
+ 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.getStartTextOffset = function() {
+ return this.node.nodeType != goog.dom.NodeType.TEXT ? -1 : this.node == this.startNode_ ? this.startOffset_ : 0
+};
+goog.dom.TextRangeIterator.prototype.getEndTextOffset = function() {
+ return this.node.nodeType != goog.dom.NodeType.TEXT ? -1 : this.node == this.endNode_ ? this.endOffset_ : this.node.nodeValue.length
+};
+goog.dom.TextRangeIterator.prototype.getStartNode = function() {
+ return this.startNode_
+};
+goog.dom.TextRangeIterator.prototype.setStartNode = function(node) {
+ if(!this.isStarted()) {
+ this.setPosition(node)
+ }
+ this.startNode_ = node;
+ this.startOffset_ = 0
+};
+goog.dom.TextRangeIterator.prototype.getEndNode = function() {
+ return this.endNode_
+};
+goog.dom.TextRangeIterator.prototype.setEndNode = function(node) {
+ this.endNode_ = node;
+ this.endOffset_ = 0
+};
+goog.dom.TextRangeIterator.prototype.isLast = function() {
+ return this.isStarted() && 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)
+};
+goog.dom.TextRangeIterator.prototype.skipTag = function() {
+ goog.dom.TextRangeIterator.superClass_.skipTag.apply(this);
+ if(goog.dom.contains(this.node, this.endNode_)) {
+ throw goog.iter.StopIteration;
+ }
+};
+goog.dom.TextRangeIterator.prototype.copyFrom = function(other) {
+ this.startNode_ = other.startNode_;
+ this.endNode_ = other.endNode_;
+ this.startOffset_ = other.startOffset_;
+ this.endOffset_ = other.endOffset_;
+ this.isReversed_ = other.isReversed_;
+ goog.dom.TextRangeIterator.superClass_.copyFrom.call(this, other)
+};
+goog.dom.TextRangeIterator.prototype.clone = function() {
+ var copy = new goog.dom.TextRangeIterator(this.startNode_, this.startOffset_, this.endNode_, this.endOffset_, this.isReversed_);
+ copy.copyFrom(this);
+ return copy
+};
+goog.provide("goog.dom.RangeEndpoint");
+goog.dom.RangeEndpoint = {START:1, END:0};
+goog.provide("goog.userAgent.jscript");
+goog.require("goog.string");
+goog.userAgent.jscript.ASSUME_NO_JSCRIPT = false;
+goog.userAgent.jscript.init_ = function() {
+ var hasScriptEngine = "ScriptEngine" in goog.global;
+ goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_ = hasScriptEngine && goog.global["ScriptEngine"]() == "JScript";
+ goog.userAgent.jscript.DETECTED_VERSION_ = goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_ ? goog.global["ScriptEngineMajorVersion"]() + "." + goog.global["ScriptEngineMinorVersion"]() + "." + goog.global["ScriptEngineBuildVersion"]() : "0"
+};
+if(!goog.userAgent.jscript.ASSUME_NO_JSCRIPT) {
+ goog.userAgent.jscript.init_()
+}
+goog.userAgent.jscript.HAS_JSCRIPT = goog.userAgent.jscript.ASSUME_NO_JSCRIPT ? false : goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_;
+goog.userAgent.jscript.VERSION = goog.userAgent.jscript.ASSUME_NO_JSCRIPT ? "0" : goog.userAgent.jscript.DETECTED_VERSION_;
+goog.userAgent.jscript.isVersion = function(version) {
+ return goog.string.compareVersions(goog.userAgent.jscript.VERSION, version) >= 0
+};
+goog.provide("goog.string.StringBuffer");
+goog.require("goog.userAgent.jscript");
+goog.string.StringBuffer = function(opt_a1, var_args) {
+ this.buffer_ = goog.userAgent.jscript.HAS_JSCRIPT ? [] : "";
+ if(opt_a1 != null) {
+ this.append.apply(this, arguments)
+ }
+};
+goog.string.StringBuffer.prototype.set = function(s) {
+ this.clear();
+ this.append(s)
+};
+if(goog.userAgent.jscript.HAS_JSCRIPT) {
+ goog.string.StringBuffer.prototype.bufferLength_ = 0;
+ goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
+ if(opt_a2 == null) {
+ this.buffer_[this.bufferLength_++] = a1
+ }else {
+ this.buffer_.push.apply(this.buffer_, arguments);
+ this.bufferLength_ = this.buffer_.length
+ }
+ return this
+ }
+}else {
+ goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
+ this.buffer_ += a1;
+ if(opt_a2 != null) {
+ for(var i = 1;i < arguments.length;i++) {
+ this.buffer_ += arguments[i]
+ }
+ }
+ return this
+ }
+}
+goog.string.StringBuffer.prototype.clear = function() {
+ if(goog.userAgent.jscript.HAS_JSCRIPT) {
+ this.buffer_.length = 0;
+ this.bufferLength_ = 0
+ }else {
+ this.buffer_ = ""
+ }
+};
+goog.string.StringBuffer.prototype.getLength = function() {
+ return this.toString().length
+};
+goog.string.StringBuffer.prototype.toString = function() {
+ if(goog.userAgent.jscript.HAS_JSCRIPT) {
+ var str = this.buffer_.join("");
+ this.clear();
+ if(str) {
+ this.append(str)
+ }
+ return str
+ }else {
+ return this.buffer_
+ }
+};
+goog.provide("goog.dom.browserrange.AbstractRange");
+goog.require("goog.dom");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.RangeEndpoint");
+goog.require("goog.dom.TagName");
+goog.require("goog.dom.TextRangeIterator");
+goog.require("goog.iter");
+goog.require("goog.string");
+goog.require("goog.string.StringBuffer");
+goog.require("goog.userAgent");
+goog.dom.browserrange.AbstractRange = function() {
+};
+goog.dom.browserrange.AbstractRange.prototype.clone = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getBrowserRange = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getContainer = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getStartNode = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getStartOffset = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getEndNode = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getEndOffset = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.compareBrowserRangeEndpoints = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.containsRange = function(abstractRange, opt_allowPartial) {
+ var checkPartial = opt_allowPartial && !abstractRange.isCollapsed();
+ var range = abstractRange.getBrowserRange();
+ var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END;
+ try {
+ if(checkPartial) {
+ return this.compareBrowserRangeEndpoints(range, end, start) >= 0 && this.compareBrowserRangeEndpoints(range, start, end) <= 0
+ }else {
+ return this.compareBrowserRangeEndpoints(range, end, end) >= 0 && this.compareBrowserRangeEndpoints(range, start, start) <= 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.dom.browserrange.createRangeFromNodeContents(node), opt_allowPartial)
+};
+goog.dom.browserrange.AbstractRange.prototype.isCollapsed = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getText = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.getHtmlFragment = function() {
+ var output = new goog.string.StringBuffer;
+ goog.iter.forEach(this, function(node, ignore, it) {
+ if(node.nodeType == goog.dom.NodeType.TEXT) {
+ output.append(goog.string.htmlEscape(node.nodeValue.substring(it.getStartTextOffset(), it.getEndTextOffset())))
+ }else {
+ if(node.nodeType == goog.dom.NodeType.ELEMENT) {
+ if(it.isEndTag()) {
+ if(goog.dom.canHaveChildren(node)) {
+ output.append("</" + node.tagName + ">")
+ }
+ }else {
+ var shallow = node.cloneNode(false);
+ var html = goog.dom.getOuterHtml(shallow);
+ if(goog.userAgent.IE && node.tagName == goog.dom.TagName.LI) {
+ output.append(html)
+ }else {
+ var index = html.lastIndexOf("<");
+ output.append(index ? html.substr(0, index) : html)
+ }
+ }
+ }
+ }
+ }, this);
+ return output.toString()
+};
+goog.dom.browserrange.AbstractRange.prototype.getValidHtml = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.__iterator__ = function(opt_keys) {
+ return new goog.dom.TextRangeIterator(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset())
+};
+goog.dom.browserrange.AbstractRange.prototype.select = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.removeContents = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.surroundContents = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.insertNode = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.surroundWithNodes = goog.abstractMethod;
+goog.dom.browserrange.AbstractRange.prototype.collapse = goog.abstractMethod;
+goog.provide("goog.dom.browserrange.W3cRange");
+goog.require("goog.dom");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.RangeEndpoint");
+goog.require("goog.dom.browserrange.AbstractRange");
+goog.require("goog.string");
+goog.dom.browserrange.W3cRange = function(range) {
+ this.range_ = range
+};
+goog.inherits(goog.dom.browserrange.W3cRange, goog.dom.browserrange.AbstractRange);
+goog.dom.browserrange.W3cRange.getBrowserRangeForNode = function(node) {
+ var nodeRange = goog.dom.getOwnerDocument(node).createRange();
+ if(node.nodeType == goog.dom.NodeType.TEXT) {
+ nodeRange.setStart(node, 0);
+ nodeRange.setEnd(node, node.length)
+ }else {
+ if(!goog.dom.browserrange.canContainRangeEndpoint(node)) {
+ var rangeParent = node.parentNode;
+ var rangeStartOffset = goog.array.indexOf(rangeParent.childNodes, node);
+ nodeRange.setStart(rangeParent, rangeStartOffset);
+ nodeRange.setEnd(rangeParent, rangeStartOffset + 1)
+ }else {
+ var tempNode, leaf = node;
+ while((tempNode = leaf.firstChild) && goog.dom.browserrange.canContainRangeEndpoint(tempNode)) {
+ leaf = tempNode
+ }
+ nodeRange.setStart(leaf, 0);
+ leaf = node;
+ while((tempNode = leaf.lastChild) && goog.dom.browserrange.canContainRangeEndpoint(tempNode)) {
+ leaf = tempNode
+ }
+ nodeRange.setEnd(leaf, leaf.nodeType == goog.dom.NodeType.ELEMENT ? 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.createFromNodeContents = function(node) {
+ return new goog.dom.browserrange.W3cRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node))
+};
+goog.dom.browserrange.W3cRange.createFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ return new goog.dom.browserrange.W3cRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, startOffset, endNode, endOffset))
+};
+goog.dom.browserrange.W3cRange.prototype.clone = function() {
+ return new this.constructor(this.range_.cloneRange())
+};
+goog.dom.browserrange.W3cRange.prototype.getBrowserRange = function() {
+ return this.range_
+};
+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 == goog.dom.RangeEndpoint.START ? thisEndpoint == goog.dom.RangeEndpoint.START ? goog.global["Range"].START_TO_START : goog.global["Range"].START_TO_END : thisEndpoint == goog.dom.RangeEndpoint.START ? 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.getText = function() {
+ return this.range_.toString()
+};
+goog.dom.browserrange.W3cRange.prototype.getValidHtml = function() {
+ var div = goog.dom.getDomHelper(this.range_.startContainer).createDom("div");
+ div.appendChild(this.range_.cloneContents());
+ var result = div.innerHTML;
+ if(goog.string.startsWith(result, "<") || !this.isCollapsed() && !goog.string.contains(result, "<")) {
+ return result
+ }
+ var container = this.getContainer();
+ container = container.nodeType == goog.dom.NodeType.ELEMENT ? container : container.parentNode;
+ var html = goog.dom.getOuterHtml(container.cloneNode(false));
+ return html.replace(">", ">" + result)
+};
+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, reverse) {
+ selection.removeAllRanges();
+ selection.addRange(this.range_)
+};
+goog.dom.browserrange.W3cRange.prototype.removeContents = function() {
+ var range = this.range_;
+ range.extractContents();
+ if(range.startContainer.hasChildNodes()) {
+ var rangeStartContainer = range.startContainer.childNodes[range.startOffset];
+ if(rangeStartContainer) {
+ var rangePrevious = rangeStartContainer.previousSibling;
+ if(goog.dom.getRawTextContent(rangeStartContainer) == "") {
+ goog.dom.removeNode(rangeStartContainer)
+ }
+ if(rangePrevious && goog.dom.getRawTextContent(rangePrevious) == "") {
+ goog.dom.removeNode(rangePrevious)
+ }
+ }
+ }
+};
+goog.dom.browserrange.W3cRange.prototype.surroundContents = function(element) {
+ this.range_.surroundContents(element);
+ return element
+};
+goog.dom.browserrange.W3cRange.prototype.insertNode = function(node, before) {
+ var range = this.range_.cloneRange();
+ range.collapse(before);
+ range.insertNode(node);
+ range.detach();
+ return node
+};
+goog.dom.browserrange.W3cRange.prototype.surroundWithNodes = function(startNode, endNode) {
+ var win = goog.dom.getWindow(goog.dom.getOwnerDocument(this.getStartNode()));
+ var selectionRange = goog.dom.Range.createFromWindow(win);
+ if(selectionRange) {
+ var sNode = selectionRange.getStartNode();
+ var eNode = selectionRange.getEndNode();
+ var sOffset = selectionRange.getStartOffset();
+ var eOffset = selectionRange.getEndOffset()
+ }
+ var clone1 = this.range_.cloneRange();
+ var clone2 = this.range_.cloneRange();
+ clone1.collapse(false);
+ clone2.collapse(true);
+ clone1.insertNode(endNode);
+ clone2.insertNode(startNode);
+ clone1.detach();
+ clone2.detach();
+ if(selectionRange) {
+ var isInsertedNode = function(n) {
+ return n == startNode || n == endNode
+ };
+ if(sNode.nodeType == goog.dom.NodeType.TEXT) {
+ while(sOffset > sNode.length) {
+ sOffset -= sNode.length;
+ do {
+ sNode = sNode.nextSibling
+ }while(isInsertedNode(sNode))
+ }
+ }
+ if(eNode.nodeType == goog.dom.NodeType.TEXT) {
+ while(eOffset > eNode.length) {
+ eOffset -= eNode.length;
+ do {
+ eNode = eNode.nextSibling
+ }while(isInsertedNode(eNode))
+ }
+ }
+ goog.dom.Range.createFromNodes(sNode, sOffset, eNode, eOffset).select()
+ }
+};
+goog.dom.browserrange.W3cRange.prototype.collapse = function(toStart) {
+ this.range_.collapse(toStart)
+};
+goog.provide("goog.dom.browserrange.GeckoRange");
+goog.require("goog.dom.browserrange.W3cRange");
+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.createFromNodeContents = function(node) {
+ return new goog.dom.browserrange.GeckoRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node))
+};
+goog.dom.browserrange.GeckoRange.createFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ return new goog.dom.browserrange.GeckoRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, startOffset, endNode, endOffset))
+};
+goog.dom.browserrange.GeckoRange.prototype.selectInternal = function(selection, reversed) {
+ var anchorNode = reversed ? this.getEndNode() : this.getStartNode();
+ var anchorOffset = reversed ? this.getEndOffset() : this.getStartOffset();
+ var focusNode = reversed ? this.getStartNode() : this.getEndNode();
+ var focusOffset = reversed ? this.getStartOffset() : this.getEndOffset();
+ selection.collapse(anchorNode, anchorOffset);
+ if(anchorNode != focusNode || anchorOffset != focusOffset) {
+ selection.extend(focusNode, focusOffset)
+ }
+};
+goog.provide("goog.dom.NodeIterator");
+goog.require("goog.dom.TagIterator");
+goog.dom.NodeIterator = function(opt_node, opt_reversed, opt_unconstrained, opt_depth) {
+ goog.dom.TagIterator.call(this, opt_node, opt_reversed, opt_unconstrained, null, opt_depth)
+};
+goog.inherits(goog.dom.NodeIterator, goog.dom.TagIterator);
+goog.dom.NodeIterator.prototype.next = function() {
+ do {
+ goog.dom.NodeIterator.superClass_.next.call(this)
+ }while(this.isEndTag());
+ return this.node
+};
+goog.provide("goog.dom.browserrange.IeRange");
+goog.require("goog.array");
+goog.require("goog.debug.Logger");
+goog.require("goog.dom");
+goog.require("goog.dom.NodeIterator");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.RangeEndpoint");
+goog.require("goog.dom.TagName");
+goog.require("goog.dom.browserrange.AbstractRange");
+goog.require("goog.iter");
+goog.require("goog.iter.StopIteration");
+goog.require("goog.string");
+goog.dom.browserrange.IeRange = function(range, doc) {
+ this.range_ = range;
+ this.doc_ = doc
+};
+goog.inherits(goog.dom.browserrange.IeRange, goog.dom.browserrange.AbstractRange);
+goog.dom.browserrange.IeRange.logger_ = goog.debug.Logger.getLogger("goog.dom.browserrange.IeRange");
+goog.dom.browserrange.IeRange.getBrowserRangeForNode_ = function(node) {
+ var nodeRange = goog.dom.getOwnerDocument(node).body.createTextRange();
+ if(node.nodeType == goog.dom.NodeType.ELEMENT) {
+ nodeRange.moveToElementText(node);
+ if(goog.dom.browserrange.canContainRangeEndpoint(node) && !node.childNodes.length) {
+ nodeRange.collapse(false)
+ }
+ }else {
+ var offset = 0;
+ var sibling = node;
+ while(sibling = sibling.previousSibling) {
+ var nodeType = sibling.nodeType;
+ if(nodeType == goog.dom.NodeType.TEXT) {
+ offset += sibling.length
+ }else {
+ if(nodeType == goog.dom.NodeType.ELEMENT) {
+ nodeRange.moveToElementText(sibling);
+ break
+ }
+ }
+ }
+ if(!sibling) {
+ nodeRange.moveToElementText(node.parentNode)
+ }
+ nodeRange.collapse(!sibling);
+ if(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 == goog.dom.NodeType.ELEMENT) {
+ if(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.lastChild || startNode;
+ startOffset = 0
+ }
+ var leftRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_(startNode);
+ if(startOffset) {
+ leftRange.move("character", startOffset)
+ }
+ if(startNode == endNode && startOffset == endOffset) {
+ leftRange.collapse(true);
+ return leftRange
+ }
+ if(collapse) {
+ leftRange.collapse(false)
+ }
+ collapse = false;
+ if(endNode.nodeType == goog.dom.NodeType.ELEMENT) {
+ if(endOffset > endNode.childNodes.length) {
+ goog.dom.browserrange.IeRange.logger_.severe("Cannot have endOffset > endNode child count")
+ }
+ child = endNode.childNodes[endOffset];
+ endNode = child || endNode.lastChild || endNode;
+ endOffset = 0;
+ collapse = !child
+ }
+ var rightRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_(endNode);
+ rightRange.collapse(!collapse);
+ if(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));
+ if(!goog.dom.browserrange.canContainRangeEndpoint(node)) {
+ range.startNode_ = range.endNode_ = range.parentNode_ = node.parentNode;
+ range.startOffset_ = goog.array.indexOf(range.parentNode_.childNodes, node);
+ range.endOffset_ = range.startOffset_ + 1
+ }else {
+ var tempNode, leaf = node;
+ while((tempNode = leaf.firstChild) && goog.dom.browserrange.canContainRangeEndpoint(tempNode)) {
+ leaf = tempNode
+ }
+ range.startNode_ = leaf;
+ range.startOffset_ = 0;
+ leaf = node;
+ while((tempNode = leaf.lastChild) && goog.dom.browserrange.canContainRangeEndpoint(tempNode)) {
+ leaf = tempNode
+ }
+ range.endNode_ = leaf;
+ range.endOffset_ = leaf.nodeType == goog.dom.NodeType.ELEMENT ? leaf.childNodes.length : leaf.length;
+ range.parentNode_ = node
+ }
+ return range
+};
+goog.dom.browserrange.IeRange.createFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ var range = new goog.dom.browserrange.IeRange(goog.dom.browserrange.IeRange.getBrowserRangeForNodes_(startNode, startOffset, endNode, endOffset), goog.dom.getOwnerDocument(startNode));
+ range.startNode_ = startNode;
+ range.startOffset_ = startOffset;
+ range.endNode_ = endNode;
+ range.endOffset_ = endOffset;
+ 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.startOffset_ = -1;
+goog.dom.browserrange.IeRange.prototype.endOffset_ = -1;
+goog.dom.browserrange.IeRange.prototype.clone = function() {
+ var range = new goog.dom.browserrange.IeRange(this.range_.duplicate(), this.doc_);
+ range.parentNode_ = this.parentNode_;
+ range.startNode_ = this.startNode_;
+ range.endNode_ = this.endNode_;
+ return range
+};
+goog.dom.browserrange.IeRange.prototype.getBrowserRange = function() {
+ return this.range_
+};
+goog.dom.browserrange.IeRange.prototype.clearCachedValues_ = function() {
+ this.parentNode_ = this.startNode_ = this.endNode_ = null;
+ this.startOffset_ = this.endOffset_ = -1
+};
+goog.dom.browserrange.IeRange.prototype.getContainer = function() {
+ if(!this.parentNode_) {
+ var selectText = this.range_.text;
+ var range = this.range_.duplicate();
+ var rightTrimmedSelectText = selectText.replace(/ +$/, "");
+ var numSpacesAtEnd = selectText.length - rightTrimmedSelectText.length;
+ if(numSpacesAtEnd) {
+ range.moveEnd("character", -numSpacesAtEnd)
+ }
+ var parent = range.parentElement();
+ var htmlText = range.htmlText;
+ var htmlTextLen = goog.string.stripNewlines(htmlText).length;
+ if(this.isCollapsed() && htmlTextLen > 0) {
+ return this.parentNode_ = parent
+ }
+ while(htmlTextLen > goog.string.stripNewlines(parent.outerHTML).length) {
+ parent = parent.parentNode
+ }
+ while(parent.childNodes.length == 1 && parent.innerText == goog.dom.browserrange.IeRange.getNodeText_(parent.firstChild)) {
+ if(!goog.dom.browserrange.canContainRangeEndpoint(parent.firstChild)) {
+ 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) {
+ var childNodes = node.childNodes;
+ for(var i = 0, len = childNodes.length;i < len;i++) {
+ var child = childNodes[i];
+ if(goog.dom.browserrange.canContainRangeEndpoint(child)) {
+ var childRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_(child);
+ var start = goog.dom.RangeEndpoint.START;
+ var end = goog.dom.RangeEndpoint.END;
+ var isChildRangeErratic = childRange.htmlText != child.outerHTML;
+ var isNativeInRangeErratic = this.isCollapsed() && isChildRangeErratic;
+ var inChildRange = isNativeInRangeErratic ? this.compareBrowserRangeEndpoints(childRange, start, start) >= 0 && this.compareBrowserRangeEndpoints(childRange, start, end) <= 0 : this.range_.inRange(childRange);
+ if(inChildRange) {
+ return this.findDeepestContainer_(child)
+ }
+ }
+ }
+ return node
+};
+goog.dom.browserrange.IeRange.prototype.getStartNode = function() {
+ if(!this.startNode_) {
+ this.startNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.START);
+ if(this.isCollapsed()) {
+ this.endNode_ = this.startNode_
+ }
+ }
+ return this.startNode_
+};
+goog.dom.browserrange.IeRange.prototype.getStartOffset = function() {
+ if(this.startOffset_ < 0) {
+ this.startOffset_ = this.getOffset_(goog.dom.RangeEndpoint.START);
+ if(this.isCollapsed()) {
+ this.endOffset_ = this.startOffset_
+ }
+ }
+ return this.startOffset_
+};
+goog.dom.browserrange.IeRange.prototype.getEndNode = function() {
+ if(this.isCollapsed()) {
+ return this.getStartNode()
+ }
+ if(!this.endNode_) {
+ this.endNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.END)
+ }
+ return this.endNode_
+};
+goog.dom.browserrange.IeRange.prototype.getEndOffset = function() {
+ if(this.isCollapsed()) {
+ return this.getStartOffset()
+ }
+ if(this.endOffset_ < 0) {
+ this.endOffset_ = this.getOffset_(goog.dom.RangeEndpoint.END);
+ if(this.isCollapsed()) {
+ this.startOffset_ = this.endOffset_
+ }
+ }
+ return this.endOffset_
+};
+goog.dom.browserrange.IeRange.prototype.compareBrowserRangeEndpoints = function(range, thisEndpoint, otherEndpoint) {
+ return this.range_.compareEndPoints((thisEndpoint == goog.dom.RangeEndpoint.START ? "Start" : "End") + "To" + (otherEndpoint == goog.dom.RangeEndpoint.START ? "Start" : "End"), range)
+};
+goog.dom.browserrange.IeRange.prototype.getEndpointNode_ = function(endpoint, opt_node) {
+ var node = opt_node || this.getContainer();
+ if(!node || !node.firstChild) {
+ return node
+ }
+ var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END;
+ var isStartEndpoint = endpoint == start;
+ for(var j = 0, length = node.childNodes.length;j < length;j++) {
+ var i = isStartEndpoint ? j : length - j - 1;
+ var child = node.childNodes[i];
+ var childRange;
+ try {
+ childRange = goog.dom.browserrange.createRangeFromNodeContents(child)
+ }catch(e) {
+ continue
+ }
+ var ieRange = childRange.getBrowserRange();
+ if(this.isCollapsed()) {
+ if(!goog.dom.browserrange.canContainRangeEndpoint(child)) {
+ if(this.compareBrowserRangeEndpoints(ieRange, start, start) == 0) {
+ this.startOffset_ = this.endOffset_ = i;
+ return node
+ }
+ }else {
+ if(childRange.containsRange(this)) {
+ return this.getEndpointNode_(endpoint, child)
+ }
+ }
+ }else {
+ if(this.containsRange(childRange)) {
+ if(!goog.dom.browserrange.canContainRangeEndpoint(child)) {
+ if(isStartEndpoint) {
+ this.startOffset_ = i
+ }else {
+ this.endOffset_ = i + 1
+ }
+ return node
+ }
+ return this.getEndpointNode_(endpoint, child)
+ }else {
+ if(this.compareBrowserRangeEndpoints(ieRange, start, end) < 0 && this.compareBrowserRangeEndpoints(ieRange, end, start) > 0) {
+ return this.getEndpointNode_(endpoint, child)
+ }
+ }
+ }
+ }
+ return node
+};
+goog.dom.browserrange.IeRange.prototype.compareNodeEndpoints_ = function(node, thisEndpoint, otherEndpoint) {
+ return this.range_.compareEndPoints((thisEndpoint == goog.dom.RangeEndpoint.START ? "Start" : "End") + "To" + (otherEndpoint == goog.dom.RangeEndpoint.START ? "Start" : "End"), goog.dom.browserrange.createRangeFromNodeContents(node).getBrowserRange())
+};
+goog.dom.browserrange.IeRange.prototype.getOffset_ = function(endpoint, opt_container) {
+ var isStartEndpoint = endpoint == goog.dom.RangeEndpoint.START;
+ var container = opt_container || (isStartEndpoint ? this.getStartNode() : this.getEndNode());
+ if(container.nodeType == goog.dom.NodeType.ELEMENT) {
+ var children = container.childNodes;
+ var len = children.length;
+ var edge = isStartEndpoint ? 0 : len - 1;
+ var sign = isStartEndpoint ? 1 : -1;
+ for(var i = edge;i >= 0 && i < len;i += sign) {
+ var child = children[i];
+ if(goog.dom.browserrange.canContainRangeEndpoint(child)) {
+ continue
+ }
+ var endPointCompare = this.compareNodeEndpoints_(child, endpoint, endpoint);
+ if(endPointCompare == 0) {
+ return isStartEndpoint ? i : i + 1
+ }
+ }
+ return i == -1 ? 0 : i
+ }else {
+ var range = this.range_.duplicate();
+ var nodeRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_(container);
+ range.setEndPoint(isStartEndpoint ? "EndToEnd" : "StartToStart", nodeRange);
+ var rangeLength = range.text.length;
+ return isStartEndpoint ? container.length - rangeLength : rangeLength
+ }
+};
+goog.dom.browserrange.IeRange.getNodeText_ = function(node) {
+ return node.nodeType == goog.dom.NodeType.TEXT ? node.nodeValue : node.innerText
+};
+goog.dom.browserrange.IeRange.prototype.isRangeInDocument = function() {
+ var range = this.doc_.body.createTextRange();
+ range.moveToElementText(this.doc_.body);
+ return this.containsRange(new goog.dom.browserrange.IeRange(range, this.doc_), true)
+};
+goog.dom.browserrange.IeRange.prototype.isCollapsed = function() {
+ return this.range_.compareEndPoints("StartToEnd", this.range_) == 0
+};
+goog.dom.browserrange.IeRange.prototype.getText = function() {
+ return this.range_.text
+};
+goog.dom.browserrange.IeRange.prototype.getValidHtml = function() {
+ return this.range_.htmlText
+};
+goog.dom.browserrange.IeRange.prototype.select = function(opt_reverse) {
+ this.range_.select()
+};
+goog.dom.browserrange.IeRange.prototype.removeContents = function() {
+ if(this.range_.htmlText) {
+ var startNode = this.getStartNode();
+ var endNode = this.getEndNode();
+ var oldText = this.range_.text;
+ var clone = this.range_.duplicate();
+ clone.moveStart("character", 1);
+ clone.moveStart("character", -1);
+ if(clone.text != oldText) {
+ var iter = new goog.dom.NodeIterator(startNode, false, true);
+ var toDelete = [];
+ goog.iter.forEach(iter, function(node) {
+ if(node.nodeType != goog.dom.NodeType.TEXT && this.containsNode(node)) {
+ toDelete.push(node);
+ iter.skipTag()
+ }
+ if(node == endNode) {
+ throw goog.iter.StopIteration;
+ }
+ });
+ this.collapse(true);
+ goog.array.forEach(toDelete, goog.dom.removeNode);
+ this.clearCachedValues_();
+ return
+ }
+ this.range_ = clone;
+ this.range_.text = "";
+ this.clearCachedValues_();
+ var newStartNode = this.getStartNode();
+ var newStartOffset = this.getStartOffset();
+ try {
+ var sibling = startNode.nextSibling;
+ if(startNode == endNode && startNode.parentNode && startNode.nodeType == goog.dom.NodeType.TEXT && sibling && sibling.nodeType == goog.dom.NodeType.TEXT) {
+ startNode.nodeValue += sibling.nodeValue;
+ goog.dom.removeNode(sibling);
+ this.range_ = goog.dom.browserrange.IeRange.getBrowserRangeForNode_(newStartNode);
+ this.range_.move("character", newStartOffset);
+ this.clearCachedValues_()
+ }
+ }catch(e) {
+ }
+ }
+};
+goog.dom.browserrange.IeRange.getDomHelper_ = function(range) {
+ return goog.dom.getDomHelper(range.parentElement())
+};
+goog.dom.browserrange.IeRange.pasteElement_ = function(range, element, opt_domHelper) {
+ opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_(range);
+ var id;
+ var originalId = id = element.id;
+ if(!id) {
+ id = element.id = goog.string.createUniqueString()
+ }
+ range.pasteHTML(element.outerHTML);
+ element = opt_domHelper.getElement(id);
+ if(element) {
+ if(!originalId) {
+ element.removeAttribute("id")
+ }
+ }
+ return element
+};
+goog.dom.browserrange.IeRange.prototype.surroundContents = function(element) {
+ goog.dom.removeNode(element);
+ element.innerHTML = this.range_.htmlText;
+ element = goog.dom.browserrange.IeRange.pasteElement_(this.range_, element);
+ if(element) {
+ this.range_.moveToElementText(element)
+ }
+ this.clearCachedValues_();
+ return element
+};
+goog.dom.browserrange.IeRange.insertNode_ = function(clone, node, before, opt_domHelper) {
+ opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_(clone);
+ var isNonElement;
+ if(node.nodeType != goog.dom.NodeType.ELEMENT) {
+ isNonElement = true;
+ node = opt_domHelper.createDom(goog.dom.TagName.DIV, null, node)
+ }
+ clone.collapse(before);
+ node = goog.dom.browserrange.IeRange.pasteElement_(clone, node, opt_domHelper);
+ if(isNonElement) {
+ var newNonElement = node.firstChild;
+ opt_domHelper.flattenElement(node);
+ node = newNonElement
+ }
+ return node
+};
+goog.dom.browserrange.IeRange.prototype.insertNode = function(node, before) {
+ var output = goog.dom.browserrange.IeRange.insertNode_(this.range_.duplicate(), node, before);
+ this.clearCachedValues_();
+ return output
+};
+goog.dom.browserrange.IeRange.prototype.surroundWithNodes = function(startNode, endNode) {
+ var clone1 = this.range_.duplicate();
+ var clone2 = this.range_.duplicate();
+ goog.dom.browserrange.IeRange.insertNode_(clone1, startNode, true);
+ goog.dom.browserrange.IeRange.insertNode_(clone2, endNode, false);
+ this.clearCachedValues_()
+};
+goog.dom.browserrange.IeRange.prototype.collapse = function(toStart) {
+ this.range_.collapse(toStart);
+ if(toStart) {
+ this.endNode_ = this.startNode_;
+ this.endOffset_ = this.startOffset_
+ }else {
+ this.startNode_ = this.endNode_;
+ this.startOffset_ = this.endOffset_
+ }
+};
+goog.provide("goog.dom.browserrange.OperaRange");
+goog.require("goog.dom.browserrange.W3cRange");
+goog.dom.browserrange.OperaRange = function(range) {
+ goog.dom.browserrange.W3cRange.call(this, range)
+};
+goog.inherits(goog.dom.browserrange.OperaRange, goog.dom.browserrange.W3cRange);
+goog.dom.browserrange.OperaRange.createFromNodeContents = function(node) {
+ return new goog.dom.browserrange.OperaRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node))
+};
+goog.dom.browserrange.OperaRange.createFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ return new goog.dom.browserrange.OperaRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, startOffset, endNode, endOffset))
+};
+goog.dom.browserrange.OperaRange.prototype.selectInternal = function(selection, reversed) {
+ selection.collapse(this.getStartNode(), this.getStartOffset());
+ if(this.getEndNode() != this.getStartNode() || this.getEndOffset() != this.getStartOffset()) {
+ selection.extend(this.getEndNode(), this.getEndOffset())
+ }
+ if(selection.rangeCount == 0) {
+ selection.addRange(this.range_)
+ }
+};
+goog.provide("goog.dom.browserrange.WebKitRange");
+goog.require("goog.dom.RangeEndpoint");
+goog.require("goog.dom.browserrange.W3cRange");
+goog.require("goog.userAgent");
+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.createFromNodeContents = function(node) {
+ return new goog.dom.browserrange.WebKitRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node))
+};
+goog.dom.browserrange.WebKitRange.createFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ return new goog.dom.browserrange.WebKitRange(goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, startOffset, endNode, endOffset))
+};
+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 == goog.dom.RangeEndpoint.START ? thisEndpoint == goog.dom.RangeEndpoint.START ? goog.global["Range"].START_TO_START : goog.global["Range"].END_TO_START : thisEndpoint == goog.dom.RangeEndpoint.START ? goog.global["Range"].START_TO_END : goog.global["Range"].END_TO_END, range)
+};
+goog.dom.browserrange.WebKitRange.prototype.selectInternal = function(selection, reversed) {
+ selection.removeAllRanges();
+ if(reversed) {
+ selection.setBaseAndExtent(this.getEndNode(), this.getEndOffset(), this.getStartNode(), this.getStartOffset())
+ }else {
+ selection.setBaseAndExtent(this.getStartNode(), this.getStartOffset(), this.getEndNode(), this.getEndOffset())
+ }
+};
+goog.provide("goog.dom.browserrange");
+goog.provide("goog.dom.browserrange.Error");
+goog.require("goog.dom");
+goog.require("goog.dom.browserrange.GeckoRange");
+goog.require("goog.dom.browserrange.IeRange");
+goog.require("goog.dom.browserrange.OperaRange");
+goog.require("goog.dom.browserrange.W3cRange");
+goog.require("goog.dom.browserrange.WebKitRange");
+goog.require("goog.userAgent");
+goog.dom.browserrange.Error = {NOT_IMPLEMENTED:"Not Implemented"};
+goog.dom.browserrange.createRange = function(range) {
+ if(goog.userAgent.IE && !goog.userAgent.isVersion("9")) {
+ return new goog.dom.browserrange.IeRange(range, goog.dom.getOwnerDocument(range.parentElement()))
+ }else {
+ if(goog.userAgent.WEBKIT) {
+ return new goog.dom.browserrange.WebKitRange(range)
+ }else {
+ if(goog.userAgent.GECKO) {
+ return new goog.dom.browserrange.GeckoRange(range)
+ }else {
+ if(goog.userAgent.OPERA) {
+ return new goog.dom.browserrange.OperaRange(range)
+ }else {
+ return new goog.dom.browserrange.W3cRange(range)
+ }
+ }
+ }
+ }
+};
+goog.dom.browserrange.createRangeFromNodeContents = function(node) {
+ if(goog.userAgent.IE && !goog.userAgent.isVersion("9")) {
+ return goog.dom.browserrange.IeRange.createFromNodeContents(node)
+ }else {
+ if(goog.userAgent.WEBKIT) {
+ return goog.dom.browserrange.WebKitRange.createFromNodeContents(node)
+ }else {
+ if(goog.userAgent.GECKO) {
+ return goog.dom.browserrange.GeckoRange.createFromNodeContents(node)
+ }else {
+ if(goog.userAgent.OPERA) {
+ return goog.dom.browserrange.OperaRange.createFromNodeContents(node)
+ }else {
+ return goog.dom.browserrange.W3cRange.createFromNodeContents(node)
+ }
+ }
+ }
+ }
+};
+goog.dom.browserrange.createRangeFromNodes = function(startNode, startOffset, endNode, endOffset) {
+ if(goog.userAgent.IE && !goog.userAgent.isVersion("9")) {
+ return goog.dom.browserrange.IeRange.createFromNodes(startNode, startOffset, endNode, endOffset)
+ }else {
+ if(goog.userAgent.WEBKIT) {
+ return goog.dom.browserrange.WebKitRange.createFromNodes(startNode, startOffset, endNode, endOffset)
+ }else {
+ if(goog.userAgent.GECKO) {
+ return goog.dom.browserrange.GeckoRange.createFromNodes(startNode, startOffset, endNode, endOffset)
+ }else {
+ if(goog.userAgent.OPERA) {
+ return goog.dom.browserrange.OperaRange.createFromNodes(startNode, startOffset, endNode, endOffset)
+ }else {
+ return goog.dom.browserrange.W3cRange.createFromNodes(startNode, startOffset, endNode, endOffset)
+ }
+ }
+ }
+ }
+};
+goog.dom.browserrange.canContainRangeEndpoint = function(node) {
+ return goog.dom.canHaveChildren(node) || node.nodeType == goog.dom.NodeType.TEXT
+};
+goog.provide("goog.dom.TextRange");
+goog.require("goog.array");
+goog.require("goog.dom");
+goog.require("goog.dom.AbstractRange");
+goog.require("goog.dom.RangeType");
+goog.require("goog.dom.SavedRange");
+goog.require("goog.dom.TagName");
+goog.require("goog.dom.TextRangeIterator");
+goog.require("goog.dom.browserrange");
+goog.require("goog.string");
+goog.require("goog.userAgent");
+goog.dom.TextRange = function() {
+};
+goog.inherits(goog.dom.TextRange, goog.dom.AbstractRange);
+goog.dom.TextRange.createFromBrowserRange = function(range, opt_isReversed) {
+ return goog.dom.TextRange.createFromBrowserRangeWrapper_(goog.dom.browserrange.createRange(range), opt_isReversed)
+};
+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.dom.browserrange.createRangeFromNodeContents(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") {
+ var 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.clone = function() {
+ var range = new goog.dom.TextRange;
+ range.browserRangeWrapper_ = this.browserRangeWrapper_;
+ range.startNode_ = this.startNode_;
+ range.startOffset_ = this.startOffset_;
+ range.endNode_ = this.endNode_;
+ range.endOffset_ = this.endOffset_;
+ range.isReversed_ = this.isReversed_;
+ return range
+};
+goog.dom.TextRange.prototype.getType = function() {
+ return goog.dom.RangeType.TEXT
+};
+goog.dom.TextRange.prototype.getBrowserRangeObject = function() {
+ return this.getBrowserRangeWrapper_().getBrowserRange()
+};
+goog.dom.TextRange.prototype.setBrowserRangeObject = function(nativeRange) {
+ if(goog.dom.AbstractRange.isNativeControlRange(nativeRange)) {
+ return false
+ }
+ this.browserRangeWrapper_ = goog.dom.browserrange.createRange(nativeRange);
+ this.clearCachedValues_();
+ return true
+};
+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(i) {
+ 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.moveToNodes = function(startNode, startOffset, endNode, endOffset, isReversed) {
+ this.startNode_ = startNode;
+ this.startOffset_ = startOffset;
+ this.endNode_ = endNode;
+ this.endOffset_ = endOffset;
+ this.isReversed_ = isReversed;
+ this.browserRangeWrapper_ = null
+};
+goog.dom.TextRange.prototype.isReversed = function() {
+ return this.isReversed_
+};
+goog.dom.TextRange.prototype.containsRange = function(otherRange, opt_allowPartial) {
+ var otherRangeType = otherRange.getType();
+ if(otherRangeType == goog.dom.RangeType.TEXT) {
+ return this.getBrowserRangeWrapper_().containsRange(otherRange.getBrowserRangeWrapper_(), opt_allowPartial)
+ }else {
+ if(otherRangeType == goog.dom.RangeType.CONTROL) {
+ var elements = otherRange.getElements();
+ var fn = opt_allowPartial ? goog.array.some : goog.array.every;
+ return fn(elements, function(el) {
+ return this.containsNode(el, opt_allowPartial)
+ }, this)
+ }
+ }
+ return false
+};
+goog.dom.TextRange.isAttachedNode = function(node) {
+ if(goog.userAgent.IE) {
+ var returnValue = false;
+ try {
+ returnValue = node.parentNode
+ }catch(e) {
+ }
+ return!!returnValue
+ }else {
+ return goog.dom.contains(node.ownerDocument.body, node)
+ }
+};
+goog.dom.TextRange.prototype.isRangeInDocument = function() {
+ return(!this.startNode_ || goog.dom.TextRange.isAttachedNode(this.startNode_)) && (!this.endNode_ || goog.dom.TextRange.isAttachedNode(this.endNode_)) && (!goog.userAgent.IE || this.getBrowserRangeWrapper_().isRangeInDocument())
+};
+goog.dom.TextRange.prototype.isCollapsed = function() {
+ return this.getBrowserRangeWrapper_().isCollapsed()
+};
+goog.dom.TextRange.prototype.getText = function() {
+ return this.getBrowserRangeWrapper_().getText()
+};
+goog.dom.TextRange.prototype.getHtmlFragment = function() {
+ return this.getBrowserRangeWrapper_().getHtmlFragment()
+};
+goog.dom.TextRange.prototype.getValidHtml = function() {
+ return this.getBrowserRangeWrapper_().getValidHtml()
+};
+goog.dom.TextRange.prototype.getPastableHtml = function() {
+ var html = this.getValidHtml();
+ if(html.match(/^\s*<td\b/i)) {
+ html = "<table><tbody><tr>" + html + "</tr></tbody></table>"
+ }else {
+ if(html.match(/^\s*<tr\b/i)) {
+ html = "<table><tbody>" + html + "</tbody></table>"
+ }else {
+ if(html.match(/^\s*<tbody\b/i)) {
+ html = "<table>" + html + "</table>"
+ }else {
+ if(html.match(/^\s*<li\b/i)) {
+ var container = this.getContainer();
+ var tagType = goog.dom.TagName.UL;
+ while(container) {
+ if(container.tagName == goog.dom.TagName.OL) {
+ tagType = goog.dom.TagName.OL;
+ break
+ }else {
+ if(container.tagName == goog.dom.TagName.UL) {
+ break
+ }
+ }
+ container = container.parentNode
+ }
+ html = goog.string.buildString("<", tagType, ">", html, "</", tagType, ">")
+ }
+ }
+ }
+ }
+ return html
+};
+goog.dom.TextRange.prototype.__iterator__ = function(opt_keys) {
+ 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.removeContents = function() {
+ this.getBrowserRangeWrapper_().removeContents();
+ this.clearCachedValues_()
+};
+goog.dom.TextRange.prototype.surroundContents = function(element) {
+ var output = this.getBrowserRangeWrapper_().surroundContents(element);
+ this.clearCachedValues_();
+ return output
+};
+goog.dom.TextRange.prototype.insertNode = function(node, before) {
+ var output = this.getBrowserRangeWrapper_().insertNode(node, before);
+ this.clearCachedValues_();
+ return output
+};
+goog.dom.TextRange.prototype.surroundWithNodes = function(startNode, endNode) {
+ this.getBrowserRangeWrapper_().surroundWithNodes(startNode, endNode);
+ this.clearCachedValues_()
+};
+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;
+ if(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
+};
+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);
+goog.dom.DomSavedTextRange_.prototype.restoreInternal = function() {
+ return goog.dom.Range.createFromNodes(this.anchorNode_, this.anchorOffset_, this.focusNode_, this.focusOffset_)
+};
+goog.dom.DomSavedTextRange_.prototype.disposeInternal = function() {
+ goog.dom.DomSavedTextRange_.superClass_.disposeInternal.call(this);
+ this.anchorNode_ = null;
+ this.focusNode_ = null
+};
+goog.provide("goog.dom.ControlRange");
+goog.provide("goog.dom.ControlRangeIterator");
+goog.require("goog.array");
+goog.require("goog.dom");
+goog.require("goog.dom.AbstractMultiRange");
+goog.require("goog.dom.AbstractRange");
+goog.require("goog.dom.RangeIterator");
+goog.require("goog.dom.RangeType");
+goog.require("goog.dom.SavedRange");
+goog.require("goog.dom.TagWalkType");
+goog.require("goog.dom.TextRange");
+goog.require("goog.iter.StopIteration");
+goog.require("goog.userAgent");
+goog.dom.ControlRange = function() {
+};
+goog.inherits(goog.dom.ControlRange, goog.dom.AbstractMultiRange);
+goog.dom.ControlRange.createFromBrowserRange = function(controlRange) {
+ var range = new goog.dom.ControlRange;
+ range.range_ = controlRange;
+ return range
+};
+goog.dom.ControlRange.createFromElements = function(var_args) {
+ var range = goog.dom.getOwnerDocument(arguments[0]).body.createControlRange();
+ for(var i = 0, len = arguments.length;i < len;i++) {
+ range.addElement(arguments[i])
+ }
+ return goog.dom.ControlRange.createFromBrowserRange(range)
+};
+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.elements_ = null;
+ this.sortedElements_ = null
+};
+goog.dom.ControlRange.prototype.clone = function() {
+ return goog.dom.ControlRange.createFromElements.apply(this, this.getElements())
+};
+goog.dom.ControlRange.prototype.getType = function() {
+ return goog.dom.RangeType.CONTROL
+};
+goog.dom.ControlRange.prototype.getBrowserRangeObject = function() {
+ return this.range_ || document.body.createControlRange()
+};
+goog.dom.ControlRange.prototype.setBrowserRangeObject = function(nativeRange) {
+ if(!goog.dom.AbstractRange.isNativeControlRange(nativeRange)) {
+ return false
+ }
+ this.range_ = nativeRange;
+ return true
+};
+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();
+ var 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.isRangeInDocument = function() {
+ var returnValue = false;
+ try {
+ returnValue = goog.array.every(this.getElements(), function(element) {
+ return goog.userAgent.IE ? element.parentNode : goog.dom.contains(element.ownerDocument.body, element)
+ })
+ }catch(e) {
+ }
+ return returnValue
+};
+goog.dom.ControlRange.prototype.isCollapsed = function() {
+ return!this.range_ || !this.range_.length
+};
+goog.dom.ControlRange.prototype.getText = function() {
+ return""
+};
+goog.dom.ControlRange.prototype.getHtmlFragment = function() {
+ return goog.array.map(this.getSortedElements(), goog.dom.getOuterHtml).join("")
+};
+goog.dom.ControlRange.prototype.getValidHtml = function() {
+ return this.getHtmlFragment()
+};
+goog.dom.ControlRange.prototype.getPastableHtml = goog.dom.ControlRange.prototype.getValidHtml;
+goog.dom.ControlRange.prototype.__iterator__ = function(opt_keys) {
+ return new goog.dom.ControlRangeIterator(this)
+};
+goog.dom.ControlRange.prototype.select = function() {
+ if(this.range_) {
+ this.range_.select()
+ }
+};
+goog.dom.ControlRange.prototype.removeContents = function() {
+ if(this.range_) {
+ var nodes = [];
+ for(var i = 0, len = this.range_.length;i < len;i++) {
+ nodes.push(this.range_.item(i))
+ }
+ goog.array.forEach(nodes, goog.dom.removeNode);
+ this.collapse(false)
+ }
+};
+goog.dom.ControlRange.prototype.replaceContentsWithNode = function(node) {
+ var result = this.insertNode(node, true);
+ if(!this.isCollapsed()) {
+ this.removeContents()
+ }
+ return result
+};
+goog.dom.ControlRange.prototype.saveUsingDom = function() {
+ return new goog.dom.DomSavedControlRange_(this)
+};
+goog.dom.ControlRange.prototype.collapse = function(toAnchor) {
+ this.range_ = null;
+ this.clearCachedValues_()
+};
+goog.dom.DomSavedControlRange_ = function(range) {
+ this.elements_ = range.getElements()
+};
+goog.inherits(goog.dom.DomSavedControlRange_, goog.dom.SavedRange);
+goog.dom.DomSavedControlRange_.prototype.restoreInternal = function() {
+ var doc = this.elements_.length ? goog.dom.getOwnerDocument(this.elements_[0]) : document;
+ var controlRange = doc.body.createControlRange();
+ for(var i = 0, len = this.elements_.length;i < len;i++) {
+ controlRange.addElement(this.elements_[i])
+ }
+ return goog.dom.ControlRange.createFromBrowserRange(controlRange)
+};
+goog.dom.DomSavedControlRange_.prototype.disposeInternal = function() {
+ goog.dom.DomSavedControlRange_.superClass_.disposeInternal.call(this);
+ delete this.elements_
+};
+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.getStartTextOffset = function() {
+ return 0
+};
+goog.dom.ControlRangeIterator.prototype.getEndTextOffset = function() {
+ return 0
+};
+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, goog.dom.TagWalkType.START_TAG, goog.dom.TagWalkType.START_TAG);
+ return el
+ }
+ }
+ return goog.dom.ControlRangeIterator.superClass_.next.call(this)
+};
+goog.dom.ControlRangeIterator.prototype.copyFrom = function(other) {
+ this.elements_ = other.elements_;
+ this.startNode_ = other.startNode_;
+ this.endNode_ = other.endNode_;
+ goog.dom.ControlRangeIterator.superClass_.copyFrom.call(this, other)
+};
+goog.dom.ControlRangeIterator.prototype.clone = function() {
+ var copy = new goog.dom.ControlRangeIterator(null);
+ copy.copyFrom(this);
+ return copy
+};
+goog.provide("goog.dom.MultiRange");
+goog.provide("goog.dom.MultiRangeIterator");
+goog.require("goog.array");
+goog.require("goog.debug.Logger");
+goog.require("goog.dom.AbstractMultiRange");
+goog.require("goog.dom.AbstractRange");
+goog.require("goog.dom.RangeIterator");
+goog.require("goog.dom.RangeType");
+goog.require("goog.dom.SavedRange");
+goog.require("goog.dom.TextRange");
+goog.require("goog.iter.StopIteration");
+goog.dom.MultiRange = function() {
+ this.browserRanges_ = [];
+ this.ranges_ = [];
+ this.sortedRanges_ = null;
+ this.container_ = null
+};
+goog.inherits(goog.dom.MultiRange, goog.dom.AbstractMultiRange);
+goog.dom.MultiRange.createFromBrowserSelection = function(selection) {
+ var range = new goog.dom.MultiRange;
+ for(var i = 0, len = selection.rangeCount;i < len;i++) {
+ range.browserRanges_.push(selection.getRangeAt(i))
+ }
+ return range
+};
+goog.dom.MultiRange.createFromBrowserRanges = function(browserRanges) {
+ var range = new goog.dom.MultiRange;
+ range.browserRanges_ = goog.array.clone(browserRanges);
+ return range
+};
+goog.dom.MultiRange.createFromTextRanges = function(textRanges) {
+ var range = new goog.dom.MultiRange;
+ range.ranges_ = textRanges;
+ range.browserRanges_ = goog.array.map(textRanges, function(range) {
+ return range.getBrowserRangeObject()
+ });
+ return range
+};
+goog.dom.MultiRange.prototype.logger_ = goog.debug.Logger.getLogger("goog.dom.MultiRange");
+goog.dom.MultiRange.prototype.clearCachedValues_ = function() {
+ this.ranges_ = [];
+ this.sortedRanges_ = null;
+ this.container_ = null
+};
+goog.dom.MultiRange.prototype.clone = function() {
+ return goog.dom.MultiRange.createFromBrowserRanges(this.browserRanges_)
+};
+goog.dom.MultiRange.prototype.getType = function() {
+ return goog.dom.RangeType.MULTI
+};
+goog.dom.MultiRange.prototype.getBrowserRangeObject = function() {
+ if(this.browserRanges_.length > 1) {
+ this.logger_.warning("getBrowserRangeObject called on MultiRange with more than 1 range")
+ }
+ return this.browserRanges_[0]
+};
+goog.dom.MultiRange.prototype.setBrowserRangeObject = function(nativeRange) {
+ return false
+};
+goog.dom.MultiRange.prototype.getTextRangeCount = function() {
+ return this.browserRanges_.length
+};
+goog.dom.MultiRange.prototype.getTextRange = function(i) {
+ if(!this.ranges_[i]) {
+ this.ranges_[i] = goog.dom.TextRange.createFromBrowserRange(this.browserRanges_[i])
+ }
+ return this.ranges_[i]
+};
+goog.dom.MultiRange.prototype.getContainer = function() {
+ if(!this.container_) {
+ var nodes = [];
+ for(var 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();
+ var aStartOffset = a.getStartOffset();
+ var bStartNode = b.getStartNode();
+ var 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.isRangeInDocument = function() {
+ return goog.array.every(this.getTextRanges(), function(range) {
+ return range.isRangeInDocument()
+ })
+};
+*/
+goog.dom.MultiRange.prototype.isCollapsed = function() {
+ return this.browserRanges_.length == 0 || this.browserRanges_.length == 1 && this.getTextRange(0).isCollapsed()
+};
+goog.dom.MultiRange.prototype.getText = function() {
+ return goog.array.map(this.getTextRanges(), function(range) {
+ return range.getText()
+ }).join("")
+};
+goog.dom.MultiRange.prototype.getHtmlFragment = function() {
+ return this.getValidHtml()
+};
+goog.dom.MultiRange.prototype.getValidHtml = function() {
+ return goog.array.map(this.getTextRanges(), function(range) {
+ return range.getValidHtml()
+ }).join("")
+};
+goog.dom.MultiRange.prototype.getPastableHtml = function() {
+ return this.getValidHtml()
+};
+goog.dom.MultiRange.prototype.__iterator__ = function(opt_keys) {
+ return new goog.dom.MultiRangeIterator(this)
+};
+goog.dom.MultiRange.prototype.select = function() {
+ var selection = goog.dom.AbstractRange.getBrowserSelectionForWindow(this.getWindow());
+ selection.removeAllRanges();
+ for(var i = 0, len = this.getTextRangeCount();i < len;i++) {
+ selection.addRange(this.getTextRange(i).getBrowserRangeObject())
+ }
+};
+goog.dom.MultiRange.prototype.removeContents = function() {
+ goog.array.forEach(this.getTextRanges(), function(range) {
+ range.removeContents()
+ })
+};
+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()]
+ }
+};
+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);
+goog.dom.DomSavedMultiRange_.prototype.restoreInternal = function() {
+ var ranges = goog.array.map(this.savedRanges_, function(savedRange) {
+ return savedRange.restore()
+ });
+ return goog.dom.MultiRange.createFromTextRanges(ranges)
+};
+goog.dom.DomSavedMultiRange_.prototype.disposeInternal = function() {
+ goog.dom.DomSavedMultiRange_.superClass_.disposeInternal.call(this);
+ goog.array.forEach(this.savedRanges_, function(savedRange) {
+ savedRange.dispose()
+ });
+ delete this.savedRanges_
+};
+goog.dom.MultiRangeIterator = function(range) {
+ if(range) {
+ this.iterators_ = goog.array.map(range.getSortedRanges(), function(r) {
+ return goog.iter.toIterator(r)
+ })
+ }
+ goog.dom.RangeIterator.call(this, range ? this.getStartNode() : null, false)
+};
+goog.inherits(goog.dom.MultiRangeIterator, goog.dom.RangeIterator);
+goog.dom.MultiRangeIterator.prototype.iterators_ = null;
+goog.dom.MultiRangeIterator.prototype.currentIdx_ = 0;
+goog.dom.MultiRangeIterator.prototype.getStartTextOffset = function() {
+ return this.iterators_[this.currentIdx_].getStartTextOffset()
+};
+goog.dom.MultiRangeIterator.prototype.getEndTextOffset = function() {
+ return this.iterators_[this.currentIdx_].getEndTextOffset()
+};
+goog.dom.MultiRangeIterator.prototype.getStartNode = function() {
+ return this.iterators_[0].getStartNode()
+};
+goog.dom.MultiRangeIterator.prototype.getEndNode = function() {
+ return goog.array.peek(this.iterators_).getEndNode()
+};
+goog.dom.MultiRangeIterator.prototype.isLast = function() {
+ return this.iterators_[this.currentIdx_].isLast()
+};
+goog.dom.MultiRangeIterator.prototype.next = function() {
+ try {
+ var it = this.iterators_[this.currentIdx_];
+ var next = it.next();
+ this.setPosition(it.node, it.tagType, it.depth);
+ return next
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration || this.iterators_.length - 1 == this.currentIdx_) {
+ throw ex;
+ }else {
+ this.currentIdx_++;
+ return this.next()
+ }
+ }
+};
+goog.dom.MultiRangeIterator.prototype.copyFrom = function(other) {
+ this.iterators_ = goog.array.clone(other.iterators_);
+ goog.dom.MultiRangeIterator.superClass_.copyFrom.call(this, other)
+};
+goog.dom.MultiRangeIterator.prototype.clone = function() {
+ var copy = new goog.dom.MultiRangeIterator(null);
+ copy.copyFrom(this);
+ return copy
+};
+goog.provide("goog.dom.Range");
+goog.require("goog.dom");
+goog.require("goog.dom.AbstractRange");
+goog.require("goog.dom.ControlRange");
+goog.require("goog.dom.MultiRange");
+goog.require("goog.dom.NodeType");
+goog.require("goog.dom.TextRange");
+goog.require("goog.userAgent");
+goog.dom.Range.createFromWindow = function(opt_win) {
+ var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow(opt_win || window);
+ return sel && goog.dom.Range.createFromBrowserSelection(sel)
+};
+goog.dom.Range.createFromBrowserSelection = function(selection) {
+ var range;
+ var isReversed = false;
+ if(selection.createRange) {
+ try {
+ range = selection.createRange()
+ }catch(e) {
+ return null
+ }
+ }else {
+ if(selection.rangeCount) {
+ if(selection.rangeCount > 1) {
+ return goog.dom.MultiRange.createFromBrowserSelection(selection)
+ }else {
+ range = selection.getRangeAt(0);
+ isReversed = goog.dom.Range.isReversed(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset)
+ }
+ }else {
+ return null
+ }
+ }
+ return goog.dom.Range.createFromBrowserRange(range, isReversed)
+};
+goog.dom.Range.createFromBrowserRange = function(range, opt_isReversed) {
+ return goog.dom.AbstractRange.isNativeControlRange(range) ? goog.dom.ControlRange.createFromBrowserRange(range) : goog.dom.TextRange.createFromBrowserRange(range, opt_isReversed)
+};
+goog.dom.Range.createFromNodeContents = function(node, opt_isReversed) {
+ return goog.dom.TextRange.createFromNodeContents(node, opt_isReversed)
+};
+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.clearSelection = function(opt_win) {
+ var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow(opt_win || window);
+ if(!sel) {
+ return
+ }
+ if(sel.empty) {
+ try {
+ sel.empty()
+ }catch(e) {
+ }
+ }else {
+ sel.removeAllRanges()
+ }
+};
+goog.dom.Range.hasSelection = function(opt_win) {
+ var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow(opt_win || window);
+ return!!sel && (goog.userAgent.IE ? sel.type != "None" : !!sel.rangeCount)
+};
+goog.dom.Range.isReversed = function(anchorNode, anchorOffset, focusNode, focusOffset) {
+ if(anchorNode == focusNode) {
+ return focusOffset < anchorOffset
+ }
+ var child;
+ if(anchorNode.nodeType == goog.dom.NodeType.ELEMENT && anchorOffset) {
+ child = anchorNode.childNodes[anchorOffset];
+ if(child) {
+ anchorNode = child;
+ anchorOffset = 0
+ }else {
+ if(goog.dom.contains(anchorNode, focusNode)) {
+ return true
+ }
+ }
+ }
+ if(focusNode.nodeType == goog.dom.NodeType.ELEMENT && focusOffset) {
+ child = focusNode.childNodes[focusOffset];
+ if(child) {
+ focusNode = child;
+ focusOffset = 0
+ }else {
+ if(goog.dom.contains(focusNode, anchorNode)) {
+ return false
+ }
+ }
+ }
+ return(goog.dom.compareNodeOrder(anchorNode, focusNode) || anchorOffset - focusOffset) > 0
+};
+window.createFromWindow = goog.dom.Range.createFromWindow;
+window.createFromNodes = goog.dom.Range.createFromNodes;
+window.createCaret = goog.dom.Range.createCaret; \ No newline at end of file
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/run.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/run.js
new file mode 100644
index 0000000000..6e2acd937c
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/run.js
@@ -0,0 +1,383 @@
+/**
+ * @fileoverview
+ * Main functions used in running the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+/**
+ * Info function: returns true if the suite (mainly) tests the result HTML/Text.
+ *
+ * @param suite {String} the test suite
+ * @return {boolean} Whether the suite main focus is the output HTML/Text
+ */
+function suiteChecksHTMLOrText(suite) {
+ return suite.id[0] != 'S';
+}
+
+/**
+ * Info function: returns true if the suite checks the result selection.
+ *
+ * @param suite {String} the test suite
+ * @return {boolean} Whether the suite checks the selection
+ */
+function suiteChecksSelection(suite) {
+ return suite.id[0] != 'Q';
+}
+
+/**
+ * Helper function returning the effective value of a test parameter.
+ *
+ * @param suite {Object} the test suite
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test
+ * @param param {String} the test parameter to be checked
+ * @return {Any} the effective value of the parameter (can be undefined)
+ */
+function getTestParameter(suite, group, test, param) {
+ var val = test[param];
+ if (val === undefined) {
+ val = group[param];
+ }
+ if (val === undefined) {
+ val = suite[param];
+ }
+ return val;
+}
+
+/**
+ * Helper function returning the effective value of a container/test parameter.
+ *
+ * @param suite {Object} the test suite
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} the test
+ * @param container {Object} the container descriptor object
+ * @param param {String} the test parameter to be checked
+ * @return {Any} the effective value of the parameter (can be undefined)
+ */
+function getContainerParameter(suite, group, test, container, param) {
+ var val = undefined;
+ if (test[container.id]) {
+ val = test[container.id][param];
+ }
+ if (val === undefined) {
+ val = test[param];
+ }
+ if (val === undefined) {
+ val = group[param];
+ }
+ if (val === undefined) {
+ val = suite[param];
+ }
+ return val;
+}
+
+/**
+ * Initializes the global variables before any tests are run.
+ */
+function initVariables() {
+ results = {
+ count: 0,
+ valscore: 0,
+ selscore: 0
+ };
+}
+
+/**
+ * Runs a single test - outputs and sets the result variables.
+ *
+ * @param suite {Object} suite that test originates in as object reference
+ * @param group {Object} group of tests within the suite the test belongs to
+ * @param test {Object} test to be run as object reference
+ * @param container {Object} container descriptor as object reference
+ * @see variables.js for RESULT... values
+ */
+function runSingleTest(suite, group, test, container) {
+ var result = {
+ valscore: 0,
+ selscore: 0,
+ valresult: VALRESULT_NOT_RUN,
+ selresult: SELRESULT_NOT_RUN,
+ output: ''
+ };
+
+ // 1.) Populate the editor element with the initial test setup HTML.
+ try {
+ initContainer(suite, group, test, container);
+ } catch(ex) {
+ result.valresult = VALRESULT_SETUP_EXCEPTION;
+ result.selresult = SELRESULT_NA;
+ result.output = SETUP_EXCEPTION + ex.toString();
+ return result;
+ }
+
+ // 2.) Run the test command, general function or query function.
+ var isHTMLTest = false;
+
+ try {
+ var cmd = undefined;
+
+ if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
+ isHTMLTest = true;
+ // Note: "getTestParameter(suite, group, test, PARAM_VALUE) || null"
+ // doesn't work, since value might be the empty string, e.g., for 'insertText'!
+ var value = getTestParameter(suite, group, test, PARAM_VALUE);
+ if (value === undefined) {
+ value = null;
+ }
+ container.doc.execCommand(cmd, false, value);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
+ isHTMLTest = true;
+ eval(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
+ result.output = container.doc.queryCommandSupported(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
+ result.output = container.doc.queryCommandEnabled(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
+ result.output = container.doc.queryCommandIndeterm(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
+ result.output = container.doc.queryCommandState(cmd);
+ } else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
+ result.output = container.doc.queryCommandValue(cmd);
+ if (result.output === false) {
+ // A return value of boolean 'false' for queryCommandValue means 'not supported'.
+ result.valresult = VALRESULT_UNSUPPORTED;
+ result.selresult = SELRESULT_NA;
+ result.output = UNSUPPORTED;
+ return result;
+ }
+ } else {
+ result.valresult = VALRESULT_SETUP_EXCEPTION;
+ result.selresult = SELRESULT_NA;
+ result.output = SETUP_EXCEPTION + SETUP_NOCOMMAND;
+ return result;
+ }
+ } catch (ex) {
+ result.valresult = VALRESULT_EXECUTION_EXCEPTION;
+ result.selresult = SELRESULT_NA;
+ result.output = EXECUTION_EXCEPTION + ex.toString();
+ return result;
+ }
+
+ // 4.) Verify test result
+ try {
+ if (isHTMLTest) {
+ // First, retrieve HTML from container
+ prepareHTMLTestResult(container, result);
+
+ // Compare result to expectations
+ compareHTMLTestResult(suite, group, test, container, result);
+
+ result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
+ result.selscore = (result.selresult === SELRESULT_EQUAL) ? 1 : 0;
+ } else {
+ compareTextTestResult(suite, group, test, result);
+
+ result.selresult = SELRESULT_NA;
+ result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
+ }
+ } catch (ex) {
+ result.valresult = VALRESULT_VERIFICATION_EXCEPTION;
+ result.selresult = SELRESULT_NA;
+ result.output = VERIFICATION_EXCEPTION + ex.toString();
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * Initializes the results dictionary for a given test suite.
+ * (for all classes -> tests -> containers)
+ *
+ * @param {Object} suite as object reference
+ */
+function initTestSuiteResults(suite) {
+ var suiteID = suite.id;
+
+ // Initialize results entries for this suite
+ results[suiteID] = {
+ count: 0,
+ valscore: 0,
+ selscore: 0,
+ time: 0
+ };
+ var totalTestCount = 0;
+
+ for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
+ var clsID = testClassIDs[clsIdx];
+ var cls = suite[clsID];
+ if (!cls)
+ continue;
+
+ results[suiteID][clsID] = {
+ count: 0,
+ valscore: 0,
+ selscore: 0
+ };
+ var clsTestCount = 0;
+
+ var groupCount = cls.length;
+ for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
+ var group = cls[groupIdx];
+ var testCount = group.tests.length;
+
+ clsTestCount += testCount;
+ totalTestCount += testCount;
+
+ for (var testIdx = 0; testIdx < testCount; ++testIdx) {
+ var test = group.tests[testIdx];
+
+ results[suiteID][clsID ][test.id] = {
+ valscore: 0,
+ selscore: 0,
+ valresult: VALRESULT_NOT_RUN,
+ selresult: SELRESULT_NOT_RUN
+ };
+ for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
+ var cntID = containers[cntIdx].id;
+
+ results[suiteID][clsID][test.id][cntID] = {
+ valscore: 0,
+ selscore: 0,
+ valresult: VALRESULT_NOT_RUN,
+ selresult: SELRESULT_NOT_RUN,
+ output: ''
+ }
+ }
+ }
+ }
+ results[suiteID][clsID].count = clsTestCount;
+ }
+ results[suiteID].count = totalTestCount;
+}
+
+/**
+ * Runs a single test suite (such as DELETE tests or INSERT tests).
+ *
+ * @param suite {Object} suite as object reference
+ */
+function runTestSuite(suite) {
+ var suiteID = suite.id;
+ var suiteStartTime = new Date().getTime();
+
+ initTestSuiteResults(suite);
+
+ for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
+ var clsID = testClassIDs[clsIdx];
+ var cls = suite[clsID];
+ if (!cls)
+ continue;
+
+ var groupCount = cls.length;
+
+ for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
+ var group = cls[groupIdx];
+ var testCount = group.tests.length;
+
+ for (var testIdx = 0; testIdx < testCount; ++testIdx) {
+ var test = group.tests[testIdx];
+
+ var valscore = 1;
+ var selscore = 1;
+ var valresult = VALRESULT_EQUAL;
+ var selresult = SELRESULT_EQUAL;
+
+ for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
+ var container = containers[cntIdx];
+ var cntID = container.id;
+
+ var result = runSingleTest(suite, group, test, container);
+
+ results[suiteID][clsID][test.id][cntID] = result;
+
+ valscore = Math.min(valscore, result.valscore);
+ selscore = Math.min(selscore, result.selscore);
+ valresult = Math.min(valresult, result.valresult);
+ selresult = Math.min(selresult, result.selresult);
+
+ resetContainer(container);
+ }
+
+ results[suiteID][clsID][test.id].valscore = valscore;
+ results[suiteID][clsID][test.id].selscore = selscore;
+ results[suiteID][clsID][test.id].valresult = valresult;
+ results[suiteID][clsID][test.id].selresult = selresult;
+
+ results[suiteID][clsID].valscore += valscore;
+ results[suiteID][clsID].selscore += selscore;
+ results[suiteID].valscore += valscore;
+ results[suiteID].selscore += selscore;
+ results.valscore += valscore;
+ results.selscore += selscore;
+ }
+ }
+ }
+
+ results[suiteID].time = new Date().getTime() - suiteStartTime;
+}
+
+/**
+ * Runs a single test suite (such as DELETE tests or INSERT tests)
+ * and updates the output HTML.
+ *
+ * @param {Object} suite as object reference
+ */
+function runAndOutputTestSuite(suite) {
+ runTestSuite(suite);
+ outputTestSuiteResults(suite);
+}
+
+/**
+ * Fills the beacon with the test results.
+ */
+function fillResults() {
+ // Result totals of the individual categories
+ categoryTotals = [
+ 'selection=' + results['S'].selscore,
+ 'apply=' + results['A'].valscore,
+ 'applyCSS=' + results['AC'].valscore,
+ 'change=' + results['C'].valscore,
+ 'changeCSS=' + results['CC'].valscore,
+ 'unapply=' + results['U'].valscore,
+ 'unapplyCSS=' + results['UC'].valscore,
+ 'delete=' + results['D'].valscore,
+ 'forwarddelete=' + results['FD'].valscore,
+ 'insert=' + results['I'].valscore,
+ 'selectionResult=' + (results['A'].selscore +
+ results['AC'].selscore +
+ results['C'].selscore +
+ results['CC'].selscore +
+ results['U'].selscore +
+ results['UC'].selscore +
+ results['D'].selscore +
+ results['FD'].selscore +
+ results['I'].selscore),
+ 'querySupported=' + results['Q'].valscore,
+ 'queryEnabled=' + results['QE'].valscore,
+ 'queryIndeterm=' + results['QI'].valscore,
+ 'queryState=' + results['QS'].valscore,
+ 'queryStateCSS=' + results['QSC'].valscore,
+ 'queryValue=' + results['QV'].valscore,
+ 'queryValueCSS=' + results['QVC'].valscore
+ ];
+
+ // Beacon copies category results
+ beacon = categoryTotals.slice(0);
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/units.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/units.js
new file mode 100644
index 0000000000..f2c23fbe50
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/units.js
@@ -0,0 +1,416 @@
+/**
+ * @fileoverview
+ * Common constants and variables used in the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+// All colors defined in CSS3.
+var colorChart = {
+ 'aliceblue': {red: 0xF0, green: 0xF8, blue: 0xFF},
+ 'antiquewhite': {red: 0xFA, green: 0xEB, blue: 0xD7},
+ 'aqua': {red: 0x00, green: 0xFF, blue: 0xFF},
+ 'aquamarine': {red: 0x7F, green: 0xFF, blue: 0xD4},
+ 'azure': {red: 0xF0, green: 0xFF, blue: 0xFF},
+ 'beige': {red: 0xF5, green: 0xF5, blue: 0xDC},
+ 'bisque': {red: 0xFF, green: 0xE4, blue: 0xC4},
+ 'black': {red: 0x00, green: 0x00, blue: 0x00},
+ 'blanchedalmond': {red: 0xFF, green: 0xEB, blue: 0xCD},
+ 'blue': {red: 0x00, green: 0x00, blue: 0xFF},
+ 'blueviolet': {red: 0x8A, green: 0x2B, blue: 0xE2},
+ 'brown': {red: 0xA5, green: 0x2A, blue: 0x2A},
+ 'burlywood': {red: 0xDE, green: 0xB8, blue: 0x87},
+ 'cadetblue': {red: 0x5F, green: 0x9E, blue: 0xA0},
+ 'chartreuse': {red: 0x7F, green: 0xFF, blue: 0x00},
+ 'chocolate': {red: 0xD2, green: 0x69, blue: 0x1E},
+ 'coral': {red: 0xFF, green: 0x7F, blue: 0x50},
+ 'cornflowerblue': {red: 0x64, green: 0x95, blue: 0xED},
+ 'cornsilk': {red: 0xFF, green: 0xF8, blue: 0xDC},
+ 'crimson': {red: 0xDC, green: 0x14, blue: 0x3C},
+ 'cyan': {red: 0x00, green: 0xFF, blue: 0xFF},
+ 'darkblue': {red: 0x00, green: 0x00, blue: 0x8B},
+ 'darkcyan': {red: 0x00, green: 0x8B, blue: 0x8B},
+ 'darkgoldenrod': {red: 0xB8, green: 0x86, blue: 0x0B},
+ 'darkgray': {red: 0xA9, green: 0xA9, blue: 0xA9},
+ 'darkgreen': {red: 0x00, green: 0x64, blue: 0x00},
+ 'darkgrey': {red: 0xA9, green: 0xA9, blue: 0xA9},
+ 'darkkhaki': {red: 0xBD, green: 0xB7, blue: 0x6B},
+ 'darkmagenta': {red: 0x8B, green: 0x00, blue: 0x8B},
+ 'darkolivegreen': {red: 0x55, green: 0x6B, blue: 0x2F},
+ 'darkorange': {red: 0xFF, green: 0x8C, blue: 0x00},
+ 'darkorchid': {red: 0x99, green: 0x32, blue: 0xCC},
+ 'darkred': {red: 0x8B, green: 0x00, blue: 0x00},
+ 'darksalmon': {red: 0xE9, green: 0x96, blue: 0x7A},
+ 'darkseagreen': {red: 0x8F, green: 0xBC, blue: 0x8F},
+ 'darkslateblue': {red: 0x48, green: 0x3D, blue: 0x8B},
+ 'darkslategray': {red: 0x2F, green: 0x4F, blue: 0x4F},
+ 'darkslategrey': {red: 0x2F, green: 0x4F, blue: 0x4F},
+ 'darkturquoise': {red: 0x00, green: 0xCE, blue: 0xD1},
+ 'darkviolet': {red: 0x94, green: 0x00, blue: 0xD3},
+ 'deeppink': {red: 0xFF, green: 0x14, blue: 0x93},
+ 'deepskyblue': {red: 0x00, green: 0xBF, blue: 0xFF},
+ 'dimgray': {red: 0x69, green: 0x69, blue: 0x69},
+ 'dimgrey': {red: 0x69, green: 0x69, blue: 0x69},
+ 'dodgerblue': {red: 0x1E, green: 0x90, blue: 0xFF},
+ 'firebrick': {red: 0xB2, green: 0x22, blue: 0x22},
+ 'floralwhite': {red: 0xFF, green: 0xFA, blue: 0xF0},
+ 'forestgreen': {red: 0x22, green: 0x8B, blue: 0x22},
+ 'fuchsia': {red: 0xFF, green: 0x00, blue: 0xFF},
+ 'gainsboro': {red: 0xDC, green: 0xDC, blue: 0xDC},
+ 'ghostwhite': {red: 0xF8, green: 0xF8, blue: 0xFF},
+ 'gold': {red: 0xFF, green: 0xD7, blue: 0x00},
+ 'goldenrod': {red: 0xDA, green: 0xA5, blue: 0x20},
+ 'gray': {red: 0x80, green: 0x80, blue: 0x80},
+ 'green': {red: 0x00, green: 0x80, blue: 0x00},
+ 'greenyellow': {red: 0xAD, green: 0xFF, blue: 0x2F},
+ 'grey': {red: 0x80, green: 0x80, blue: 0x80},
+ 'honeydew': {red: 0xF0, green: 0xFF, blue: 0xF0},
+ 'hotpink': {red: 0xFF, green: 0x69, blue: 0xB4},
+ 'indianred': {red: 0xCD, green: 0x5C, blue: 0x5C},
+ 'indigo': {red: 0x4B, green: 0x00, blue: 0x82},
+ 'ivory': {red: 0xFF, green: 0xFF, blue: 0xF0},
+ 'khaki': {red: 0xF0, green: 0xE6, blue: 0x8C},
+ 'lavender': {red: 0xE6, green: 0xE6, blue: 0xFA},
+ 'lavenderblush': {red: 0xFF, green: 0xF0, blue: 0xF5},
+ 'lawngreen': {red: 0x7C, green: 0xFC, blue: 0x00},
+ 'lemonchiffon': {red: 0xFF, green: 0xFA, blue: 0xCD},
+ 'lightblue': {red: 0xAD, green: 0xD8, blue: 0xE6},
+ 'lightcoral': {red: 0xF0, green: 0x80, blue: 0x80},
+ 'lightcyan': {red: 0xE0, green: 0xFF, blue: 0xFF},
+ 'lightgoldenrodyellow': {red: 0xFA, green: 0xFA, blue: 0xD2},
+ 'lightgray': {red: 0xD3, green: 0xD3, blue: 0xD3},
+ 'lightgreen': {red: 0x90, green: 0xEE, blue: 0x90},
+ 'lightgrey': {red: 0xD3, green: 0xD3, blue: 0xD3},
+ 'lightpink': {red: 0xFF, green: 0xB6, blue: 0xC1},
+ 'lightsalmon': {red: 0xFF, green: 0xA0, blue: 0x7A},
+ 'lightseagreen': {red: 0x20, green: 0xB2, blue: 0xAA},
+ 'lightskyblue': {red: 0x87, green: 0xCE, blue: 0xFA},
+ 'lightslategray': {red: 0x77, green: 0x88, blue: 0x99},
+ 'lightslategrey': {red: 0x77, green: 0x88, blue: 0x99},
+ 'lightsteelblue': {red: 0xB0, green: 0xC4, blue: 0xDE},
+ 'lightyellow': {red: 0xFF, green: 0xFF, blue: 0xE0},
+ 'lime': {red: 0x00, green: 0xFF, blue: 0x00},
+ 'limegreen': {red: 0x32, green: 0xCD, blue: 0x32},
+ 'linen': {red: 0xFA, green: 0xF0, blue: 0xE6},
+ 'magenta': {red: 0xFF, green: 0x00, blue: 0xFF},
+ 'maroon': {red: 0x80, green: 0x00, blue: 0x00},
+ 'mediumaquamarine': {red: 0x66, green: 0xCD, blue: 0xAA},
+ 'mediumblue': {red: 0x00, green: 0x00, blue: 0xCD},
+ 'mediumorchid': {red: 0xBA, green: 0x55, blue: 0xD3},
+ 'mediumpurple': {red: 0x93, green: 0x70, blue: 0xDB},
+ 'mediumseagreen': {red: 0x3C, green: 0xB3, blue: 0x71},
+ 'mediumslateblue': {red: 0x7B, green: 0x68, blue: 0xEE},
+ 'mediumspringgreen': {red: 0x00, green: 0xFA, blue: 0x9A},
+ 'mediumturquoise': {red: 0x48, green: 0xD1, blue: 0xCC},
+ 'mediumvioletred': {red: 0xC7, green: 0x15, blue: 0x85},
+ 'midnightblue': {red: 0x19, green: 0x19, blue: 0x70},
+ 'mintcream': {red: 0xF5, green: 0xFF, blue: 0xFA},
+ 'mistyrose': {red: 0xFF, green: 0xE4, blue: 0xE1},
+ 'moccasin': {red: 0xFF, green: 0xE4, blue: 0xB5},
+ 'navajowhite': {red: 0xFF, green: 0xDE, blue: 0xAD},
+ 'navy': {red: 0x00, green: 0x00, blue: 0x80},
+ 'oldlace': {red: 0xFD, green: 0xF5, blue: 0xE6},
+ 'olive': {red: 0x80, green: 0x80, blue: 0x00},
+ 'olivedrab': {red: 0x6B, green: 0x8E, blue: 0x23},
+ 'orange': {red: 0xFF, green: 0xA5, blue: 0x00},
+ 'orangered': {red: 0xFF, green: 0x45, blue: 0x00},
+ 'orchid': {red: 0xDA, green: 0x70, blue: 0xD6},
+ 'palegoldenrod': {red: 0xEE, green: 0xE8, blue: 0xAA},
+ 'palegreen': {red: 0x98, green: 0xFB, blue: 0x98},
+ 'paleturquoise': {red: 0xAF, green: 0xEE, blue: 0xEE},
+ 'palevioletred': {red: 0xDB, green: 0x70, blue: 0x93},
+ 'papayawhip': {red: 0xFF, green: 0xEF, blue: 0xD5},
+ 'peachpuff': {red: 0xFF, green: 0xDA, blue: 0xB9},
+ 'peru': {red: 0xCD, green: 0x85, blue: 0x3F},
+ 'pink': {red: 0xFF, green: 0xC0, blue: 0xCB},
+ 'plum': {red: 0xDD, green: 0xA0, blue: 0xDD},
+ 'powderblue': {red: 0xB0, green: 0xE0, blue: 0xE6},
+ 'purple': {red: 0x80, green: 0x00, blue: 0x80},
+ 'red': {red: 0xFF, green: 0x00, blue: 0x00},
+ 'rosybrown': {red: 0xBC, green: 0x8F, blue: 0x8F},
+ 'royalblue': {red: 0x41, green: 0x69, blue: 0xE1},
+ 'saddlebrown': {red: 0x8B, green: 0x45, blue: 0x13},
+ 'salmon': {red: 0xFA, green: 0x80, blue: 0x72},
+ 'sandybrown': {red: 0xF4, green: 0xA4, blue: 0x60},
+ 'seagreen': {red: 0x2E, green: 0x8B, blue: 0x57},
+ 'seashell': {red: 0xFF, green: 0xF5, blue: 0xEE},
+ 'sienna': {red: 0xA0, green: 0x52, blue: 0x2D},
+ 'silver': {red: 0xC0, green: 0xC0, blue: 0xC0},
+ 'skyblue': {red: 0x87, green: 0xCE, blue: 0xEB},
+ 'slateblue': {red: 0x6A, green: 0x5A, blue: 0xCD},
+ 'slategray': {red: 0x70, green: 0x80, blue: 0x90},
+ 'slategrey': {red: 0x70, green: 0x80, blue: 0x90},
+ 'snow': {red: 0xFF, green: 0xFA, blue: 0xFA},
+ 'springgreen': {red: 0x00, green: 0xFF, blue: 0x7F},
+ 'steelblue': {red: 0x46, green: 0x82, blue: 0xB4},
+ 'tan': {red: 0xD2, green: 0xB4, blue: 0x8C},
+ 'teal': {red: 0x00, green: 0x80, blue: 0x80},
+ 'thistle': {red: 0xD8, green: 0xBF, blue: 0xD8},
+ 'tomato': {red: 0xFF, green: 0x63, blue: 0x47},
+ 'turquoise': {red: 0x40, green: 0xE0, blue: 0xD0},
+ 'violet': {red: 0xEE, green: 0x82, blue: 0xEE},
+ 'wheat': {red: 0xF5, green: 0xDE, blue: 0xB3},
+ 'white': {red: 0xFF, green: 0xFF, blue: 0xFF},
+ 'whitesmoke': {red: 0xF5, green: 0xF5, blue: 0xF5},
+ 'yellow': {red: 0xFF, green: 0xFF, blue: 0x00},
+ 'yellowgreen': {red: 0x9A, green: 0xCD, blue: 0x32},
+
+ 'transparent': {red: 0x00, green: 0x00, blue: 0x00, alpha: 0.0}
+};
+
+/**
+ * Color class allows cross-browser comparison of values, which can
+ * be returned from queryCommandValue in several formats:
+ * #ff00ff
+ * #f0f
+ * rgb(255, 0, 0)
+ * rgb(100%, 0%, 28%) // disabled for the time being (see below)
+ * rgba(127, 0, 64, 0.25)
+ * rgba(50%, 0%, 10%, 0.65) // disabled for the time being (see below)
+ * palegoldenrod
+ * transparent
+ *
+ * @constructor
+ * @param value {String} original value
+ */
+function Color(value) {
+ this.compare = function(other) {
+ if (!this.valid || !other.valid) {
+ return false;
+ }
+ if (this.alpha != other.alpha) {
+ return false;
+ }
+ if (this.alpha == 0.0) {
+ // both are fully transparent -> ignore the specific color information
+ return true;
+ }
+ // TODO(rolandsteiner): handle hsl/hsla values
+ return this.red == other.red && this.green == other.green && this.blue == other.blue;
+ }
+ this.parse = function(value) {
+ if (!value)
+ return false;
+ value = String(value).toLowerCase();
+ var match;
+ // '#' + 6 hex digits, e.g., #ff3300
+ match = value.match(/#([0-9a-f]{6})/i);
+ if (match) {
+ this.red = parseInt(match[1].substring(0, 2), 16);
+ this.green = parseInt(match[1].substring(2, 4), 16);
+ this.blue = parseInt(match[1].substring(4, 6), 16);
+ this.alpha = 1.0;
+ return true;
+ }
+ // '#' + 3 hex digits, e.g., #f30
+ match = value.match(/#([0-9a-f]{3})/i);
+ if (match) {
+ this.red = parseInt(match[1].substring(0, 1), 16) * 16;
+ this.green = parseInt(match[1].substring(1, 2), 16) * 16;
+ this.blue = parseInt(match[1].substring(2, 3), 16) * 16;
+ this.alpha = 1.0;
+ return true;
+ }
+ // a color name, e.g., springgreen
+ match = colorChart[value];
+ if (match) {
+ this.red = match.red;
+ this.green = match.green;
+ this.blue = match.blue;
+ this.alpha = (match.alpha === undefined) ? 1.0 : match.alpha;
+ return true;
+ }
+ // rgb(r, g, b), e.g., rgb(128, 12, 217)
+ match = value.match(/rgb\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/i);
+ if (match) {
+ this.red = Number(match[1]);
+ this.green = Number(match[2]);
+ this.blue = Number(match[3]);
+ this.alpha = 1.0;
+ return true;
+ }
+ // rgb(r%, g%, b%), e.g., rgb(100%, 0%, 50%)
+// Commented out for the time being, since it seems likely that the resulting
+// decimal values will create false negatives when compared with non-% values.
+//
+// => store as separate percent values and do exact matching when compared with % values
+// and fuzzy matching when compared with non-% values?
+//
+// match = value.match(/rgb\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*\)/i);
+// if (match) {
+// this.red = Number(match[1]) * 255 / 100;
+// this.green = Number(match[2]) * 255 / 100;
+// this.blue = Number(match[3]) * 255 / 100;
+// this.alpha = 1.0;
+// return true;
+// }
+ // rgba(r, g, b, a), e.g., rgb(128, 12, 217, 0.2)
+ match = value.match(/rgba\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
+ if (match) {
+ this.red = Number(match[1]);
+ this.green = Number(match[2]);
+ this.blue = Number(match[3]);
+ this.alpha = Number(match[4]);
+ return true;
+ }
+ // rgba(r%, g%, b%, a), e.g., rgb(100%, 0%, 50%, 0.3)
+// Commented out for the time being (cf. rgb() matching above)
+// match = value.match(/rgba\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
+// if (match) {
+// this.red = Number(match[1]) * 255 / 100;
+// this.green = Number(match[2]) * 255 / 100;
+// this.blue = Number(match[3]) * 255 / 100;
+// this.alpha = Number(match[4]);
+// return true;
+// }
+ // TODO(rolandsteiner): handle "hsl(h, s, l)" and "hsla(h, s, l, a)" notation
+ return false;
+ }
+ this.toString = function() {
+ return this.valid ? this.red + ',' + this.green + ',' + this.blue : '(invalid)';
+ }
+ this.toHexString = function() {
+ if (!this.valid)
+ return '(invalid)';
+ return ((this.red < 16) ? '0' : '') + this.red.toString(16) +
+ ((this.green < 16) ? '0' : '') + this.green.toString(16) +
+ ((this.blue < 16) ? '0' : '') + this.blue.toString(16);
+ }
+ 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.
+ *
+ * @constructor
+ * @param value {String} original value
+ */
+function FontSize(value) {
+ this.parse = function(str) {
+ if (!str)
+ this.valid = false;
+ var match;
+ if (match = String(str).match(/([0-9]+)px/)) {
+ var px = Number(match[1]);
+ if (px <= 0 || px > 47)
+ return false;
+ 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 {
+ this.size = '7';
+ }
+ return true;
+ }
+ if (match = String(str).match(/([+-][0-9]+)/)) {
+ this.size = match[1];
+ return this.size >= 1 && this.size <= 7;
+ }
+ if (Number(str)) {
+ this.size = String(Number(str));
+ return this.size >= 1 && this.size <= 7;
+ }
+ switch (str) {
+ case 'x-small':
+ this.size = '1';
+ return true;
+ case 'small':
+ this.size = '2';
+ return true;
+ case 'medium':
+ this.size = '3';
+ return true;
+ case 'large':
+ this.size = '4';
+ return true;
+ case 'x-large':
+ this.size = '5';
+ return true;
+ case 'xx-large':
+ this.size = '6';
+ return true;
+ case 'xxx-large':
+ this.size = '7';
+ return true;
+ case '-webkit-xxx-large':
+ this.size = '7';
+ return true;
+ case 'larger':
+ this.size = '+1';
+ return true;
+ case 'smaller':
+ this.size = '-1';
+ return true;
+ }
+ return false;
+ }
+ this.compare = function(other) {
+ return this.valid && other.valid && this.size === other.size;
+ }
+ this.toString = function() {
+ return this.valid ? this.size : '(invalid)';
+ }
+ this.valid = this.parse(value);
+}
+
+/**
+ * Utility class for converting & canonicalizing font names.
+ *
+ * @constructor
+ * @param value {String} original value
+ */
+function FontName(value) {
+ this.parse = function(str) {
+ if (!str)
+ return false;
+ str = String(str).toLowerCase();
+ switch (str) {
+ case 'arial new':
+ this.fontname = 'arial';
+ return true;
+ case 'courier new':
+ this.fontname = 'courier';
+ return true;
+ case 'times new':
+ case 'times roman':
+ case 'times new roman':
+ this.fontname = 'times';
+ return true;
+ }
+ this.fontname = value;
+ return true;
+ }
+ this.compare = function(other) {
+ return this.valid && other.valid && this.fontname === other.fontname;
+ }
+ this.toString = function() {
+ return this.valid ? this.fontname : '(invalid)';
+ }
+ this.valid = this.parse(value);
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/variables.js b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/variables.js
new file mode 100644
index 0000000000..cdc6f1e929
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/static/js/variables.js
@@ -0,0 +1,227 @@
+/**
+ * @fileoverview
+ * Common constants and variables used in the RTE test suite.
+ *
+ * Copyright 2010 Google Inc.
+ *
+ * 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.
+ *
+ * @version 0.1
+ * @author rolandsteiner@google.com
+ */
+
+// Constant for indicating a test setup is unsupported or incorrect
+// (threw exception).
+var INTERNAL_ERR = 'INTERNAL ERROR: ';
+var SETUP_EXCEPTION = 'SETUP EXCEPTION: ';
+var EXECUTION_EXCEPTION = 'EXECUTION EXCEPTION: ';
+var VERIFICATION_EXCEPTION = 'VERIFICATION EXCEPTION: ';
+
+var SETUP_CONTAINER = 'WHEN INITIALIZING TEST CONTAINER';
+var SETUP_BAD_SELECTION_SPEC = 'BAD SELECTION SPECIFICATION IN TEST OR EXPECTATION STRING';
+var SETUP_HTML = 'WHEN SETTING TEST HTML';
+var SETUP_SELECTION = 'WHEN SETTING SELECTION';
+var SETUP_NOCOMMAND = 'NO COMMAND, GENERAL FUNCTION OR QUERY FUNCTION GIVEN';
+var HTML_COMPARISON = 'WHEN COMPARING OUTPUT HTML';
+
+// Exceptiona to be thrown on unsupported selection operations
+var SELMODIFY_UNSUPPORTED = 'UNSUPPORTED selection.modify()';
+var SELALLCHILDREN_UNSUPPORTED = 'UNSUPPORTED selection.selectAllChildren()';
+
+// Output string for unsupported functions
+// (returning bool 'false' as opposed to throwing an exception)
+var UNSUPPORTED = '<i>false</i> (UNSUPPORTED)';
+
+// HTML comparison result contants.
+var VALRESULT_NOT_RUN = 0; // test hasn't been run yet
+var VALRESULT_SETUP_EXCEPTION = 1;
+var VALRESULT_EXECUTION_EXCEPTION = 2;
+var VALRESULT_VERIFICATION_EXCEPTION = 3;
+var VALRESULT_UNSUPPORTED = 4;
+var VALRESULT_CANARY = 5; // HTML changes bled into the canary.
+var VALRESULT_DIFF = 6;
+var VALRESULT_ACCEPT = 7; // HTML technically correct, but not ideal.
+var VALRESULT_EQUAL = 8;
+
+var VALOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
+ {css: 'grey', output: '???', title: 'The test has not been run yet.'}, // VALRESULT_NOT_RUN
+ {css: 'exception', output: 'EXC.', title: 'Exception was thrown during setup.'}, // VALRESULT_SETUP_EXCEPTION
+ {css: 'exception', output: 'EXC.', title: 'Exception was thrown during execution.'}, // VALRESULT_EXECUTION_EXCEPTION
+ {css: 'exception', output: 'EXC.', title: 'Exception was thrown during result verification.'}, // VALRESULT_VERIFICATION_EXCEPTION
+ {css: 'unsupported', output: 'UNS.', title: 'Unsupported command or value'}, // VALRESULT_UNSUPPORTED
+ {css: 'canary', output: 'CANARY', title: 'The command affected the contentEditable root element, or outside HTML.'}, // VALRESULT_CANARY
+ {css: 'fail', output: 'FAIL', title: 'The result differs from the expectation(s).'}, // VALRESULT_DIFF
+ {css: 'accept', output: 'ACC.', title: 'The result is technically correct, but sub-optimal.'}, // VALRESULT_ACCEPT
+ {css: 'pass', output: 'PASS', title: 'The test result matches the expectation.'} // VALRESULT_EQUAL
+]
+
+// Selection comparison result contants.
+var SELRESULT_NOT_RUN = 0; // test hasn't been run yet
+var SELRESULT_CANARY = 1; // selection escapes the contentEditable element
+var SELRESULT_DIFF = 2;
+var SELRESULT_NA = 3;
+var SELRESULT_ACCEPT = 4; // Selection is acceptable, but not ideal.
+var SELRESULT_EQUAL = 5;
+
+var SELOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
+ {css: 'grey', output: 'grey', title: 'The test has not been run yet.'}, // SELRESULT_NOT_RUN
+ {css: 'canary', output: 'CANARY', title: 'The selection escaped the contentEditable boundary!'}, // SELRESULT_CANARY
+ {css: 'fail', output: 'FAIL', title: 'The selection differs from the expectation(s).'}, // SELRESULT_DIFF
+ {css: 'na', output: 'N/A', title: 'The correctness of the selection could not be verified.'}, // SELRESULT_NA
+ {css: 'accept', output: 'ACC.', title: 'The selection is technically correct, but sub-optimal.'}, // SELRESULT_ACCEPT
+ {css: 'pass', output: 'PASS', title: 'The selection matches the expectation.'} // SELRESULT_EQUAL
+];
+
+// RegExp for selection markers
+var SELECTION_MARKERS = /[\[\]\{\}\|\^]/;
+
+// Special attributes used to mark selections within elements that otherwise
+// have no children. Important: attribute name MUST be lower case!
+var ATTRNAME_SEL_START = 'bsselstart';
+var ATTRNAME_SEL_END = 'bsselend';
+
+// DOM node type constants.
+var DOM_NODE_TYPE_ELEMENT = 1;
+var DOM_NODE_TYPE_TEXT = 3;
+var DOM_NODE_TYPE_COMMENT = 8;
+
+// Test parameter names
+var PARAM_DESCRIPTION = 'desc';
+var PARAM_PAD = 'pad';
+var PARAM_EXECCOMMAND = 'command';
+var PARAM_FUNCTION = 'function';
+var PARAM_QUERYCOMMANDSUPPORTED = 'qcsupported';
+var PARAM_QUERYCOMMANDENABLED = 'qcenabled';
+var PARAM_QUERYCOMMANDINDETERM = 'qcindeterm';
+var PARAM_QUERYCOMMANDSTATE = 'qcstate';
+var PARAM_QUERYCOMMANDVALUE = 'qcvalue';
+var PARAM_VALUE = 'value';
+var PARAM_EXPECTED = 'expected';
+var PARAM_EXPECTED_OUTER = 'expOuter';
+var PARAM_ACCEPT = 'accept';
+var PARAM_ACCEPT_OUTER = 'accOuter';
+var PARAM_CHECK_ATTRIBUTES = 'checkAttrs';
+var PARAM_CHECK_STYLE = 'checkStyle';
+var PARAM_CHECK_CLASS = 'checkClass';
+var PARAM_CHECK_ID = 'checkID';
+var PARAM_STYLE_WITH_CSS = 'styleWithCSS';
+
+// ID suffixes for the output columns
+var IDOUT_TR = '_:TR:'; // per container
+var IDOUT_TESTID = '_:tid'; // per test
+var IDOUT_COMMAND = '_:cmd'; // per test
+var IDOUT_VALUE = '_:val'; // per test
+var IDOUT_CHECKATTRS = '_:att'; // per test
+var IDOUT_CHECKSTYLE = '_:sty'; // per test
+var IDOUT_CONTAINER = '_:cnt:'; // per container
+var IDOUT_STATUSVAL = '_:sta:'; // per container
+var IDOUT_STATUSSEL = '_:sel:'; // per container
+var IDOUT_PAD = '_:pad'; // per test
+var IDOUT_EXPECTED = '_:exp'; // per test
+var IDOUT_ACTUAL = '_:act:'; // per container
+
+// Output strings to use for yes/no/NA
+var OUTSTR_YES = '&#x25CF;';
+var OUTSTR_NO = '&#x25CB;';
+var OUTSTR_NA = '-';
+
+// Tags at the start of HTML strings where they were taken from
+var HTMLTAG_BODY = 'B:';
+var HTMLTAG_OUTER = 'O:';
+var HTMLTAG_INNER = 'I:';
+
+// What to use for the canary
+var CANARY = 'CAN<br>ARY';
+
+// Containers for tests, and their associated DOM elements:
+// iframe, win, doc, body, elem
+var containers = [
+ { id: 'dM',
+ iframe: null,
+ win: null,
+ doc: null,
+ body: null,
+ editor: null,
+ tagOpen: '<body>',
+ tagClose: '</body>',
+ editorID: null,
+ canary: '',
+ },
+ { id: 'body',
+ iframe: null,
+ win: null,
+ doc: null,
+ body: null,
+ editor: null,
+ tagOpen: '<body contenteditable="true">',
+ tagClose: '</body>',
+ editorID: null,
+ canary: ''
+ },
+ { id: 'div',
+ iframe: null,
+ win: null,
+ doc: null,
+ body: null,
+ editor: null,
+ tagOpen: '<div contenteditable="true" id="editor-div">',
+ tagClose: '</div>',
+ editorID: 'editor-div',
+ canary: CANARY
+ }
+];
+
+// Helper variables to use in test functions
+var win = null; // window object to use for test functions
+var doc = null; // document object to use for test functions
+var body = null; // The <body> element of the current document
+var editor = null; // The contentEditable element (i.e., the <body> or <div>)
+var sel = null; // The current selection after the pad is set up
+
+// Canonicalization emit flags for various purposes
+var emitFlagsForCanary = {
+ emitAttrs: true,
+ emitStyle: true,
+ emitClass: true,
+ emitID: true,
+ lowercase: true,
+ canonicalizeUnits: true
+};
+var emitFlagsForOutput = {
+ emitAttrs: true,
+ emitStyle: true,
+ emitClass: true,
+ emitID: true,
+ lowercase: false,
+ canonicalizeUnits: false
+};
+
+// Shades of output colors
+var colorShades = ['Lo', 'Hi'];
+
+// Classes of tests
+var testClassIDs = ['Finalized', 'RFC', 'Proposed'];
+var testClassCount = testClassIDs.length;
+
+// Dictionary storing the detailed test results.
+var results = {
+ count: 0,
+ score: 0
+};
+
+// Results - populated by the fillResults() function.
+var beacon = [];
+
+// "compatibility" between Python and JS for test quines
+var True = true;
+var False = false;
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/output.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/output.html
new file mode 100644
index 0000000000..62d917d697
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/output.html
@@ -0,0 +1,138 @@
+<!-- Legend -->
+<TABLE CLASS="legend framed">
+ <THEAD>
+ <TR><TH COLSPAN=3 CLASS="legendHdr">Result Description</TH></TR>
+ <TR><TH>Status</TH><TH ALIGN="LEFT">Meaning</TH><TH ALIGN="LEFT">Explanation</TH><TH>Scoring</TH></TR>
+ </THEAD>
+ <TBODY>
+ <TR CLASS="lo"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Passed</TD><TD CLASS="legend" ROWSPAN=2>The result matches the expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="pass">PASS (+1)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Acceptable</TD><TD CLASS="legend" ROWSPAN=2>The result is technically correct, but not ideal (too verbose, deprecated usage, etc.) - for informative purposes only.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Failure</TD><TD CLASS="legend" ROWSPAN=2>The result does not match any given expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Canary</TD><TD CLASS="legend" ROWSPAN=2>The result changes HTML other than children of the contentEditable element.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Unsupported</TD><TD CLASS="legend" ROWSPAN=2>The specific function or value is unsupported (returned boolean 'false').</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Exception</TD><TD CLASS="legend" ROWSPAN=2>An unexpected exception was thrown during the execution of the test.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD></TR>
+ <TR CLASS="lo"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Not Applicable</TD><TD CLASS="legend" ROWSPAN=2>The selection could not be tested, because the tested function failed to return a known result.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
+ <TR CLASS="hi"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD></TR>
+ </TBODY>
+</TABLE>
+<TABLE CLASS="legend framed">
+ <THEAD>
+ <TR><TH COLSPAN=2 CLASS="legendHdr">Selection and Result Display</TH></TR>
+ <TR><TH>Character</TH><TH ALIGN="LEFT">Explanation</TH></TR>
+ </THEAD>
+ <TBODY>
+ <TR><TD CLASS="sel" ALIGN="CENTER">[</TD><TD>Start of selection - selection point is within a text node.</TD></TR>
+ <TR><TD CLASS="sel" ALIGN="CENTER">]</TD><TD>End of selection - selection point is within a text node.</TD></TR>
+ <TR><TD CLASS="sel" ALIGN="CENTER">^</TD><TD>Collapsed selection - selection point is within a text node.</TD></TR>
+ <TR><TD COLSPAN=2>&nbsp;</TD></TR>
+ <TR><TD CLASS="sel" ALIGN="CENTER">{</TD><TD>Start of selection - selection point is within an element node.</TD></TR>
+ <TR><TD CLASS="sel" ALIGN="CENTER">}</TD><TD>End of selection - selection point is within an element node.</TD></TR>
+ <TR><TD CLASS="sel" ALIGN="CENTER">|</TD><TD>Collapsed selection - selection point is within an element node.</TD></TR>
+ <TR><TD COLSPAN=2>&nbsp;</TD></TR>
+ <TR><TD ALIGN="CENTER"><SPAN CLASS="fade">foo</SPAN></TD><TD>Greyed text indicates parts of the output that are ignored for the purposes of checking the result.</TD></TR>
+ <TR><TD ALIGN="CENTER"><SPAN CLASS="txt">foo</SPAN></TD><TD>Grey border indicates extent of text nodes in the result.</TD></TR>
+ </TBODY>
+</TABLE>
+<!-- progress meter -->
+<HR ID="divider">
+<H1>Running Test Suites: {% for s in suites %}<A HREF="#{{ s.id }}" ID="{{ s.id }}-progress" STYLE="color: #eeeeee">{{ s.id }}</A> {% endfor %}<SPAN ID="done">&nbsp;</SPAN></H1>
+<HR>
+<!-- main output -->
+{% for s in suites %}
+ <H1 ID="{{ s.id }}"><A NAME="{{ s.id }}" HREF="#{{ s.id }}">{{ s.id }}</A> - {{ s.caption }}:
+ <SPAN ID="{{ s.id }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
+ {% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
+ (Selection: <SPAN ID="{{ s.id }}-selscore">?/?</SPAN>)
+ {% endifnotequal %}{% endifnotequal %}
+ (time: <SPAN ID="{{ s.id }}-time">?</SPAN>&nbsp;ms)
+ </H1>
+ {% if s.comment %}
+ <DIV CLASS="comment">{{ s.comment|safe }}</DIV>
+ {% endif %}
+ {% for cls in classes %}{% for pk, pv in s.items %}{% ifequal pk cls %}
+ <H2 ID="{{ s.id }}-{{ cls }}"><A NAME="{{ s.id }}-{{ cls }}" HREF="#{{ s.id }}-{{ cls }}">{{ cls }} Tests</A>:
+ <SPAN ID="{{ s.id }}-{{ cls }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
+ {% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
+ (Selection: <SPAN ID="{{ s.id }}-{{ cls }}-selscore">?/?</SPAN>)
+ {% endifnotequal %}{% endifnotequal %}
+ </H2>
+ <TABLE WIDTH=100%>
+ <THEAD>
+ <TR>
+ <TH TITLE="Unique ID of the test" ALIGN="LEFT">ID</TH>
+ <TH TITLE="Command or function used in the test" ALIGN="LEFT">Command</TH>
+ <TH TITLE="Value field for commands" ALIGN="LEFT">Value</TH>
+ {% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
+ <TH TITLE="check Atributes?">A</TH>
+ <TH TITLE="check Style">S</TH>
+ {% endifnotequal %}{% endifnotequal %}
+ <TH TITLE="Testing HTML Element">Env.</TH>
+ {% ifnotequal s.id.0 "S" %}{% comment %} Don't output HTML status column for selection tests. {% endcomment %}
+ <TH TITLE="State of the test">Status</TH>
+ {% endifnotequal %}
+ {% ifnotequal s.id.0 "Q" %}{% comment %} Don't output selection result column for "queryCommand..." tests. {% endcomment %}
+ <TH TITLE="State of the test regarding the selection">Selection</TH>
+ {% endifnotequal %}
+ <TH TITLE="Initial HTML and selection" ALIGN="LEFT">Initial</TH>
+ <TH TITLE="Expected HTML and selection" ALIGN="LEFT">Expected</TH>
+ <TH TITLE="Actual result HTML and selection" ALIGN="LEFT">Actual (lower case, canonicalized, selection marks)</TH>
+ <TH TITLE="Short description of the test" ALIGN="LEFT">Description</TH>
+ </TR>
+ </THEAD>
+ <TBODY>
+ {% for g in pv %}{% for t in g.tests %}
+ <TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:dM" CLASS="{% cycle 'lo' 'lo' 'lo' 'hi' 'hi' 'hi' as shade %}">
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:tid"><A CLASS="idLabel" NAME="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}" HREF="#{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}">{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}</A></TD>
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cmd">&nbsp;</TD>
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:val">&nbsp;</TD>
+ {% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:att" ALIGN="CENTER">&nbsp;</TD>
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sty" ALIGN="CENTER">&nbsp;</TD>
+ {% endifnotequal %}{% endifnotequal %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:dM" TITLE="designMode=&quot;on&quot;" ALIGN="CENTER">dM</TD>
+ {% ifnotequal s.id.0 "S" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:dM" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ {% ifnotequal s.id.0 "Q" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:dM" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:pad">&nbsp;</TD>
+ <TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:exp">&nbsp;</TD>
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:dM"><I>Processing...</I></TD>
+ <TD ROWSPAN=3>{{ t.desc|default:"&nbsp;" }}</TD>
+ </TR>
+ <TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:body" CLASS="{% cycle shade %}">
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:body" TITLE="&lt;body contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">body</TD>
+ {% ifnotequal s.id.0 "S" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:body" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ {% ifnotequal s.id.0 "Q" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:body" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:body"><I>Processing...</I></TD>
+ </TR>
+ <TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:div" CLASS="{% cycle shade %}">
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:div" TITLE="&lt;div contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">div</TD>
+ {% ifnotequal s.id.0 "S" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:div" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ {% ifnotequal s.id.0 "Q" %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:div" ALIGN="CENTER">NONE</TD>
+ {% endifnotequal %}
+ <TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:div"><I>Processing...</I></TD>
+ </TR>
+ {% endfor %}{% endfor %}
+ </TBODY>
+ </TABLE>
+ {% endifequal %}{% endfor %}{% endfor %}
+{% endfor %}
+
+
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/richtext2.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/richtext2.html
new file mode 100644
index 0000000000..98de8796da
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/templates/richtext2.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
+ <title>New Rich Text Tests</title>
+
+ <link rel="stylesheet" href="static/common.css" type="text/css">
+ <link rel="stylesheet" href="static/editable.css" type="text/css">
+
+ <!-- utility scripts -->
+ <script src="static/js/variables.js"></script>
+
+ <script src="static/js/canonicalize.js"></script>
+ <script src="static/js/compare.js"></script>
+ <script src="static/js/output.js"></script>
+ <script src="static/js/pad.js"></script>
+ <script src="static/js/range.js"></script>
+ <script src="static/js/units.js"></script>
+
+ <script src="static/js/run.js"></script>
+
+ <!-- new tests -->
+ <script type="text/javascript">
+ {% autoescape off %}
+
+ var commonIDPrefix = '{{ commonIDPrefix }}';
+ {% for s in suites %}
+ var {{ s.id }}_TESTS = {{ s }};
+ {% endfor %}
+
+ /**
+ * Stuff to do after all tests are run:
+ * - write a nice "DONE!" at the end of the progress meter
+ * - beacon the results
+ * - remove the testing <iframe>s
+ */
+ function finish() {
+ var span = document.getElementById('done');
+ if (span)
+ span.innerHTML = ' ... DONE!';
+
+ fillResults();
+ parent.sendScore(beacon, categoryTotals);
+
+ cleanUp();
+ }
+
+ /**
+ * Run every individual suite, with a a brief timeout in between
+ * to allow for screen updates.
+ */
+{% for s in suites %}
+ {% if not forloop.first %}
+ setTimeout("runSuite{{ s.id }}()", 100);
+ }
+ {% endif %}
+
+ function runSuite{{ s.id }}() {
+ runAndOutputTestSuite({{ s.id }}_TESTS);
+{% endfor %}
+ finish();
+ }
+
+ /**
+ * Runs all tests in all suites.
+ */
+ function doRunTests() {
+ initVariables();
+ initEditorDocs();
+
+ // Start with the first test suite
+ runSuite{{ suites.0.id }}();
+ }
+
+ /**
+ * Runs after allowing for some time to have everything loaded
+ * (aka. horrible IE9 kludge)
+ */
+ function runTests() {
+ setTimeout("doRunTests()", 1500);
+ }
+
+ /**
+ * Removes the <iframe>s after all tests are finished
+ */
+ function cleanUp() {
+ var e = document.getElementById('iframe-dM');
+ e.parentNode.removeChild(e);
+ e = document.getElementById('iframe-body');
+ e.parentNode.removeChild(e);
+ e = document.getElementById('iframe-div');
+ e.parentNode.removeChild(e);
+ }
+ {% endautoescape %}
+ </script>
+</head>
+
+<body onload="runTests()">
+ {% include "richtext2/templates/output.html" %}
+ <hr>
+ <iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
+ <iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
+ <iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/__init__.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/__init__.py
new file mode 100644
index 0000000000..a1f5279ad5
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/__init__.py
@@ -0,0 +1,17 @@
+__all__ = [
+ 'apply',
+ 'applyCSS',
+ 'change',
+ 'changeCSS',
+ 'delete',
+ 'forwarddelete',
+ 'insert',
+ 'queryEnabled',
+ 'queryIndeterm',
+ 'queryState',
+ 'querySupported',
+ 'queryValue',
+ 'selection',
+ 'unapply',
+ 'unapplyCSS'
+] \ No newline at end of file
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/apply.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/apply.py
new file mode 100644
index 0000000000..3eb465c84c
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/apply.py
@@ -0,0 +1,364 @@
+
+APPLY_TESTS = {
+ 'id': 'A',
+ 'caption': 'Apply Formatting Tests',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[HTML5] bold',
+ 'command': 'bold',
+ 'tests': [
+ { 'id': 'B_TEXT-1_SI',
+ 'rte1-id': 'a-bold-0',
+ 'desc': 'Bold selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<b>[bar]</b>baz',
+ 'foo<strong>[bar]</strong>baz' ] },
+
+ { 'id': 'B_TEXT-1_SIR',
+ 'desc': 'Bold reversed selection',
+ 'pad': 'foo]bar[baz',
+ 'expected': [ 'foo<b>[bar]</b>baz',
+ 'foo<strong>[bar]</strong>baz' ] },
+
+ { 'id': 'B_I-1_SL',
+ 'desc': 'Bold selection, partially including italic',
+ 'pad': 'foo[bar<i>baz]qoz</i>quz',
+ 'expected': [ 'foo<b>[bar</b><i><b>baz]</b>qoz</i>quz',
+ 'foo<b>[bar<i>baz]</i></b><i>qoz</i>quz',
+ 'foo<strong>[bar</strong><i><strong>baz]</strong>qoz</i>quz',
+ 'foo<strong>[bar<i>baz]</i></strong><i>qoz</i>quz' ] }
+ ]
+ },
+
+ { 'desc': '[HTML5] italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_TEXT-1_SI',
+ 'rte1-id': 'a-italic-0',
+ 'desc': 'Italicize selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<i>[bar]</i>baz',
+ 'foo<em>[bar]</em>baz' ] }
+ ]
+ },
+
+ { 'desc': '[HTML5] underline',
+ 'command': 'underline',
+ 'tests': [
+ { 'id': 'U_TEXT-1_SI',
+ 'rte1-id': 'a-underline-0',
+ 'desc': 'Underline selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<u>[bar]</u>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] strikethrough',
+ 'command': 'strikethrough',
+ 'tests': [
+ { 'id': 'S_TEXT-1_SI',
+ 'rte1-id': 'a-strikethrough-0',
+ 'desc': 'Strike-through selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<s>[bar]</s>baz',
+ 'foo<strike>[bar]</strike>baz',
+ 'foo<del>[bar]</del>baz' ] }
+ ]
+ },
+
+ { 'desc': '[HTML5] subscript',
+ 'command': 'subscript',
+ 'tests': [
+ { 'id': 'SUB_TEXT-1_SI',
+ 'rte1-id': 'a-subscript-0',
+ 'desc': 'Change selection to subscript',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<sub>[bar]</sub>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] superscript',
+ 'command': 'superscript',
+ 'tests': [
+ { 'id': 'SUP_TEXT-1_SI',
+ 'rte1-id': 'a-superscript-0',
+ 'desc': 'Change selection to superscript',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<sup>[bar]</sup>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] createlink',
+ 'command': 'createlink',
+ 'tests': [
+ { 'id': 'CL:url_TEXT-1_SI',
+ 'rte1-id': 'a-createlink-0',
+ 'desc': 'create a link around the selection',
+ 'value': '#foo',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<a href="#foo">[bar]</a>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] formatBlock',
+ 'command': 'formatblock',
+ 'tests': [
+ { 'id': 'FB:H1_TEXT-1_SI',
+ 'rte1-id': 'a-formatblock-0',
+ 'desc': 'format the selection into a block: use <h1>',
+ 'value': 'h1',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<h1>foo[bar]baz</h1>' },
+
+ { 'id': 'FB:P_TEXT-1_SI',
+ 'desc': 'format the selection into a block: use <p>',
+ 'value': 'p',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<p>foo[bar]baz</p>' },
+
+ { 'id': 'FB:PRE_TEXT-1_SI',
+ 'desc': 'format the selection into a block: use <pre>',
+ 'value': 'pre',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<pre>foo[bar]baz</pre>' },
+
+ { 'id': 'FB:ADDRESS_TEXT-1_SI',
+ 'desc': 'format the selection into a block: use <address>',
+ 'value': 'address',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<address>foo[bar]baz</address>' },
+
+ { 'id': 'FB:BQ_TEXT-1_SI',
+ 'desc': 'format the selection into a block: use <blockquote>',
+ 'value': 'blockquote',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<blockquote>foo[bar]baz</blockquote>' },
+
+ { 'id': 'FB:BQ_BR.BR-1_SM',
+ 'desc': 'format a multi-line selection into a block: use <blockquote>',
+ 'command': 'formatblock',
+ 'value': 'blockquote',
+ 'pad': 'fo[o<br>bar<br>b]az',
+ 'expected': '<blockquote>fo[o<br>bar<br>b]az</blockquote>' }
+ ]
+ },
+
+
+ { 'desc': '[MIDAS] backcolor',
+ 'command': 'backcolor',
+ 'tests': [
+ { 'id': 'BC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-backcolor-0',
+ 'desc': 'Change background color (note: no non-CSS variant available)',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
+ 'foo<font style="background-color: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] forecolor',
+ 'command': 'forecolor',
+ 'tests': [
+ { 'id': 'FC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-forecolor-0',
+ 'desc': 'Change the text color',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<font color="blue">[bar]</font>baz' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] hilitecolor',
+ 'command': 'hilitecolor',
+ 'tests': [
+ { 'id': 'HC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-hilitecolor-0',
+ 'desc': 'Change the hilite color',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
+ 'foo<font style="background-color: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontname',
+ 'command': 'fontname',
+ 'tests': [
+ { 'id': 'FN:a_TEXT-1_SI',
+ 'rte1-id': 'a-fontname-0',
+ 'desc': 'Change the font name',
+ 'value': 'arial',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<font face="arial">[bar]</font>baz' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontsize',
+ 'command': 'fontsize',
+ 'tests': [
+ { 'id': 'FS:2_TEXT-1_SI',
+ 'rte1-id': 'a-fontsize-0',
+ 'desc': 'Change the font size to "2"',
+ 'value': '2',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<font size="2">[bar]</font>baz' },
+
+ { 'id': 'FS:18px_TEXT-1_SI',
+ 'desc': 'Change the font size to "18px"',
+ 'value': '18px',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<font size="18px">[bar]</font>baz' },
+
+ { 'id': 'FS:large_TEXT-1_SI',
+ 'desc': 'Change the font size to "large"',
+ 'value': 'large',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<font size="large">[bar]</font>baz' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] increasefontsize',
+ 'command': 'increasefontsize',
+ 'tests': [
+ { 'id': 'INCFS:2_TEXT-1_SI',
+ 'desc': 'Decrease the font size (to small)',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<font size="4">[bar]</font>baz',
+ 'foo<font size="+1">[bar]</font>baz',
+ 'foo<big>[bar]</big>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] decreasefontsize',
+ 'command': 'decreasefontsize',
+ 'tests': [
+ { 'id': 'DECFS:2_TEXT-1_SI',
+ 'rte1-id': 'a-decreasefontsize-0',
+ 'desc': 'Decrease the font size (to small)',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<font size="2">[bar]</font>baz',
+ 'foo<font size="-1">[bar]</font>baz',
+ 'foo<small>[bar]</small>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] indent (note: accept the de-facto standard indent of 40px)',
+ 'command': 'indent',
+ 'tests': [
+ { 'id': 'IND_TEXT-1_SI',
+ 'rte1-id': 'a-indent-0',
+ 'desc': 'Indent the text (accept the de-facto standard of 40px indent)',
+ 'pad': 'foo[bar]baz',
+ 'checkAttrs': False,
+ 'expected': [ '<blockquote>foo[bar]baz</blockquote>',
+ '<div style="margin-left: 40px">foo[bar]baz</div>' ],
+ 'div': {
+ 'accOuter': '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] outdent (-> unapply tests)',
+ 'command': 'outdent',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifycenter',
+ 'command': 'justifycenter',
+ 'tests': [
+ { 'id': 'JC_TEXT-1_SC',
+ 'rte1-id': 'a-justifycenter-0',
+ 'desc': 'justify the text centrally',
+ 'pad': 'foo^bar',
+ 'expected': [ '<center>foo^bar</center>',
+ '<p align="center">foo^bar</p>',
+ '<p align="middle">foo^bar</p>',
+ '<div align="center">foo^bar</div>',
+ '<div align="middle">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': [ '<div align="center" contenteditable="true">foo^bar</div>',
+ '<div align="middle" contenteditable="true">foo^bar</div>' ] } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyfull',
+ 'command': 'justifyfull',
+ 'tests': [
+ { 'id': 'JF_TEXT-1_SC',
+ 'rte1-id': 'a-justifyfull-0',
+ 'desc': 'justify the text fully',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p align="justify">foo^bar</p>',
+ '<div align="justify">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div align="justify" contenteditable="true">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyleft',
+ 'command': 'justifyleft',
+ 'tests': [
+ { 'id': 'JL_TEXT-1_SC',
+ 'rte1-id': 'a-justifyleft-0',
+ 'desc': 'justify the text left',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p align="left">foo^bar</p>',
+ '<div align="left">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div align="left" contenteditable="true">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyright',
+ 'command': 'justifyright',
+ 'tests': [
+ { 'id': 'JR_TEXT-1_SC',
+ 'rte1-id': 'a-justifyright-0',
+ 'desc': 'justify the text right',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p align="right">foo^bar</p>',
+ '<div align="right">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div align="right" contenteditable="true">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] heading',
+ 'command': 'heading',
+ 'tests': [
+ { 'id': 'H:H1_TEXT-1_SC',
+ 'desc': 'create a heading from the paragraph that contains the selection',
+ 'value': 'h1',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<h1>foo[bar]baz</h1>' }
+ ]
+ },
+
+
+ { 'desc': '[Other] createbookmark',
+ 'command': 'createbookmark',
+ 'tests': [
+ { 'id': 'CB:name_TEXT-1_SI',
+ 'rte1-id': 'a-createbookmark-0',
+ 'desc': 'create a bookmark (named link) around selection',
+ 'value': 'created',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<a name="created">[bar]</a>baz' }
+ ]
+ }
+ ]
+}
+
+
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/applyCSS.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/applyCSS.py
new file mode 100644
index 0000000000..94cdad83fb
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/applyCSS.py
@@ -0,0 +1,244 @@
+
+APPLY_TESTS_CSS = {
+ 'id': 'AC',
+ 'caption': 'Apply Formatting Tests, using styleWithCSS',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': True,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[HTML5] bold',
+ 'command': 'bold',
+ 'tests': [
+ { 'id': 'B_TEXT-1_SI',
+ 'rte1-id': 'a-bold-1',
+ 'desc': 'Bold selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="font-weight: bold">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_TEXT-1_SI',
+ 'rte1-id': 'a-italic-1',
+ 'desc': 'Italicize selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="font-style: italic">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] underline',
+ 'command': 'underline',
+ 'tests': [
+ { 'id': 'U_TEXT-1_SI',
+ 'rte1-id': 'a-underline-1',
+ 'desc': 'Underline selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="text-decoration: underline">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] strikethrough',
+ 'command': 'strikethrough',
+ 'tests': [
+ { 'id': 'S_TEXT-1_SI',
+ 'rte1-id': 'a-strikethrough-1',
+ 'desc': 'Strike-through selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="text-decoration: line-through">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] subscript',
+ 'command': 'subscript',
+ 'tests': [
+ { 'id': 'SUB_TEXT-1_SI',
+ 'rte1-id': 'a-subscript-1',
+ 'desc': 'Change selection to subscript',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': '[HTML5] superscript',
+ 'command': 'superscript',
+ 'tests': [
+ { 'id': 'SUP_TEXT-1_SI',
+ 'rte1-id': 'a-superscript-1',
+ 'desc': 'Change selection to superscript',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span style="vertical-align: super">[bar]</span>baz' }
+ ]
+ },
+
+
+ { 'desc': '[MIDAS] backcolor',
+ 'command': 'backcolor',
+ 'tests': [
+ { 'id': 'BC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-backcolor-1',
+ 'desc': 'Change background color',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
+ 'foo<font style="background-color: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] forecolor',
+ 'command': 'forecolor',
+ 'tests': [
+ { 'id': 'FC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-forecolor-1',
+ 'desc': 'Change the text color',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="color: blue">[bar]</span>baz',
+ 'foo<font style="color: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] hilitecolor',
+ 'command': 'hilitecolor',
+ 'tests': [
+ { 'id': 'HC:blue_TEXT-1_SI',
+ 'rte1-id': 'a-hilitecolor-1',
+ 'desc': 'Change the hilite color',
+ 'value': 'blue',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
+ 'foo<font style="background-color: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontname',
+ 'command': 'fontname',
+ 'tests': [
+ { 'id': 'FN:a_TEXT-1_SI',
+ 'rte1-id': 'a-fontname-1',
+ 'desc': 'Change the font name',
+ 'value': 'arial',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="font-family: arial">[bar]</span>baz',
+ 'foo<font style="font-family: blue">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontsize',
+ 'command': 'fontsize',
+ 'tests': [
+ { 'id': 'FS:2_TEXT-1_SI',
+ 'rte1-id': 'a-fontsize-1',
+ 'desc': 'Change the font size to "2"',
+ 'value': '2',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="font-size: small">[bar]</span>baz',
+ 'foo<font style="font-size: small">[bar]</font>baz' ] },
+
+ { 'id': 'FS:18px_TEXT-1_SI',
+ 'desc': 'Change the font size to "18px"',
+ 'value': '18px',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="font-size: 18px">[bar]</span>baz',
+ 'foo<font style="font-size: 18px">[bar]</font>baz' ] },
+
+ { 'id': 'FS:large_TEXT-1_SI',
+ 'desc': 'Change the font size to "large"',
+ 'value': 'large',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<span style="font-size: large">[bar]</span>baz',
+ 'foo<font style="font-size: large">[bar]</font>baz' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] indent',
+ 'command': 'indent',
+ 'tests': [
+ { 'id': 'IND_TEXT-1_SI',
+ 'rte1-id': 'a-indent-1',
+ 'desc': 'Indent the text (assume "standard" 40px)',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ '<div style="margin-left: 40px">foo[bar]baz</div>',
+ '<div style="margin: 0 0 0 40px">foo[bar]baz</div>',
+ '<blockquote style="margin-left: 40px">foo[bar]baz</blockquote>',
+ '<blockquote style="margin: 0 0 0 40px">foo[bar]baz</blockquote>' ],
+ 'div': {
+ 'accOuter': [ '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>',
+ '<div contenteditable="true" style="margin: 0 0 0 40px">foo[bar]baz</div>' ] } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] outdent (-> unapply tests)',
+ 'command': 'outdent',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifycenter',
+ 'command': 'justifycenter',
+ 'tests': [
+ { 'id': 'JC_TEXT-1_SC',
+ 'rte1-id': 'a-justifycenter-1',
+ 'desc': 'justify the text centrally',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p style="text-align: center">foo^bar</p>',
+ '<div style="text-align: center">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div contenteditable="true" style="text-align: center">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyfull',
+ 'command': 'justifyfull',
+ 'tests': [
+ { 'id': 'JF_TEXT-1_SC',
+ 'rte1-id': 'a-justifyfull-1',
+ 'desc': 'justify the text fully',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p style="text-align: justify">foo^bar</p>',
+ '<div style="text-align: justify">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div contenteditable="true" style="text-align: justify">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyleft',
+ 'command': 'justifyleft',
+ 'tests': [
+ { 'id': 'JL_TEXT-1_SC',
+ 'rte1-id': 'a-justifyleft-1',
+ 'desc': 'justify the text left',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p style="text-align: left">foo^bar</p>',
+ '<div style="text-align: left">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div contenteditable="true" style="text-align: left">foo^bar</div>' } }
+ ]
+ },
+
+ { 'desc': '[MIDAS] justifyright',
+ 'command': 'justifyright',
+ 'tests': [
+ { 'id': 'JR_TEXT-1_SC',
+ 'rte1-id': 'a-justifyright-1',
+ 'desc': 'justify the text right',
+ 'pad': 'foo^bar',
+ 'expected': [ '<p style="text-align: right">foo^bar</p>',
+ '<div style="text-align: right">foo^bar</div>' ],
+ 'div': {
+ 'accOuter': '<div contenteditable="true" style="text-align: right">foo^bar</div>' } }
+ ]
+ }
+ ]
+}
+
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/change.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/change.py
new file mode 100644
index 0000000000..6a76d3d5fd
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/change.py
@@ -0,0 +1,273 @@
+
+CHANGE_TESTS = {
+ 'id': 'C',
+ 'caption': 'Change Existing Format to Different Format Tests',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[HTML5] italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_I-1_SL',
+ 'desc': 'Italicize partially italicized text',
+ 'pad': 'foo[bar<i>baz]</i>qoz',
+ 'expected': 'foo<i>[barbaz]</i>qoz' },
+
+ { 'id': 'I_B-I-1_SO',
+ 'desc': 'Italicize partially italicized text in bold context',
+ 'pad': '<b>foo[bar<i>baz</i>}</b>',
+ 'expected': '<b>foo<i>[barbaz]</i></b>' }
+ ]
+ },
+
+ { 'desc': '[HTML5] underline',
+ 'command': 'underline',
+ 'tests': [
+ { 'id': 'U_U-1_SO',
+ 'desc': 'Underline partially underlined text',
+ 'pad': 'foo[bar<u>baz</u>qoz]quz',
+ 'expected': 'foo<u>[barbazqoz]</u>quz' },
+
+ { 'id': 'U_U-1_SL',
+ 'desc': 'Underline partially underlined text',
+ 'pad': 'foo[bar<u>baz]qoz</u>quz',
+ 'expected': 'foo<u>[barbaz]qoz</u>quz' },
+
+ { 'id': 'U_S-U-1_SO',
+ 'desc': 'Underline partially underlined text in striked context',
+ 'pad': '<s>foo[bar<u>baz</u>}</s>',
+ 'expected': '<s>foo<u>[barbaz]</u></s>' }
+ ]
+ },
+
+
+ { 'desc': '[MIDAS] backcolor',
+ 'command': 'backcolor',
+ 'tests': [
+ { 'id': 'BC:842_FONTs:bc:fca-1_SW',
+ 'rte1-id': 'c-backcolor-0',
+ 'desc': 'Change background color to new color',
+ 'value': '#884422',
+ 'pad': '<font style="background-color: #ffccaa">[foobarbaz]</font>',
+ 'expected': [ '<font style="background-color: #884422">[foobarbaz]</font>',
+ '<span style="background-color: #884422">[foobarbaz]</span>' ] },
+
+ { 'id': 'BC:00f_SPANs:bc:f00-1_SW',
+ 'rte1-id': 'c-backcolor-2',
+ 'desc': 'Change background color to new color',
+ 'value': '#0000ff',
+ 'pad': '<span style="background-color: #ff0000">[foobarbaz]</span>',
+ 'expected': [ '<font style="background-color: #0000ff">[foobarbaz]</font>',
+ '<span style="background-color: #0000ff">[foobarbaz]</span>' ] },
+
+ { 'id': 'BC:ace_FONT.ass.s:bc:rgb-1_SW',
+ 'rte1-id': 'c-backcolor-1',
+ 'desc': 'Change background color in styled span to new color',
+ 'value': '#aaccee',
+ 'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">[foobarbaz]</span>',
+ 'expected': [ '<font style="background-color: #aaccee">[foobarbaz]</font>',
+ '<span style="background-color: #aaccee">[foobarbaz]</span>' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] forecolor',
+ 'command': 'forecolor',
+ 'tests': [
+ { 'id': 'FC:g_FONTc:b-1_SW',
+ 'rte1-id': 'c-forecolor-0',
+ 'desc': 'Change the text color (without CSS)',
+ 'value': 'green',
+ 'pad': '<font color="blue">[foobarbaz]</font>',
+ 'expected': '<font color="green">[foobarbaz]</font>' },
+
+ { 'id': 'FC:g_SPANs:c:g-1_SW',
+ 'rte1-id': 'c-forecolor-1',
+ 'desc': 'Change the text color from a styled span (without CSS)',
+ 'value': 'green',
+ 'pad': '<span style="color: blue">[foobarbaz]</span>',
+ 'expected': '<font color="green">[foobarbaz]</font>' },
+
+ { 'id': 'FC:g_FONTc:b.s:c:r-1_SW',
+ 'rte1-id': 'c-forecolor-2',
+ 'desc': 'Change the text color from conflicting color and style (without CSS)',
+ 'value': 'green',
+ 'pad': '<font color="blue" style="color: red">[foobarbaz]</font>',
+ 'expected': '<font color="green">[foobarbaz]</font>' },
+
+ { 'id': 'FC:g_FONTc:b.sz:6-1_SI',
+ 'desc': 'Change the font color in content with a different font size and font color',
+ 'value': 'green',
+ 'pad': '<font color="blue" size="6">foo[bar]baz</font>',
+ 'expected': [ '<font color="blue" size="6">foo<font color="green">[bar]</font>baz</font>',
+ '<font size="6"><font color="blue">foo<font color="green">[bar]</font><font color="blue">baz</font></font>' ] }
+ ]
+ },
+
+ { 'desc': '[MIDAS] hilitecolor',
+ 'command': 'hilitecolor',
+ 'tests': [
+ { 'id': 'HC:g_FONTs:c:b-1_SW',
+ 'rte1-id': 'c-hilitecolor-0',
+ 'desc': 'Change the hilite color (without CSS)',
+ 'value': 'green',
+ 'pad': '<font style="background-color: blue">[foobarbaz]</font>',
+ 'expected': [ '<font style="background-color: green">[foobarbaz]</font>',
+ '<span style="background-color: green">[foobarbaz]</span>' ] },
+
+ { 'id': 'HC:g_SPANs:c:g-1_SW',
+ 'rte1-id': 'c-hilitecolor-2',
+ 'desc': 'Change the hilite color from a styled span (without CSS)',
+ 'value': 'green',
+ 'pad': '<span style="background-color: blue">[foobarbaz]</span>',
+ 'expected': '<span style="background-color: green">[foobarbaz]</span>' },
+
+ { 'id': 'HC:g_SPAN.ass.s:c:rgb-1_SW',
+ 'rte1-id': 'c-hilitecolor-1',
+ 'desc': 'Change the hilite color from a styled span (without CSS)',
+ 'value': 'green',
+ 'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">[foobarbaz]</span>',
+ 'expected': '<span style="background-color: green">[foobarbaz]</span>' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontname',
+ 'command': 'fontname',
+ 'tests': [
+ { 'id': 'FN:c_FONTf:a-1_SW',
+ 'rte1-id': 'c-fontname-0',
+ 'desc': 'Change existing font name to new font name (without CSS)',
+ 'value': 'courier',
+ 'pad': '<font face="arial">[foobarbaz]</font>',
+ 'expected': '<font face="courier">[foobarbaz]</font>' },
+
+ { 'id': 'FN:c_SPANs:ff:a-1_SW',
+ 'rte1-id': 'c-fontname-1',
+ 'desc': 'Change existing font name from style to new font name (without CSS)',
+ 'value': 'courier',
+ 'pad': '<span style="font-family: arial">[foobarbaz]</span>',
+ 'expected': '<font face="courier">[foobarbaz]</font>' },
+
+ { 'id': 'FN:c_FONTf:a.s:ff:v-1_SW',
+ 'rte1-id': 'c-fontname-2',
+ 'desc': 'Change existing font name with conflicting face and style to new font name (without CSS)',
+ 'value': 'courier',
+ 'pad': '<font face="arial" style="font-family: verdana">[foobarbaz]</font>',
+ 'expected': '<font face="courier">[foobarbaz]</font>' },
+
+ { 'id': 'FN:c_FONTf:a-1_SI',
+ 'desc': 'Change existing font name to new font name, text partially selected',
+ 'value': 'courier',
+ 'pad': '<font face="arial">foo[bar]baz</font>',
+ 'expected': '<font face="arial">foo</font><font face="courier">[bar]</font><font face="arial">baz</font>',
+ 'accept': '<font face="arial">foo<font face="courier">[bar]</font>baz</font>' },
+
+ { 'id': 'FN:c_FONTf:a-2_SL',
+ 'desc': 'Change existing font name to new font name, using CSS styling',
+ 'value': 'courier',
+ 'pad': 'foo[bar<font face="arial">baz]qoz</font>',
+ 'expected': 'foo<font face="courier">[barbaz]</font><font face="arial">qoz</font>' },
+
+ { 'id': 'FN:c_FONTf:v-FONTf:a-1_SW',
+ 'rte1-id': 'c-fontname-3',
+ 'desc': 'Change existing font name in nested <font> tags to new font name (without CSS)',
+ 'value': 'courier',
+ 'pad': '<font face="verdana"><font face="arial">[foobarbaz]</font></font>',
+ 'expected': '<font face="courier">[foobarbaz]</font>',
+ 'accept': '<font face="verdana"><font face="courier">[foobarbaz]</font></font>' },
+
+ { 'id': 'FN:c_SPANs:ff:v-FONTf:a-1_SW',
+ 'rte1-id': 'c-fontname-4',
+ 'desc': 'Change existing font name in nested mixed tags to new font name (without CSS)',
+ 'value': 'courier',
+ 'pad': '<span style="font-family: verdana"><font face="arial">[foobarbaz]</font></span>',
+ 'expected': '<font face="courier">[foobarbaz]</font>',
+ 'accept': '<span style="font-family: verdana"><font face="courier">[foobarbaz]</font></span>' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontsize',
+ 'command': 'fontsize',
+ 'tests': [
+ { 'id': 'FS:1_FONTsz:4-1_SW',
+ 'rte1-id': 'c-fontsize-0',
+ 'desc': 'Change existing font size to new size (without CSS)',
+ 'value': '1',
+ 'pad': '<font size="4">[foobarbaz]</font>',
+ 'expected': '<font size="1">[foobarbaz]</font>' },
+
+ { 'id': 'FS:1_SPAN.ass.s:fs:large-1_SW',
+ 'rte1-id': 'c-fontsize-1',
+ 'desc': 'Change existing font size from styled span to new size (without CSS)',
+ 'value': '1',
+ 'pad': '<span class="Apple-style-span" style="font-size: large">[foobarbaz]</span>',
+ 'expected': '<font size="1">[foobarbaz]</font>' },
+
+ { 'id': 'FS:5_FONTsz:1.s:fs:xs-1_SW',
+ 'rte1-id': 'c-fontsize-2',
+ 'desc': 'Change existing font size from tag with conflicting size and style to new size (without CSS)',
+ 'value': '5',
+ 'pad': '<font size="1" style="font-size:x-small">[foobarbaz]</font>',
+ 'expected': '<font size="5">[foobarbaz]</font>' },
+
+ { 'id': 'FS:2_FONTc:b.sz:6-1_SI',
+ 'desc': 'Change the font size in content with a different font size and font color',
+ 'value': '2',
+ 'pad': '<font color="blue" size="6">foo[bar]baz</font>',
+ 'expected': [ '<font color="blue" size="6">foo<font size="2">[bar]</font>baz</font>',
+ '<font color="blue"><font size="6">foo</font><font size="2">[bar]</font><font size="6">baz</font></font>' ] },
+
+ { 'id': 'FS:larger_FONTsz:4',
+ 'desc': 'Change selection to use next larger font',
+ 'value': 'larger',
+ 'pad': '<font size="4">foo[bar]baz</font>',
+ 'expected': '<font size="4">foo<font size="larger">[bar]</font>baz</font>',
+ 'accept': '<font size="4">foo</font><font size="5">[bar]</font><font size="4">baz</font>' },
+
+ { 'id': 'FS:smaller_FONTsz:4',
+ 'desc': 'Change selection to use next smaller font',
+ 'value': 'smaller',
+ 'pad': '<font size="4">foo[bar]baz</font>',
+ 'expected': '<font size="4">foo<font size="smaller">[bar]</font>baz</font>',
+ 'accept': '<font size="4">foo</font><font size="3">[bar]</font><font size="4">baz</font>' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] formatblock',
+ 'command': 'formatblock',
+ 'tests': [
+ { 'id': 'FB:h1_ADDRESS-1_SW',
+ 'desc': 'change block from <address> to <h1>',
+ 'value': 'h1',
+ 'pad': '<address>foo [bar] baz</address>',
+ 'expected': '<h1>foo [bar] baz</h1>' },
+
+ { 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SO',
+ 'desc': 'change block from <address> with partially formatted content to <h1>',
+ 'value': 'h1',
+ 'pad': '<address>foo [<font size="4">bar</font>] baz</address>',
+ 'expected': '<h1>foo [bar] baz</h1>' },
+
+ { 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SW',
+ 'desc': 'change block from <address> with partially formatted content to <h1>',
+ 'value': 'h1',
+ 'pad': '<address>foo <font size="4">[bar]</font> baz</address>',
+ 'expected': '<h1>foo [bar] baz</h1>' },
+
+ { 'id': 'FB:h1_ADDRESS-FONT.ass.sz:4-1_SW',
+ 'desc': 'change block from <address> with partially formatted content to <h1>',
+ 'value': 'h1',
+ 'pad': '<address>foo <font class="Apple-style-span" size="4">[bar]</font> baz</address>',
+ 'expected': '<h1>foo [bar] baz</h1>' }
+ ]
+ }
+ ]
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/changeCSS.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/changeCSS.py
new file mode 100644
index 0000000000..4862b9b733
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/changeCSS.py
@@ -0,0 +1,210 @@
+
+CHANGE_TESTS_CSS = {
+ 'id': 'CC',
+ 'caption': 'Change Existing Format to Different Format Tests, using styleWithCSS',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': True,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[HTML5] italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_I-1_SL',
+ 'desc': 'Italicize partially italicized text',
+ 'pad': 'foo[bar<i>baz]</i>qoz',
+ 'expected': 'foo<span style="font-style: italic">[barbaz]</span>qoz' },
+
+ { 'id': 'I_B-1_SL',
+ 'desc': 'Italicize partially bolded text',
+ 'pad': 'foo[bar<b>baz]</b>qoz',
+ 'expected': 'foo<span style="font-style: italic">[bar<b>baz]</b></span>qoz',
+ 'accept': 'foo<span style="font-style: italic">[bar<b>baz</b>}</span>qoz' },
+
+ { 'id': 'I_B-1_SW',
+ 'desc': 'Italicize bold text, ideally combining both',
+ 'pad': 'foobar<b>[baz]</b>qoz',
+ 'expected': 'foobar<span style="font-style: italic; font-weight: bold">[baz]</span>qoz',
+ 'accept': 'foobar<b><span style="font-style: italic">[baz]</span></b>qoz' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] backcolor',
+ 'command': 'backcolor',
+ 'tests': [
+ { 'id': 'BC:gray_SPANs:bc:b-1_SW',
+ 'desc': 'Change background color from blue to gray',
+ 'value': 'gray',
+ 'pad': '<span style="background-color: blue">[foobarbaz]</span>',
+ 'expected': '<span style="background-color: gray">[foobarbaz]</span>' },
+
+ { 'id': 'BC:gray_SPANs:bc:b-1_SO',
+ 'desc': 'Change background color from blue to gray',
+ 'value': 'gray',
+ 'pad': '{<span style="background-color: blue">foobarbaz</span>}',
+ 'expected': [ '{<span style="background-color: gray">foobarbaz</span>}',
+ '<span style="background-color: gray">[foobarbaz]</span>' ] },
+
+ { 'id': 'BC:gray_SPANs:bc:b-1_SI',
+ 'desc': 'Change background color from blue to gray',
+ 'value': 'gray',
+ 'pad': '<span style="background-color: blue">foo[bar]baz</span>',
+ 'expected': '<span style="background-color: blue">foo</span><span style="background-color: gray">[bar]</span><span style="background-color: blue">baz</span>',
+ 'accept': '<span style="background-color: blue">foo<span style="background-color: gray">[bar]</span>baz</span>' },
+
+ { 'id': 'BC:gray_P-SPANs:bc:b-1_SW',
+ 'desc': 'Change background color within a paragraph from blue to gray',
+ 'value': 'gray',
+ 'pad': '<p><span style="background-color: blue">[foobarbaz]</span></p>',
+ 'expected': [ '<p><span style="background-color: gray">[foobarbaz]</span></p>',
+ '<p style="background-color: gray">[foobarbaz]</p>' ] },
+
+ { 'id': 'BC:gray_P-SPANs:bc:b-2_SW',
+ 'desc': 'Change background color within a paragraph from blue to gray',
+ 'value': 'gray',
+ 'pad': '<p>foo<span style="background-color: blue">[bar]</span>baz</p>',
+ 'expected': '<p>foo<span style="background-color: gray">[bar]</span>baz</p>' },
+
+ { 'id': 'BC:gray_P-SPANs:bc:b-3_SO',
+ 'desc': 'Change background color within a paragraph from blue to gray (selection encloses more than previous span)',
+ 'value': 'gray',
+ 'pad': '<p>[foo<span style="background-color: blue">barbaz</span>qoz]quz</p>',
+ 'expected': '<p><span style="background-color: gray">[foobarbazqoz]</span>quz</p>' },
+
+ { 'id': 'BC:gray_P-SPANs:bc:b-3_SL',
+ 'desc': 'Change background color within a paragraph from blue to gray (previous span partially selected)',
+ 'value': 'gray',
+ 'pad': '<p>[foo<span style="background-color: blue">bar]baz</span>qozquz</p>',
+ 'expected': '<p><span style="background-color: gray">[foobar]</span><span style="background-color: blue">baz</span>qozquz</p>' },
+
+ { 'id': 'BC:gray_SPANs:bc:b-2_SL',
+ 'desc': 'Change background color from blue to gray on partially covered span, selection extends left',
+ 'value': 'gray',
+ 'pad': 'foo [bar <span style="background-color: blue">baz] qoz</span> quz sic',
+ 'expected': 'foo <span style="background-color: gray">[bar baz]</span><span style="background-color: blue"> qoz</span> quz sic' },
+
+ { 'id': 'BC:gray_SPANs:bc:b-2_SR',
+ 'desc': 'Change background color from blue to gray on partially covered span, selection extends right',
+ 'value': 'gray',
+ 'pad': 'foo bar <span style="background-color: blue">baz [qoz</span> quz] sic',
+ 'expected': 'foo bar <span style="background-color: blue">baz </span><span style="background-color: gray">[qoz quz]</span> sic' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontname',
+ 'command': 'fontname',
+ 'tests': [
+ { 'id': 'FN:c_SPANs:ff:a-1_SW',
+ 'desc': 'Change existing font name to new font name, using CSS styling',
+ 'value': 'courier',
+ 'pad': '<span style="font-family: arial">[foobarbaz]</span>',
+ 'expected': '<span style="font-family: courier">[foobarbaz]</span>' },
+
+ { 'id': 'FN:c_FONTf:a-1_SW',
+ 'desc': 'Change existing font name to new font name, using CSS styling',
+ 'value': 'courier',
+ 'pad': '<font face="arial">[foobarbaz]</font>',
+ 'expected': [ '<font style="font-family: courier">[foobarbaz]</font>',
+ '<span style="font-family: courier">[foobarbaz]</span>' ] },
+
+ { 'id': 'FN:c_FONTf:a-1_SI',
+ 'desc': 'Change existing font name to new font name, using CSS styling',
+ 'value': 'courier',
+ 'pad': '<font face="arial">foo[bar]baz</font>',
+ 'expected': '<font face="arial">foo</font><span style="font-family: courier">[bar]</span><font face="arial">baz</font>' },
+
+ { 'id': 'FN:a_FONTf:a-1_SI',
+ 'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
+ 'value': 'arial',
+ 'pad': '<font face="arial">foo[bar]baz</font>',
+ 'expected': '<font face="arial">foo[bar]baz</font>' },
+
+ { 'id': 'FN:a_FONTf:a-1_SW',
+ 'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
+ 'value': 'arial',
+ 'pad': '<font face="arial">[foobarbaz]</font>',
+ 'expected': [ '<font face="arial">[foobarbaz]</font>',
+ '<span style="font-family: arial">[foobarbaz]</span>' ] },
+
+ { 'id': 'FN:a_FONTf:a-1_SO',
+ 'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
+ 'value': 'arial',
+ 'pad': '{<font face="arial">foobarbaz</font>}',
+ 'expected': [ '{<font face="arial">foobarbaz</font>}',
+ '<font face="arial">[foobarbaz]</font>',
+ '{<span style="font-family: arial">foobarbaz</span>}',
+ '<span style="font-family: arial">[foobarbaz]</span>' ] },
+
+ { 'id': 'FN:a_SPANs:ff:a-1_SI',
+ 'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
+ 'value': 'arial',
+ 'pad': '<span style="font-family: arial">[foobarbaz]</span>',
+ 'expected': '<span style="font-family: arial">[foobarbaz]</span>' },
+
+ { 'id': 'FN:c_FONTf:a-2_SL',
+ 'desc': 'Change existing font name to new font name, using CSS styling',
+ 'value': 'courier',
+ 'pad': 'foo[bar<font face="arial">baz]qoz</font>',
+ 'expected': 'foo<span style="font-family: courier">[barbaz]</span><font face="arial">qoz</font>' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] fontsize',
+ 'command': 'fontsize',
+ 'tests': [
+ { 'id': 'FS:1_SPANs:fs:l-1_SW',
+ 'desc': 'Change existing font size to new size, using CSS styling',
+ 'value': '1',
+ 'pad': '<span style="font-size: large">[foobarbaz]</span>',
+ 'expected': '<span style="font-size: x-small">[foobarbaz]</span>' },
+
+ { 'id': 'FS:large_SPANs:fs:l-1_SW',
+ 'desc': 'Change existing font size to same size (should be noop)',
+ 'value': 'large',
+ 'pad': '<span style="font-size: large">[foobarbaz]</span>',
+ 'expected': '<span style="font-size: large">[foobarbaz]</span>' },
+
+ { 'id': 'FS:18px_SPANs:fs:l-1_SW',
+ 'desc': 'Change existing font size to equivalent px size (should be noop, or change unit)',
+ 'value': '18px',
+ 'pad': '<span style="font-size: large">[foobarbaz]</span>',
+ 'expected': [ '<span style="font-size: 18px">[foobarbaz]</span>',
+ '<span style="font-size: large">[foobarbaz]</span>' ] },
+
+ { 'id': 'FS:4_SPANs:fs:l-1_SW',
+ 'desc': 'Change existing font size to equivalent numeric size (should be noop)',
+ 'value': '4',
+ 'pad': '<span style="font-size: large">[foobarbaz]</span>',
+ 'expected': '<span style="font-size: large">[foobarbaz]</span>' },
+
+ { 'id': 'FS:4_SPANs:fs:18px-1_SW',
+ 'desc': 'Change existing font size to equivalent numeric size (should be noop)',
+ 'value': '4',
+ 'pad': '<span style="font-size: 18px">[foobarbaz]</span>',
+ 'expected': '<span style="font-size: 18px">[foobarbaz]</span>' },
+
+ { 'id': 'FS:larger_SPANs:fs:l-1_SI',
+ 'desc': 'Change selection to use next larger font',
+ 'value': 'larger',
+ 'pad': '<span style="font-size: large">foo[bar]baz</span>',
+ 'expected': [ '<span style="font-size: large">foo<span style="font-size: x-large">[bar]</span>baz</span>',
+ '<span style="font-size: large">foo</span><span style="font-size: x-large">[bar]</span><span style="font-size: large">baz</span>' ],
+ 'accept': '<span style="font-size: large">foo<font size="larger">[bar]</font>baz</span>' },
+
+ { 'id': 'FS:smaller_SPANs:fs:l-1_SI',
+ 'desc': 'Change selection to use next smaller font',
+ 'value': 'smaller',
+ 'pad': '<span style="font-size: large">foo[bar]baz</span>',
+ 'expected': [ '<span style="font-size: large">foo<span style="font-size: medium">[bar]</span>baz</span>',
+ '<span style="font-size: large">foo</span><span style="font-size: medium">[bar]</span><span style="font-size: large">baz</span>' ],
+ 'accept': '<span style="font-size: large">foo<font size="smaller">[bar]</font>baz</span>' }
+ ]
+ }
+ ]
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/delete.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/delete.py
new file mode 100644
index 0000000000..6fb81f76cc
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/delete.py
@@ -0,0 +1,330 @@
+
+DELETE_TESTS = {
+ 'id': 'D',
+ 'caption': 'Delete Tests',
+ 'command': 'delete',
+ 'checkAttrs': True,
+ 'checkStyle': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'delete single characters',
+ 'tests': [
+ { 'id': 'CHAR-1_SC',
+ 'desc': 'Delete 1 character',
+ 'pad': 'foo^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-2_SC',
+ 'desc': 'Delete 1 pre-composed character o with diaeresis',
+ 'pad': 'fo&#xF6;^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-3_SC',
+ 'desc': 'Delete 1 character with combining diaeresis above',
+ 'pad': 'foo&#x0308;^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-4_SC',
+ 'desc': 'Delete 1 character with combining diaeresis below',
+ 'pad': 'foo&#x0324;^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-5_SC',
+ 'desc': 'Delete 1 character with combining diaeresis above and below',
+ 'pad': 'foo&#x0308;&#x0324;^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-5_SI-1',
+ 'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis above',
+ 'pad': 'foo[&#x0308;]&#x0324;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-5_SI-2',
+ 'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis below',
+ 'pad': 'foo&#x0308;[&#x0324;]barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-5_SR',
+ 'desc': 'Delete 1 character with combining diaeresis above and below, selection oblique on diaeresis and following text',
+ 'pad': 'foo&#x0308;[&#x0324;bar]baz',
+ 'expected': 'fo^baz' },
+
+ { 'id': 'CHAR-6_SC',
+ 'desc': 'Delete 1 character with enclosing square',
+ 'pad': 'foo&#x20DE;^barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-7_SC',
+ 'desc': 'Delete 1 character with combining long solidus overlay',
+ 'pad': 'foo&#x0338;^barbaz',
+ 'expected': 'fo^barbaz' }
+ ]
+ },
+
+ { 'desc': 'delete text selection',
+ 'tests': [
+ { 'id': 'TEXT-1_SI',
+ 'desc': 'Delete text selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SS',
+ 'desc': 'Delete at start of span',
+ 'pad': 'foo<b>^bar</b>baz',
+ 'expected': 'fo^<b>bar</b>baz' },
+
+ { 'id': 'B-1_SA',
+ 'desc': 'Delete from position after span',
+ 'pad': 'foo<b>bar</b>^baz',
+ 'expected': 'foo<b>ba^</b>baz' },
+
+ { 'id': 'B-1_SW',
+ 'desc': 'Delete selection that wraps the whole span content',
+ 'pad': 'foo<b>[bar]</b>baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SO',
+ 'desc': 'Delete selection that wraps the whole span',
+ 'pad': 'foo[<b>bar</b>]baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SL',
+ 'desc': 'Delete oblique selection that starts before span',
+ 'pad': 'foo[bar<b>baz]quoz</b>quuz',
+ 'expected': 'foo^<b>quoz</b>quuz' },
+
+ { 'id': 'B-1_SR',
+ 'desc': 'Delete oblique selection that ends after span',
+ 'pad': 'foo<b>bar[baz</b>quoz]quuz',
+ 'expected': 'foo<b>bar^</b>quuz' },
+
+ { 'id': 'B.I-1_SM',
+ 'desc': 'Delete oblique selection that starts and ends in different spans',
+ 'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
+ 'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
+
+ { 'id': 'GEN-1_SS',
+ 'desc': 'Delete at start of span with generated content',
+ 'pad': 'foo<gen>^bar</gen>baz',
+ 'expected': 'fo^<gen>bar</gen>baz' },
+
+ { 'id': 'GEN-1_SA',
+ 'desc': 'Delete from position after span with generated content',
+ 'pad': 'foo<gen>bar</gen>^baz',
+ 'expected': 'foo<gen>ba^</gen>baz' }
+ ]
+ },
+
+ { 'desc': 'delete paragraphs',
+ 'tests': [
+ { 'id': 'P2-1_SS2',
+ 'desc': 'Delete from collapsed selection at start of paragraph - should merge with previous',
+ 'pad': '<p>foobar</p><p>^bazqoz</p>',
+ 'expected': '<p>foobar^bazqoz</p>' },
+
+ { 'id': 'P2-1_SI2',
+ 'desc': 'Delete non-collapsed selection at start of paragraph - should not merge with previous',
+ 'pad': '<p>foobar</p><p>[baz]qoz</p>',
+ 'expected': '<p>foobar</p><p>^qoz</p>' },
+
+ { 'id': 'P2-1_SM',
+ 'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
+ 'pad': '<p>foo[bar</p><p>baz]qoz</p>',
+ 'expected': '<p>foo^qoz</p>' }
+ ]
+ },
+
+ { 'desc': 'delete lists and list items',
+ 'tests': [
+ { 'id': 'OL-LI2-1_SO1',
+ 'desc': 'Delete fully wrapped list item',
+ 'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
+ 'expected': ['foo<ol>|<li>baz</li></ol>qoz',
+ 'foo<ol><li>^baz</li></ol>qoz'] },
+
+ { 'id': 'OL-LI2-1_SM',
+ 'desc': 'Delete oblique range between list items within same list',
+ 'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
+ 'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
+
+ { 'id': 'OL-LI-1_SW',
+ 'desc': 'Delete contents of last list item (list should remain)',
+ 'pad': 'foo<ol><li>[foo]</li></ol>qoz',
+ 'expected': ['foo<ol><li>|</li></ol>qoz',
+ 'foo<ol><li>^</li></ol>qoz'] },
+
+ { 'id': 'OL-LI-1_SO',
+ 'desc': 'Delete last list item of list (should remove entire list)',
+ 'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
+ 'expected': 'foo^qoz' }
+ ]
+ },
+
+ { 'desc': 'delete with strange selections',
+ 'tests': [
+ { 'id': 'HR.BR-1_SM',
+ 'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
+ 'pad': 'foo<hr {>bar<br }>baz',
+ 'expected': 'foo<hr>|<br>baz' }
+ ]
+ },
+
+ { 'desc': 'delete after table',
+ 'tests': [
+ { 'id': 'TABLE-1_SA',
+ 'desc': 'Delete from position immediately after table (should have no effect)',
+ 'pad': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz',
+ 'expected': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz' }
+ ]
+ },
+
+ { 'desc': 'delete within table cells',
+ 'tests': [
+ { 'id': 'TD-1_SS',
+ 'desc': 'Delete from start of first cell (should have no effect)',
+ 'pad': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz',
+ 'expected': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz' },
+
+ { 'id': 'TD2-1_SS2',
+ 'desc': 'Delete from start of inner cell (should have no effect)',
+ 'pad': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz',
+ 'expected': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz' },
+
+ { 'id': 'TD2-1_SM',
+ 'desc': 'Delete with selection spanning 2 cells',
+ 'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
+ 'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
+ ]
+ },
+
+ { 'desc': 'delete table rows',
+ 'tests': [
+ { 'id': 'TR3-1_SO1',
+ 'desc': 'Delete first table row',
+ 'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3-1_SO2',
+ 'desc': 'Delete middle table row',
+ 'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3-1_SO3',
+ 'desc': 'Delete last table row',
+ 'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
+
+ { 'id': 'TR2rs:2-1_SO1',
+ 'desc': 'Delete first table row where a cell has rowspan 2',
+ 'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
+
+ { 'id': 'TR2rs:2-1_SO2',
+ 'desc': 'Delete second table row where a cell has rowspan 2',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO1',
+ 'desc': 'Delete first table row where a cell has rowspan 3',
+ 'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO2',
+ 'desc': 'Delete middle table row where a cell has rowspan 3',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO3',
+ 'desc': 'Delete last table row where a cell has rowspan 3',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
+ ]
+ },
+
+ { 'desc': 'delete with non-editable nested content',
+ 'tests': [
+ { 'id': 'DIV:ce:false-1_SO',
+ 'desc': 'Delete nested non-editable <div>',
+ 'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
+ 'expected': 'foo^qoz' },
+
+ { 'id': 'DIV:ce:false-1_SB',
+ 'desc': 'Delete from immediately after a nested non-editable <div>',
+ 'pad': 'foobar<div contenteditable="false">NESTED</div>^bazqoz',
+ 'expected': 'foobar^bazqoz' },
+
+ { 'id': 'DIV:ce:false-1_SL',
+ 'desc': 'Delete nested non-editable <div> with oblique selection',
+ 'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
+ 'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
+ 'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
+
+ { 'id': 'DIV:ce:false-1_SR',
+ 'desc': 'Delete nested non-editable <div> with oblique selection',
+ 'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
+ 'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
+ 'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
+
+ { 'id': 'DIV:ce:false-1_SI',
+ 'desc': 'Delete inside nested non-editable <div> (should be no-op)',
+ 'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
+ 'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
+ ]
+ },
+
+ { 'desc': 'Delete with display:inline-block',
+ 'checkStyle': True,
+ 'tests': [
+ { 'id': 'SPAN:d:ib-1_SC',
+ 'desc': 'Delete inside an inline-block <span>',
+ 'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">ba^baz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-1_SA',
+ 'desc': 'Delete from immediately after an inline-block <span>',
+ 'pad': 'foo<span style="display: inline-block">barbaz</span>^qoz',
+ 'expected': 'foo<span style="display: inline-block">barba^</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-2_SL',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
+ 'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
+
+ { 'id': 'SPAN:d:ib-3_SR',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
+ 'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
+
+ { 'id': 'SPAN:d:ib-4i_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-4l_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-4r_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
+ ]
+ }
+ ]
+}
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/forwarddelete.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/forwarddelete.py
new file mode 100644
index 0000000000..813b22914a
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/forwarddelete.py
@@ -0,0 +1,315 @@
+
+FORWARDDELETE_TESTS = {
+ 'id': 'FD',
+ 'caption': 'Forward-Delete Tests',
+ 'command': 'forwardDelete',
+ 'checkAttrs': True,
+ 'checkStyle': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'forward-delete single characters',
+ 'tests': [
+ { 'id': 'CHAR-1_SC',
+ 'desc': 'Delete 1 character',
+ 'pad': 'foo^barbaz',
+ 'expected': 'foo^arbaz' },
+
+ { 'id': 'CHAR-2_SC',
+ 'desc': 'Delete 1 pre-composed character o with diaeresis',
+ 'pad': 'fo^&#xF6;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-3_SC',
+ 'desc': 'Delete 1 character with combining diaeresis above',
+ 'pad': 'fo^o&#x0308;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-4_SC',
+ 'desc': 'Delete 1 character with combining diaeresis below',
+ 'pad': 'fo^o&#x0324;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-5_SC',
+ 'desc': 'Delete 1 character with combining diaeresis above and below',
+ 'pad': 'fo^o&#x0308;&#x0324;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-6_SC',
+ 'desc': 'Delete 1 character with enclosing square',
+ 'pad': 'fo^o&#x20DE;barbaz',
+ 'expected': 'fo^barbaz' },
+
+ { 'id': 'CHAR-7_SC',
+ 'desc': 'Delete 1 character with combining long solidus overlay',
+ 'pad': 'fo^o&#x0338;barbaz',
+ 'expected': 'fo^barbaz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete text selections',
+ 'tests': [
+ { 'id': 'TEXT-1_SI',
+ 'desc': 'Delete text selection',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SE',
+ 'desc': 'Forward-delete at end of span',
+ 'pad': 'foo<b>bar^</b>baz',
+ 'expected': 'foo<b>bar^</b>az' },
+
+ { 'id': 'B-1_SB',
+ 'desc': 'Forward-delete from position before span',
+ 'pad': 'foo^<b>bar</b>baz',
+ 'expected': 'foo^<b>ar</b>baz' },
+
+ { 'id': 'B-1_SW',
+ 'desc': 'Delete selection that wraps the whole span content',
+ 'pad': 'foo<b>[bar]</b>baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SO',
+ 'desc': 'Delete selection that wraps the whole span',
+ 'pad': 'foo[<b>bar</b>]baz',
+ 'expected': 'foo^baz' },
+
+ { 'id': 'B-1_SL',
+ 'desc': 'Delete oblique selection that starts before span',
+ 'pad': 'foo[bar<b>baz]quoz</b>quuz',
+ 'expected': 'foo^<b>quoz</b>quuz' },
+
+ { 'id': 'B-1_SR',
+ 'desc': 'Delete oblique selection that ends after span',
+ 'pad': 'foo<b>bar[baz</b>quoz]quuz',
+ 'expected': 'foo<b>bar^</b>quuz' },
+
+ { 'id': 'B.I-1_SM',
+ 'desc': 'Delete oblique selection that starts and ends in different spans',
+ 'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
+ 'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
+
+ { 'id': 'GEN-1_SE',
+ 'desc': 'Delete at end of span with generated content',
+ 'pad': 'foo<gen>bar^</gen>baz',
+ 'expected': 'foo<gen>bar^</gen>az' },
+
+ { 'id': 'GEN-1_SB',
+ 'desc': 'Delete from position before span with generated content',
+ 'pad': 'foo^<gen>bar</gen>baz',
+ 'expected': 'foo^<gen>ar</gen>baz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete paragraphs',
+ 'tests': [
+ { 'id': 'P2-1_SE1',
+ 'desc': 'Delete from collapsed selection at end of paragraph - should merge with next',
+ 'pad': '<p>foobar^</p><p>bazqoz</p>',
+ 'expected': '<p>foobar^bazqoz</p>' },
+
+ { 'id': 'P2-1_SI1',
+ 'desc': 'Delete non-collapsed selection at end of paragraph - should not merge with next',
+ 'pad': '<p>foo[bar]</p><p>bazqoz</p>',
+ 'expected': '<p>foo^</p><p>bazqoz</p>' },
+
+ { 'id': 'P2-1_SM',
+ 'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
+ 'pad': '<p>foo[bar</p><p>baz]qoz</p>',
+ 'expected': '<p>foo^qoz</p>' }
+ ]
+ },
+
+ { 'desc': 'forward-delete lists and list items',
+ 'tests': [
+ { 'id': 'OL-LI2-1_SO1',
+ 'desc': 'Delete fully wrapped list item',
+ 'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
+ 'expected': ['foo<ol>|<li>baz</li></ol>qoz',
+ 'foo<ol><li>^baz</li></ol>qoz'] },
+
+ { 'id': 'OL-LI2-1_SM',
+ 'desc': 'Delete oblique range between list items within same list',
+ 'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
+ 'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
+
+ { 'id': 'OL-LI-1_SW',
+ 'desc': 'Delete contents of last list item (list should remain)',
+ 'pad': 'foo<ol><li>[foo]</li></ol>qoz',
+ 'expected': ['foo<ol><li>|</li></ol>qoz',
+ 'foo<ol><li>^</li></ol>qoz'] },
+
+ { 'id': 'OL-LI-1_SO',
+ 'desc': 'Delete last list item of list (should remove entire list)',
+ 'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
+ 'expected': 'foo^qoz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete with strange selections',
+ 'tests': [
+ { 'id': 'HR.BR-1_SM',
+ 'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
+ 'pad': 'foo<hr {>bar<br }>baz',
+ 'expected': 'foo<hr>|<br>baz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete from immediately before a table',
+ 'tests': [
+ { 'id': 'TABLE-1_SB',
+ 'desc': 'Delete from position immediately before table (should have no effect)',
+ 'pad': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz',
+ 'expected': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete within table cells',
+ 'tests': [
+ { 'id': 'TD-1_SE',
+ 'desc': 'Delete from end of last cell (should have no effect)',
+ 'pad': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz',
+ 'expected': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz' },
+
+ { 'id': 'TD2-1_SE1',
+ 'desc': 'Delete from end of inner cell (should have no effect)',
+ 'pad': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz',
+ 'expected': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz' },
+
+ { 'id': 'TD2-1_SM',
+ 'desc': 'Delete with selection spanning 2 cells',
+ 'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
+ 'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
+ ]
+ },
+
+ { 'desc': 'forward-delete table rows',
+ 'tests': [
+ { 'id': 'TR3-1_SO1',
+ 'desc': 'Delete first table row',
+ 'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3-1_SO2',
+ 'desc': 'Delete middle table row',
+ 'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3-1_SO3',
+ 'desc': 'Delete last table row',
+ 'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
+
+ { 'id': 'TR2rs:2-1_SO1',
+ 'desc': 'Delete first table row where a cell has rowspan 2',
+ 'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
+
+ { 'id': 'TR2rs:2-1_SO2',
+ 'desc': 'Delete second table row where a cell has rowspan 2',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO1',
+ 'desc': 'Delete first table row where a cell has rowspan 3',
+ 'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO2',
+ 'desc': 'Delete middle table row where a cell has rowspan 3',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
+ 'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
+ '<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
+
+ { 'id': 'TR3rs:3-1_SO3',
+ 'desc': 'Delete last table row where a cell has rowspan 3',
+ 'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
+ 'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
+ '<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
+ ]
+ },
+
+ { 'desc': 'delete with non-editable nested content',
+ 'tests': [
+ { 'id': 'DIV:ce:false-1_SO',
+ 'desc': 'Delete nested non-editable <div>',
+ 'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
+ 'expected': 'foo^qoz' },
+
+ { 'id': 'DIV:ce:false-1_SB',
+ 'desc': 'Delete from immediately before a nested non-editable <div>',
+ 'pad': 'foobar^<div contenteditable="false">NESTED</div>bazqoz',
+ 'expected': 'foobar^bazqoz' },
+
+ { 'id': 'DIV:ce:false-1_SL',
+ 'desc': 'Delete nested non-editable <div> with oblique selection',
+ 'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
+ 'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
+ 'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
+
+ { 'id': 'DIV:ce:false-1_SR',
+ 'desc': 'Delete nested non-editable <div> with oblique selection',
+ 'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
+ 'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
+ 'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
+
+ { 'id': 'DIV:ce:false-1_SI',
+ 'desc': 'Delete inside nested non-editable <div> (should be no-op)',
+ 'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
+ 'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
+ ]
+ },
+
+ { 'desc': 'Delete with display:inline-block',
+ 'checkStyle': True,
+ 'tests': [
+ { 'id': 'SPAN:d:ib-1_SC',
+ 'desc': 'Delete inside an inline-block <span>',
+ 'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">bar^az</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-1_SA',
+ 'desc': 'Delete from immediately before an inline-block <span>',
+ 'pad': 'foo^<span style="display: inline-block">barbaz</span>qoz',
+ 'expected': 'foo^<span style="display: inline-block">arbaz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-2_SL',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
+ 'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
+
+ { 'id': 'SPAN:d:ib-3_SR',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
+ 'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
+
+ { 'id': 'SPAN:d:ib-4i_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-4l_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
+
+ { 'id': 'SPAN:d:ib-4r_SI',
+ 'desc': 'Delete with nested inline-block <span>, oblique selection',
+ 'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
+ 'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
+ ]
+ }
+ ]
+}
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/insert.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/insert.py
new file mode 100644
index 0000000000..a2e79c27c8
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/insert.py
@@ -0,0 +1,285 @@
+
+INSERT_TESTS = {
+ 'id': 'I',
+ 'caption': 'Insert Tests',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'insert <hr>',
+ 'command': 'inserthorizontalrule',
+ 'tests': [
+ { 'id': 'IHR_TEXT-1_SC',
+ 'rte1-id': 'a-inserthorizontalrule-0',
+ 'desc': 'Insert <hr> into text',
+ 'pad': 'foo^bar',
+ 'expected': 'foo<hr>^bar',
+ 'accept': 'foo<hr>|bar' },
+
+ { 'id': 'IHR_TEXT-1_SI',
+ 'desc': 'Insert <hr>, replacing selected text',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<hr>^baz',
+ 'accept': 'foo<hr>|baz' },
+
+ { 'id': 'IHR_DIV-B-1_SX',
+ 'desc': 'Insert <hr> between elements',
+ 'pad': '<div><b>foo</b>|<b>bar</b></div>',
+ 'expected': '<div><b>foo</b><hr>|<b>bar</b></div>' },
+
+ { 'id': 'IHR_DIV-B-2_SO',
+ 'desc': 'Insert <hr>, replacing a fully wrapped element',
+ 'pad': '<div><b>foo</b>{<b>bar</b>}<b>baz</b></div>',
+ 'expected': '<div><b>foo</b><hr>|<b>baz</b></div>' },
+
+ { 'id': 'IHR_B-1_SC',
+ 'desc': 'Insert <hr> into a span, splitting it',
+ 'pad': '<b>foo^bar</b>',
+ 'expected': '<b>foo</b><hr><b>^bar</b>' },
+
+ { 'id': 'IHR_B-1_SS',
+ 'desc': 'Insert <hr> into a span at the start (should not create an empty span)',
+ 'pad': '<b>^foobar</b>',
+ 'expected': '<hr><b>^foobar</b>' },
+
+ { 'id': 'IHR_B-1_SE',
+ 'desc': 'Insert <hr> into a span at the end',
+ 'pad': '<b>foobar^</b>',
+ 'expected': [ '<b>foobar</b><hr>|',
+ '<b>foobar</b><hr><b>^</b>' ] },
+
+ { 'id': 'IHR_B-2_SL',
+ 'desc': 'Insert <hr> with oblique selection starting outside of span',
+ 'pad': 'foo[bar<b>baz]qoz</b>',
+ 'expected': 'foo<hr>|<b>qoz</b>' },
+
+ { 'id': 'IHR_B-2_SLR',
+ 'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
+ 'pad': 'foo]bar<b>baz[qoz</b>',
+ 'expected': [ 'foo<hr>|<b>qoz</b>',
+ 'foo<hr><b>^qoz</b>' ] },
+
+ { 'id': 'IHR_B-3_SR',
+ 'desc': 'Insert <hr> with oblique selection ending outside of span',
+ 'pad': '<b>foo[bar</b>baz]quoz',
+ 'expected': [ '<b>foo</b><hr>|quoz',
+ '<b>foo</b><hr><b>^</b>quoz' ] },
+
+ { 'id': 'IHR_B-3_SRR',
+ 'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
+ 'pad': '<b>foo]bar</b>baz[quoz',
+ 'expected': '<b>foo</b><hr>|quoz' },
+
+ { 'id': 'IHR_B-I-1_SM',
+ 'desc': 'Insert <hr> with oblique selection between different spans',
+ 'pad': '<b>foo[bar</b><i>baz]quoz</i>',
+ 'expected': [ '<b>foo</b><hr>|<i>quoz</i>',
+ '<b>foo</b><hr><b>^</b><i>quoz</i>' ] },
+
+ { 'id': 'IHR_B-I-1_SMR',
+ 'desc': 'Insert <hr> with reversed oblique selection between different spans',
+ 'pad': '<b>foo]bar</b><i>baz[quoz</i>',
+ 'expected': '<b>foo</b><hr><i>^quoz</i>' },
+
+ { 'id': 'IHR_P-1_SC',
+ 'desc': 'Insert <hr> into a paragraph, splitting it',
+ 'pad': '<p>foo^bar</p>',
+ 'expected': [ '<p>foo</p><hr>|<p>bar</p>',
+ '<p>foo</p><hr><p>^bar</p>' ] },
+
+ { 'id': 'IHR_P-1_SS',
+ 'desc': 'Insert <hr> into a paragraph at the start (should not create an empty span)',
+ 'pad': '<p>^foobar</p>',
+ 'expected': [ '<hr>|<p>foobar</p>',
+ '<hr><p>^foobar</p>' ] },
+
+ { 'id': 'IHR_P-1_SE',
+ 'desc': 'Insert <hr> into a paragraph at the end (should not create an empty span)',
+ 'pad': '<p>foobar^</p>',
+ 'expected': '<p>foobar</p><hr>|' }
+ ]
+ },
+
+ { 'desc': 'insert <p>',
+ 'command': 'insertparagraph',
+ 'tests': [
+ { 'id': 'IP_P-1_SC',
+ 'desc': 'Split paragraph',
+ 'pad': '<p>foo^bar</p>',
+ 'expected': '<p>foo</p><p>^bar</p>' },
+
+ { 'id': 'IP_UL-LI-1_SC',
+ 'desc': 'Split list item',
+ 'pad': '<ul><li>foo^bar</li></ul>',
+ 'expected': '<ul><li>foo</li><li>^bar</li></ul>' }
+ ]
+ },
+
+ { 'desc': 'insert text',
+ 'command': 'inserttext',
+ 'tests': [
+ { 'id': 'ITEXT:text_TEXT-1_SC',
+ 'desc': 'Insert text',
+ 'value': 'text',
+ 'pad': 'foo^bar',
+ 'expected': 'footext^bar' },
+
+ { 'id': 'ITEXT:text_TEXT-1_SI',
+ 'desc': 'Insert text, replacing selected text',
+ 'value': 'text',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'footext^baz' }
+ ]
+ },
+
+ { 'desc': 'insert <br>',
+ 'command': 'insertlinebreak',
+ 'tests': [
+ { 'id': 'IBR_TEXT-1_SC',
+ 'desc': 'Insert <br> into text',
+ 'pad': 'foo^bar',
+ 'expected': [ 'foo<br>|bar',
+ 'foo<br>^bar' ] },
+
+ { 'id': 'IBR_TEXT-1_SI',
+ 'desc': 'Insert <br>, replacing selected text',
+ 'pad': 'foo[bar]baz',
+ 'expected': [ 'foo<br>|baz',
+ 'foo<br>^baz' ] },
+
+ { 'id': 'IBR_LI-1_SC',
+ 'desc': 'Insert <br> within list item',
+ 'pad': '<ul><li>foo^bar</li></ul>',
+ 'expected': '<ul><li>foo<br>^bar</li></ul>' }
+ ]
+ },
+
+ { 'desc': 'insert <img>',
+ 'command': 'insertimage',
+ 'tests': [
+ { 'id': 'IIMG:url_TEXT-1_SC',
+ 'rte1-id': 'a-insertimage-0',
+ 'desc': 'Insert image with URL "bar.png"',
+ 'value': 'bar.png',
+ 'checkAttrs': True,
+ 'pad': 'foo^bar',
+ 'expected': [ 'foo<img src="bar.png">|bar',
+ 'foo<img src="bar.png">^bar' ] },
+
+ { 'id': 'IIMG:url_IMG-1_SO',
+ 'desc': 'Change existing image to new URL, selection on <img>',
+ 'value': 'quz.png',
+ 'checkAttrs': True,
+ 'pad': '<span>foo{<img src="bar.png">}bar</span>',
+ 'expected': [ '<span>foo<img src="quz.png"/>|bar</span>',
+ '<span>foo<img src="quz.png"/>^bar</span>' ] },
+
+ { 'id': 'IIMG:url_SPAN-IMG-1_SO',
+ 'desc': 'Change existing image to new URL, selection in text surrounding <img>',
+ 'value': 'quz.png',
+ 'checkAttrs': True,
+ 'pad': 'foo[<img src="bar.png">]bar',
+ 'expected': [ 'foo<img src="quz.png"/>|bar',
+ 'foo<img src="quz.png"/>^bar' ] },
+
+ { 'id': 'IIMG:._SPAN-IMG-1_SO',
+ 'desc': 'Remove existing image or URL, selection on <img>',
+ 'value': '',
+ 'checkAttrs': True,
+ 'pad': '<span>foo{<img src="bar.png">}bar</span>',
+ 'expected': [ '<span>foo^bar</span>',
+ '<span>foo<img>|bar</span>',
+ '<span>foo<img>^bar</span>',
+ '<span>foo<img src="">|bar</span>',
+ '<span>foo<img src="">^bar</span>' ] },
+
+ { 'id': 'IIMG:._IMG-1_SO',
+ 'desc': 'Remove existing image or URL, selection in text surrounding <img>',
+ 'value': '',
+ 'checkAttrs': True,
+ 'pad': 'foo[<img src="bar.png">]bar',
+ 'expected': [ 'foo^bar',
+ 'foo<img>|bar',
+ 'foo<img>^bar',
+ 'foo<img src="">|bar',
+ 'foo<img src="">^bar' ] }
+ ]
+ },
+
+ { 'desc': 'insert <ol>',
+ 'command': 'insertorderedlist',
+ 'tests': [
+ { 'id': 'IOL_TEXT-1_SC',
+ 'rte1-id': 'a-insertorderedlist-0',
+ 'desc': 'Insert ordered list on collapsed selection',
+ 'pad': 'foo^bar',
+ 'expected': '<ol><li>foo^bar</li></ol>' },
+
+ { 'id': 'IOL_TEXT-1_SI',
+ 'desc': 'Insert ordered list on selected text',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<ol><li>foo[bar]baz</li></ol>' }
+ ]
+ },
+
+ { 'desc': 'insert <ul>',
+ 'command': 'insertunorderedlist',
+ 'tests': [
+ { 'id': 'IUL_TEXT-1_SC',
+ 'desc': 'Insert unordered list on collapsed selection',
+ 'pad': 'foo^bar',
+ 'expected': '<ul><li>foo^bar</li></ul>' },
+
+ { 'id': 'IUL_TEXT-1_SI',
+ 'rte1-id': 'a-insertunorderedlist-0',
+ 'desc': 'Insert unordered list on selected text',
+ 'pad': 'foo[bar]baz',
+ 'expected': '<ul><li>foo[bar]baz</li></ul>' }
+ ]
+ },
+
+ { 'desc': 'insert arbitrary HTML',
+ 'command': 'inserthtml',
+ 'tests': [
+ { 'id': 'IHTML:BR_TEXT-1_SC',
+ 'rte1-id': 'a-inserthtml-0',
+ 'desc': 'InsertHTML: <br>',
+ 'value': '<br>',
+ 'pad': 'foo^barbaz',
+ 'expected': 'foo<br>^barbaz' },
+
+ { 'id': 'IHTML:text_TEXT-1_SI',
+ 'desc': 'InsertHTML: "NEW"',
+ 'value': 'NEW',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'fooNEW^baz' },
+
+ { 'id': 'IHTML:S_TEXT-1_SI',
+ 'desc': 'InsertHTML: "<span>NEW<span>"',
+ 'value': '<span>NEW</span>',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<span>NEW</span>^baz' },
+
+ { 'id': 'IHTML:H1.H2_TEXT-1_SI',
+ 'desc': 'InsertHTML: "<h1>NEW</h1><h2>HTML</h2>"',
+ 'value': '<h1>NEW</h1><h2>HTML</h2>',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<h1>NEW</h1><h2>HTML</h2>^baz' },
+
+ { 'id': 'IHTML:P-B_TEXT-1_SI',
+ 'desc': 'InsertHTML: "<p>NEW<b>HTML</b>!</p>"',
+ 'value': '<p>NEW<b>HTML</b>!</p>',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'foo<p>NEW<b>HTML</b>!</p>^baz' }
+ ]
+ }
+ ]
+}
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryEnabled.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryEnabled.py
new file mode 100644
index 0000000000..eb721923b6
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryEnabled.py
@@ -0,0 +1,215 @@
+
+QUERYENABLED_TESTS = {
+ 'id': 'QE',
+ 'caption': 'queryCommandEnabled Tests',
+ 'pad': 'foo[bar]baz',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': False,
+ 'expected': True,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'HTML5 commands',
+ 'tests': [
+ { 'id': 'SELECTALL_TEXT-1',
+ 'desc': 'check whether the "selectall" command is enabled',
+ 'qcenabled': 'selectall' },
+
+ { 'id': 'UNSELECT_TEXT-1',
+ 'desc': 'check whether the "unselect" command is enabled',
+ 'qcenabled': 'unselect' },
+
+ { 'id': 'UNDO_TEXT-1',
+ 'desc': 'check whether the "undo" command is enabled',
+ 'qcenabled': 'undo' },
+
+ { 'id': 'REDO_TEXT-1',
+ 'desc': 'check whether the "redo" command is enabled',
+ 'qcenabled': 'redo' },
+
+ { 'id': 'BOLD_TEXT-1',
+ 'desc': 'check whether the "bold" command is enabled',
+ 'qcenabled': 'bold' },
+
+ { 'id': 'ITALIC_TEXT-1',
+ 'desc': 'check whether the "italic" command is enabled',
+ 'qcenabled': 'italic' },
+
+ { 'id': 'UNDERLINE_TEXT-1',
+ 'desc': 'check whether the "underline" command is enabled',
+ 'qcenabled': 'underline' },
+
+ { 'id': 'STRIKETHROUGH_TEXT-1',
+ 'desc': 'check whether the "strikethrough" command is enabled',
+ 'qcenabled': 'strikethrough' },
+
+ { 'id': 'SUBSCRIPT_TEXT-1',
+ 'desc': 'check whether the "subscript" command is enabled',
+ 'qcenabled': 'subscript' },
+
+ { 'id': 'SUPERSCRIPT_TEXT-1',
+ 'desc': 'check whether the "superscript" command is enabled',
+ 'qcenabled': 'superscript' },
+
+ { 'id': 'FORMATBLOCK_TEXT-1',
+ 'desc': 'check whether the "formatblock" command is enabled',
+ 'qcenabled': 'formatblock' },
+
+ { 'id': 'CREATELINK_TEXT-1',
+ 'desc': 'check whether the "createlink" command is enabled',
+ 'qcenabled': 'createlink' },
+
+ { 'id': 'UNLINK_TEXT-1',
+ 'desc': 'check whether the "unlink" command is enabled',
+ 'qcenabled': 'unlink' },
+
+ { 'id': 'INSERTHTML_TEXT-1',
+ 'desc': 'check whether the "inserthtml" command is enabled',
+ 'qcenabled': 'inserthtml' },
+
+ { 'id': 'INSERTHORIZONTALRULE_TEXT-1',
+ 'desc': 'check whether the "inserthorizontalrule" command is enabled',
+ 'qcenabled': 'inserthorizontalrule' },
+
+ { 'id': 'INSERTIMAGE_TEXT-1',
+ 'desc': 'check whether the "insertimage" command is enabled',
+ 'qcenabled': 'insertimage' },
+
+ { 'id': 'INSERTLINEBREAK_TEXT-1',
+ 'desc': 'check whether the "insertlinebreak" command is enabled',
+ 'qcenabled': 'insertlinebreak' },
+
+ { 'id': 'INSERTPARAGRAPH_TEXT-1',
+ 'desc': 'check whether the "insertparagraph" command is enabled',
+ 'qcenabled': 'insertparagraph' },
+
+ { 'id': 'INSERTORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertorderedlist" command is enabled',
+ 'qcenabled': 'insertorderedlist' },
+
+ { 'id': 'INSERTUNORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertunorderedlist" command is enabled',
+ 'qcenabled': 'insertunorderedlist' },
+
+ { 'id': 'INSERTTEXT_TEXT-1',
+ 'desc': 'check whether the "inserttext" command is enabled',
+ 'qcenabled': 'inserttext' },
+
+ { 'id': 'DELETE_TEXT-1',
+ 'desc': 'check whether the "delete" command is enabled',
+ 'qcenabled': 'delete' },
+
+ { 'id': 'FORWARDDELETE_TEXT-1',
+ 'desc': 'check whether the "forwarddelete" command is enabled',
+ 'qcenabled': 'forwarddelete' }
+ ]
+ },
+
+ { 'desc': 'MIDAS commands',
+ 'tests': [
+ { 'id': 'STYLEWITHCSS_TEXT-1',
+ 'desc': 'check whether the "styleWithCSS" command is enabled',
+ 'qcenabled': 'styleWithCSS' },
+
+ { 'id': 'CONTENTREADONLY_TEXT-1',
+ 'desc': 'check whether the "contentreadonly" command is enabled',
+ 'qcenabled': 'contentreadonly' },
+
+ { 'id': 'BACKCOLOR_TEXT-1',
+ 'desc': 'check whether the "backcolor" command is enabled',
+ 'qcenabled': 'backcolor' },
+
+ { 'id': 'FORECOLOR_TEXT-1',
+ 'desc': 'check whether the "forecolor" command is enabled',
+ 'qcenabled': 'forecolor' },
+
+ { 'id': 'HILITECOLOR_TEXT-1',
+ 'desc': 'check whether the "hilitecolor" command is enabled',
+ 'qcenabled': 'hilitecolor' },
+
+ { 'id': 'FONTNAME_TEXT-1',
+ 'desc': 'check whether the "fontname" command is enabled',
+ 'qcenabled': 'fontname' },
+
+ { 'id': 'FONTSIZE_TEXT-1',
+ 'desc': 'check whether the "fontsize" command is enabled',
+ 'qcenabled': 'fontsize' },
+
+ { 'id': 'INCREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "increasefontsize" command is enabled',
+ 'qcenabled': 'increasefontsize' },
+
+ { 'id': 'DECREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "decreasefontsize" command is enabled',
+ 'qcenabled': 'decreasefontsize' },
+
+ { 'id': 'HEADING_TEXT-1',
+ 'desc': 'check whether the "heading" command is enabled',
+ 'qcenabled': 'heading' },
+
+ { 'id': 'INDENT_TEXT-1',
+ 'desc': 'check whether the "indent" command is enabled',
+ 'qcenabled': 'indent' },
+
+ { 'id': 'OUTDENT_TEXT-1',
+ 'desc': 'check whether the "outdent" command is enabled',
+ 'qcenabled': 'outdent' },
+
+ { 'id': 'CREATEBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "createbookmark" command is enabled',
+ 'qcenabled': 'createbookmark' },
+
+ { 'id': 'UNBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "unbookmark" command is enabled',
+ 'qcenabled': 'unbookmark' },
+
+ { 'id': 'JUSTIFYCENTER_TEXT-1',
+ 'desc': 'check whether the "justifycenter" command is enabled',
+ 'qcenabled': 'justifycenter' },
+
+ { 'id': 'JUSTIFYFULL_TEXT-1',
+ 'desc': 'check whether the "justifyfull" command is enabled',
+ 'qcenabled': 'justifyfull' },
+
+ { 'id': 'JUSTIFYLEFT_TEXT-1',
+ 'desc': 'check whether the "justifyleft" command is enabled',
+ 'qcenabled': 'justifyleft' },
+
+ { 'id': 'JUSTIFYRIGHT_TEXT-1',
+ 'desc': 'check whether the "justifyright" command is enabled',
+ 'qcenabled': 'justifyright' },
+
+ { 'id': 'REMOVEFORMAT_TEXT-1',
+ 'desc': 'check whether the "removeformat" command is enabled',
+ 'qcenabled': 'removeformat' },
+
+ { 'id': 'COPY_TEXT-1',
+ 'desc': 'check whether the "copy" command is enabled',
+ 'qcenabled': 'copy' },
+
+ { 'id': 'CUT_TEXT-1',
+ 'desc': 'check whether the "cut" command is enabled',
+ 'qcenabled': 'cut' },
+
+ { 'id': 'PASTE_TEXT-1',
+ 'desc': 'check whether the "paste" command is enabled',
+ 'qcenabled': 'paste' }
+ ]
+ },
+
+ { 'desc': 'Other tests',
+ 'tests': [
+ { 'id': 'garbage-1_TEXT-1',
+ 'desc': 'check correct return value with garbage input',
+ 'qcenabled': '#!#@7',
+ 'expected': False }
+ ]
+ }
+ ]
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryIndeterm.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryIndeterm.py
new file mode 100644
index 0000000000..d1ad8debdb
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryIndeterm.py
@@ -0,0 +1,214 @@
+
+QUERYINDETERM_TESTS = {
+ 'id': 'QI',
+ 'caption': 'queryCommandIndeterm Tests',
+ 'pad': 'foo[bar]baz',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': False,
+ 'expected': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'HTML5 commands',
+ 'tests': [
+ { 'id': 'SELECTALL_TEXT-1',
+ 'desc': 'check whether the "selectall" command is indeterminate',
+ 'qcindeterm': 'selectall' },
+
+ { 'id': 'UNSELECT_TEXT-1',
+ 'desc': 'check whether the "unselect" command is indeterminate',
+ 'qcindeterm': 'unselect' },
+
+ { 'id': 'UNDO_TEXT-1',
+ 'desc': 'check whether the "undo" command is indeterminate',
+ 'qcindeterm': 'undo' },
+
+ { 'id': 'REDO_TEXT-1',
+ 'desc': 'check whether the "redo" command is indeterminate',
+ 'qcindeterm': 'redo' },
+
+ { 'id': 'BOLD_TEXT-1',
+ 'desc': 'check whether the "bold" command is indeterminate',
+ 'qcindeterm': 'bold' },
+
+ { 'id': 'ITALIC_TEXT-1',
+ 'desc': 'check whether the "italic" command is indeterminate',
+ 'qcindeterm': 'italic' },
+
+ { 'id': 'UNDERLINE_TEXT-1',
+ 'desc': 'check whether the "underline" command is indeterminate',
+ 'qcindeterm': 'underline' },
+
+ { 'id': 'STRIKETHROUGH_TEXT-1',
+ 'desc': 'check whether the "strikethrough" command is indeterminate',
+ 'qcindeterm': 'strikethrough' },
+
+ { 'id': 'SUBSCRIPT_TEXT-1',
+ 'desc': 'check whether the "subscript" command is indeterminate',
+ 'qcindeterm': 'subscript' },
+
+ { 'id': 'SUPERSCRIPT_TEXT-1',
+ 'desc': 'check whether the "superscript" command is indeterminate',
+ 'qcindeterm': 'superscript' },
+
+ { 'id': 'FORMATBLOCK_TEXT-1',
+ 'desc': 'check whether the "formatblock" command is indeterminate',
+ 'qcindeterm': 'formatblock' },
+
+ { 'id': 'CREATELINK_TEXT-1',
+ 'desc': 'check whether the "createlink" command is indeterminate',
+ 'qcindeterm': 'createlink' },
+
+ { 'id': 'UNLINK_TEXT-1',
+ 'desc': 'check whether the "unlink" command is indeterminate',
+ 'qcindeterm': 'unlink' },
+
+ { 'id': 'INSERTHTML_TEXT-1',
+ 'desc': 'check whether the "inserthtml" command is indeterminate',
+ 'qcindeterm': 'inserthtml' },
+
+ { 'id': 'INSERTHORIZONTALRULE_TEXT-1',
+ 'desc': 'check whether the "inserthorizontalrule" command is indeterminate',
+ 'qcindeterm': 'inserthorizontalrule' },
+
+ { 'id': 'INSERTIMAGE_TEXT-1',
+ 'desc': 'check whether the "insertimage" command is indeterminate',
+ 'qcindeterm': 'insertimage' },
+
+ { 'id': 'INSERTLINEBREAK_TEXT-1',
+ 'desc': 'check whether the "insertlinebreak" command is indeterminate',
+ 'qcindeterm': 'insertlinebreak' },
+
+ { 'id': 'INSERTPARAGRAPH_TEXT-1',
+ 'desc': 'check whether the "insertparagraph" command is indeterminate',
+ 'qcindeterm': 'insertparagraph' },
+
+ { 'id': 'INSERTORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertorderedlist" command is indeterminate',
+ 'qcindeterm': 'insertorderedlist' },
+
+ { 'id': 'INSERTUNORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertunorderedlist" command is indeterminate',
+ 'qcindeterm': 'insertunorderedlist' },
+
+ { 'id': 'INSERTTEXT_TEXT-1',
+ 'desc': 'check whether the "inserttext" command is indeterminate',
+ 'qcindeterm': 'inserttext' },
+
+ { 'id': 'DELETE_TEXT-1',
+ 'desc': 'check whether the "delete" command is indeterminate',
+ 'qcindeterm': 'delete' },
+
+ { 'id': 'FORWARDDELETE_TEXT-1',
+ 'desc': 'check whether the "forwarddelete" command is indeterminate',
+ 'qcindeterm': 'forwarddelete' }
+ ]
+ },
+
+ { 'desc': 'MIDAS commands',
+ 'tests': [
+ { 'id': 'STYLEWITHCSS_TEXT-1',
+ 'desc': 'check whether the "styleWithCSS" command is indeterminate',
+ 'qcindeterm': 'styleWithCSS' },
+
+ { 'id': 'CONTENTREADONLY_TEXT-1',
+ 'desc': 'check whether the "contentreadonly" command is indeterminate',
+ 'qcindeterm': 'contentreadonly' },
+
+ { 'id': 'BACKCOLOR_TEXT-1',
+ 'desc': 'check whether the "backcolor" command is indeterminate',
+ 'qcindeterm': 'backcolor' },
+
+ { 'id': 'FORECOLOR_TEXT-1',
+ 'desc': 'check whether the "forecolor" command is indeterminate',
+ 'qcindeterm': 'forecolor' },
+
+ { 'id': 'HILITECOLOR_TEXT-1',
+ 'desc': 'check whether the "hilitecolor" command is indeterminate',
+ 'qcindeterm': 'hilitecolor' },
+
+ { 'id': 'FONTNAME_TEXT-1',
+ 'desc': 'check whether the "fontname" command is indeterminate',
+ 'qcindeterm': 'fontname' },
+
+ { 'id': 'FONTSIZE_TEXT-1',
+ 'desc': 'check whether the "fontsize" command is indeterminate',
+ 'qcindeterm': 'fontsize' },
+
+ { 'id': 'INCREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "increasefontsize" command is indeterminate',
+ 'qcindeterm': 'increasefontsize' },
+
+ { 'id': 'DECREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "decreasefontsize" command is indeterminate',
+ 'qcindeterm': 'decreasefontsize' },
+
+ { 'id': 'HEADING_TEXT-1',
+ 'desc': 'check whether the "heading" command is indeterminate',
+ 'qcindeterm': 'heading' },
+
+ { 'id': 'INDENT_TEXT-1',
+ 'desc': 'check whether the "indent" command is indeterminate',
+ 'qcindeterm': 'indent' },
+
+ { 'id': 'OUTDENT_TEXT-1',
+ 'desc': 'check whether the "outdent" command is indeterminate',
+ 'qcindeterm': 'outdent' },
+
+ { 'id': 'CREATEBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "createbookmark" command is indeterminate',
+ 'qcindeterm': 'createbookmark' },
+
+ { 'id': 'UNBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "unbookmark" command is indeterminate',
+ 'qcindeterm': 'unbookmark' },
+
+ { 'id': 'JUSTIFYCENTER_TEXT-1',
+ 'desc': 'check whether the "justifycenter" command is indeterminate',
+ 'qcindeterm': 'justifycenter' },
+
+ { 'id': 'JUSTIFYFULL_TEXT-1',
+ 'desc': 'check whether the "justifyfull" command is indeterminate',
+ 'qcindeterm': 'justifyfull' },
+
+ { 'id': 'JUSTIFYLEFT_TEXT-1',
+ 'desc': 'check whether the "justifyleft" command is indeterminate',
+ 'qcindeterm': 'justifyleft' },
+
+ { 'id': 'JUSTIFYRIGHT_TEXT-1',
+ 'desc': 'check whether the "justifyright" command is indeterminate',
+ 'qcindeterm': 'justifyright' },
+
+ { 'id': 'REMOVEFORMAT_TEXT-1',
+ 'desc': 'check whether the "removeformat" command is indeterminate',
+ 'qcindeterm': 'removeformat' },
+
+ { 'id': 'COPY_TEXT-1',
+ 'desc': 'check whether the "copy" command is indeterminate',
+ 'qcindeterm': 'copy' },
+
+ { 'id': 'CUT_TEXT-1',
+ 'desc': 'check whether the "cut" command is indeterminate',
+ 'qcindeterm': 'cut' },
+
+ { 'id': 'PASTE_TEXT-1',
+ 'desc': 'check whether the "paste" command is indeterminate',
+ 'qcindeterm': 'paste' }
+ ]
+ },
+
+ { 'desc': 'Other tests',
+ 'tests': [
+ { 'id': 'garbage-1_TEXT-1',
+ 'desc': 'check correct return value with garbage input',
+ 'qcindeterm': '#!#@7' }
+ ]
+ }
+ ]
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryState.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryState.py
new file mode 100644
index 0000000000..297559d625
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryState.py
@@ -0,0 +1,575 @@
+
+QUERYSTATE_TESTS = {
+ 'id': 'QS',
+ 'caption': 'queryCommandState Tests',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'qcstate': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'query bold state',
+ 'qcstate': 'bold',
+ 'tests': [
+ { 'id': 'B_TEXT_SI',
+ 'rte1-id': 'q-bold-0',
+ 'desc': 'query the "bold" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'B_B-1_SI',
+ 'rte1-id': 'q-bold-1',
+ 'desc': 'query the "bold" state',
+ 'pad': '<b>foo[bar]baz</b>',
+ 'expected': True },
+
+ { 'id': 'B_STRONG-1_SI',
+ 'rte1-id': 'q-bold-2',
+ 'desc': 'query the "bold" state',
+ 'pad': '<strong>foo[bar]baz</strong>',
+ 'expected': True },
+
+ { 'id': 'B_SPANs:fw:b-1_SI',
+ 'rte1-id': 'q-bold-3',
+ 'desc': 'query the "bold" state',
+ 'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'B_SPANs:fw:n-1_SI',
+ 'desc': 'query the "bold" state',
+ 'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
+ 'expected': False },
+
+ { 'id': 'B_Bs:fw:n-1_SI',
+ 'rte1-id': 'q-bold-4',
+ 'desc': 'query the "bold" state',
+ 'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
+ 'expected': False },
+
+ { 'id': 'B_B-SPANs:fw:n-1_SI',
+ 'rte1-id': 'q-bold-5',
+ 'desc': 'query the "bold" state',
+ 'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
+ 'expected': False },
+
+ { 'id': 'B_SPAN.b-1-SI',
+ 'desc': 'query the "bold" state',
+ 'pad': '<span class="b">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'B_MYB-1-SI',
+ 'desc': 'query the "bold" state',
+ 'pad': '<myb>foo[bar]baz</myb>',
+ 'expected': True },
+
+ { 'id': 'B_B-I-1_SC',
+ 'desc': 'query the "bold" state, bold tag not immediate parent',
+ 'pad': '<b>foo<i>ba^r</i>baz</b>',
+ 'expected': True },
+
+ { 'id': 'B_B-I-1_SL',
+ 'desc': 'query the "bold" state, selection partially in child element',
+ 'pad': '<b>fo[o<i>b]ar</i>baz</b>',
+ 'expected': True },
+
+ { 'id': 'B_B-I-1_SR',
+ 'desc': 'query the "bold" state, selection partially in child element',
+ 'pad': '<b>foo<i>ba[r</i>b]az</b>',
+ 'expected': True },
+
+ { 'id': 'B_STRONG-I-1_SC',
+ 'desc': 'query the "bold" state, bold tag not immediate parent',
+ 'pad': '<strong>foo<i>ba^r</i>baz</strong>',
+ 'expected': True },
+
+ { 'id': 'B_B-I-U-1_SC',
+ 'desc': 'query the "bold" state, bold tag not immediate parent',
+ 'pad': '<b>foo<i>bar<u>b^az</u></i></strong>',
+ 'expected': True },
+
+ { 'id': 'B_B-I-U-1_SM',
+ 'desc': 'query the "bold" state, bold tag not immediate parent',
+ 'pad': '<b>foo<i>ba[r<u>b]az</u></i></strong>',
+ 'expected': True },
+
+ { 'id': 'B_TEXT-B-1_SO-1',
+ 'desc': 'query the "bold" state, selection wrapping the bold tag',
+ 'pad': 'foo[<b>bar</b>]baz',
+ 'expected': True },
+
+ { 'id': 'B_TEXT-B-1_SO-2',
+ 'desc': 'query the "bold" state, selection wrapping the bold tag',
+ 'pad': 'foo{<b>bar</b>}baz',
+ 'expected': True },
+
+ { 'id': 'B_TEXT-B-1_SL',
+ 'desc': 'query the "bold" state, selection containing non-bold text',
+ 'pad': 'fo[o<b>ba]r</b>baz',
+ 'expected': False },
+
+ { 'id': 'B_TEXT-B-1_SR',
+ 'desc': 'query the "bold" state, selection containing non-bold text',
+ 'pad': 'foo<b>b[ar</b>b]az',
+ 'expected': False },
+
+ { 'id': 'B_TEXT-B-1_SO-3',
+ 'desc': 'query the "bold" state, selection containing non-bold text',
+ 'pad': 'fo[o<b>bar</b>b]az',
+ 'expected': False },
+
+ { 'id': 'B_B.TEXT.B-1_SM',
+ 'desc': 'query the "bold" state, selection including non-bold text',
+ 'pad': '<b>fo[o</b>bar<b>b]az</b>',
+ 'expected': False },
+
+ { 'id': 'B_B.B.B-1_SM',
+ 'desc': 'query the "bold" state, selection mixed, but all bold',
+ 'pad': '<b>fo[o</b><b>bar</b><b>b]az</b>',
+ 'expected': True },
+
+ { 'id': 'B_B.STRONG.B-1_SM',
+ 'desc': 'query the "bold" state, selection mixed, but all bold',
+ 'pad': '<b>fo[o</b><strong>bar</strong><b>b]az</b>',
+ 'expected': True },
+
+ { 'id': 'B_SPAN.b.MYB.SPANs:fw:b-1_SM',
+ 'desc': 'query the "bold" state, selection mixed, but all bold',
+ 'pad': '<span class="b">fo[o</span><myb>bar</myb><span style="font-weight: bold">b]az</span>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query italic state',
+ 'qcstate': 'italic',
+ 'tests': [
+ { 'id': 'I_TEXT_SI',
+ 'rte1-id': 'q-italic-0',
+ 'desc': 'query the "italic" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'I_I-1_SI',
+ 'rte1-id': 'q-italic-1',
+ 'desc': 'query the "italic" state',
+ 'pad': '<i>foo[bar]baz</i>',
+ 'expected': True },
+
+ { 'id': 'I_EM-1_SI',
+ 'rte1-id': 'q-italic-2',
+ 'desc': 'query the "italic" state',
+ 'pad': '<em>foo[bar]baz</em>',
+ 'expected': True },
+
+ { 'id': 'I_SPANs:fs:i-1_SI',
+ 'rte1-id': 'q-italic-3',
+ 'desc': 'query the "italic" state',
+ 'pad': '<span style="font-style: italic">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'I_SPANs:fs:n-1_SI',
+ 'desc': 'query the "italic" state',
+ 'pad': '<span style="font-style: normal">foo[bar]baz</span>',
+ 'expected': False },
+
+ { 'id': 'I_I-SPANs:fs:n-1_SI',
+ 'rte1-id': 'q-italic-4',
+ 'desc': 'query the "italic" state',
+ 'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
+ 'expected': False },
+
+ { 'id': 'I_SPAN.i-1-SI',
+ 'desc': 'query the "italic" state',
+ 'pad': '<span class="i">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'I_MYI-1-SI',
+ 'desc': 'query the "italic" state',
+ 'pad': '<myi>foo[bar]baz</myi>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query underline state',
+ 'qcstate': 'underline',
+ 'tests': [
+ { 'id': 'U_TEXT_SI',
+ 'rte1-id': 'q-underline-0',
+ 'desc': 'query the "underline" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'U_U-1_SI',
+ 'rte1-id': 'q-underline-1',
+ 'desc': 'query the "underline" state',
+ 'pad': '<u>foo[bar]baz</u>',
+ 'expected': True },
+
+ { 'id': 'U_Us:td:n-1_SI',
+ 'rte1-id': 'q-underline-4',
+ 'desc': 'query the "underline" state',
+ 'pad': '<u style="text-decoration: none">foo[bar]baz</u>',
+ 'expected': False },
+
+ { 'id': 'U_Ah:url-1_SI',
+ 'rte1-id': 'q-underline-2',
+ 'desc': 'query the "underline" state',
+ 'pad': '<a href="http://www.goo.gl">foo[bar]baz</a>',
+ 'expected': True },
+
+ { 'id': 'U_Ah:url.s:td:n-1_SI',
+ 'rte1-id': 'q-underline-5',
+ 'desc': 'query the "underline" state',
+ 'pad': '<a href="http://www.goo.gl" style="text-decoration: none">foo[bar]baz</a>',
+ 'expected': False },
+
+ { 'id': 'U_SPANs:td:u-1_SI',
+ 'rte1-id': 'q-underline-3',
+ 'desc': 'query the "underline" state',
+ 'pad': '<span style="text-decoration: underline">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'U_SPAN.u-1-SI',
+ 'desc': 'query the "underline" state',
+ 'pad': '<span class="u">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'U_MYU-1-SI',
+ 'desc': 'query the "underline" state',
+ 'pad': '<myu>foo[bar]baz</myu>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query strike-through state',
+ 'qcstate': 'strikethrough',
+ 'tests': [
+ { 'id': 'S_TEXT_SI',
+ 'rte1-id': 'q-strikethrough-0',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'S_S-1_SI',
+ 'rte1-id': 'q-strikethrough-3',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<s>foo[bar]baz</s>',
+ 'expected': True },
+
+ { 'id': 'S_STRIKE-1_SI',
+ 'rte1-id': 'q-strikethrough-1',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<strike>foo[bar]baz</strike>',
+ 'expected': True },
+
+ { 'id': 'S_STRIKEs:td:n-1_SI',
+ 'rte1-id': 'q-strikethrough-2',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<strike style="text-decoration: none">foo[bar]baz</strike>',
+ 'expected': False },
+
+ { 'id': 'S_DEL-1_SI',
+ 'rte1-id': 'q-strikethrough-4',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<del>foo[bar]baz</del>',
+ 'expected': True },
+
+ { 'id': 'S_SPANs:td:lt-1_SI',
+ 'rte1-id': 'q-strikethrough-5',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<span style="text-decoration: line-through">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'S_SPAN.s-1-SI',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<span class="s">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'S_MYS-1-SI',
+ 'desc': 'query the "strikethrough" state',
+ 'pad': '<mys>foo[bar]baz</mys>',
+ 'expected': True },
+
+ { 'id': 'S_S.STRIKE.DEL-1_SM',
+ 'desc': 'query the "strikethrough" state, selection mixed, but all struck',
+ 'pad': '<s>fo[o</s><strike>bar</strike><del>b]az</del>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query subscript state',
+ 'qcstate': 'subscript',
+ 'tests': [
+ { 'id': 'SUB_TEXT_SI',
+ 'rte1-id': 'q-subscript-0',
+ 'desc': 'query the "subscript" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'SUB_SUB-1_SI',
+ 'rte1-id': 'q-subscript-1',
+ 'desc': 'query the "subscript" state',
+ 'pad': '<sub>foo[bar]baz</sub>',
+ 'expected': True },
+
+ { 'id': 'SUB_SPAN.sub-1-SI',
+ 'desc': 'query the "subscript" state',
+ 'pad': '<span class="sub">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'SUB_MYSUB-1-SI',
+ 'desc': 'query the "subscript" state',
+ 'pad': '<mysub>foo[bar]baz</mysub>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query superscript state',
+ 'qcstate': 'superscript',
+ 'tests': [
+ { 'id': 'SUP_TEXT_SI',
+ 'rte1-id': 'q-superscript-0',
+ 'desc': 'query the "superscript" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'SUP_SUP-1_SI',
+ 'rte1-id': 'q-superscript-1',
+ 'desc': 'query the "superscript" state',
+ 'pad': '<sup>foo[bar]baz</sup>',
+ 'expected': True },
+
+ { 'id': 'IOL_TEXT_SI',
+ 'desc': 'query the "insertorderedlist" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'SUP_SPAN.sup-1-SI',
+ 'desc': 'query the "superscript" state',
+ 'pad': '<span class="sup">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'SUP_MYSUP-1-SI',
+ 'desc': 'query the "superscript" state',
+ 'pad': '<mysup>foo[bar]baz</mysup>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query whether the selection is in an ordered list',
+ 'qcstate': 'insertorderedlist',
+ 'tests': [
+ { 'id': 'IOL_TEXT-1_SI',
+ 'rte1-id': 'q-insertorderedlist-0',
+ 'desc': 'query the "insertorderedlist" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'IOL_OL-LI-1_SI',
+ 'rte1-id': 'q-insertorderedlist-1',
+ 'desc': 'query the "insertorderedlist" state',
+ 'pad': '<ol><li>foo[bar]baz</li></ol>',
+ 'expected': True },
+
+ { 'id': 'IOL_UL_LI-1_SI',
+ 'rte1-id': 'q-insertorderedlist-2',
+ 'desc': 'query the "insertorderedlist" state',
+ 'pad': '<ul><li>foo[bar]baz</li></ul>',
+ 'expected': False }
+ ]
+ },
+
+ { 'desc': 'query whether the selection is in an unordered list',
+ 'qcstate': 'insertunorderedlist',
+ 'tests': [
+ { 'id': 'IUL_TEXT_SI',
+ 'rte1-id': 'q-insertunorderedlist-0',
+ 'desc': 'query the "insertunorderedlist" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'IUL_OL-LI-1_SI',
+ 'rte1-id': 'q-insertunorderedlist-1',
+ 'desc': 'query the "insertunorderedlist" state',
+ 'pad': '<ol><li>foo[bar]baz</li></ol>',
+ 'expected': False },
+
+ { 'id': 'IUL_UL-LI-1_SI',
+ 'rte1-id': 'q-insertunorderedlist-2',
+ 'desc': 'query the "insertunorderedlist" state',
+ 'pad': '<ul><li>foo[bar]baz</li></ul>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query whether the paragraph is centered',
+ 'qcstate': 'justifycenter',
+ 'tests': [
+ { 'id': 'JC_TEXT_SI',
+ 'rte1-id': 'q-justifycenter-0',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'JC_DIVa:c-1_SI',
+ 'rte1-id': 'q-justifycenter-1',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': '<div align="center">foo[bar]baz</div>',
+ 'expected': True },
+
+ { 'id': 'JC_Pa:c-1_SI',
+ 'rte1-id': 'q-justifycenter-2',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': '<p align="center">foo[bar]baz</p>',
+ 'expected': True },
+
+ { 'id': 'JC_SPANs:ta:c-1_SI',
+ 'rte1-id': 'q-justifycenter-3',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': '<div style="text-align: center">foo[bar]baz</div>',
+ 'expected': True },
+
+ { 'id': 'JC_SPAN.jc-1-SI',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': '<span class="jc">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JC_MYJC-1-SI',
+ 'desc': 'query the "justifycenter" state',
+ 'pad': '<myjc>foo[bar]baz</myjc>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query whether the paragraph is justified',
+ 'qcstate': 'justifyfull',
+ 'tests': [
+ { 'id': 'JF_TEXT_SI',
+ 'rte1-id': 'q-justifyfull-0',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'JF_DIVa:j-1_SI',
+ 'rte1-id': 'q-justifyfull-1',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': '<div align="justify">foo[bar]baz</div>',
+ 'expected': True },
+
+ { 'id': 'JF_Pa:j-1_SI',
+ 'rte1-id': 'q-justifyfull-2',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': '<p align="justify">foo[bar]baz</p>',
+ 'expected': True },
+
+ { 'id': 'JF_SPANs:ta:j-1_SI',
+ 'rte1-id': 'q-justifyfull-3',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': '<span style="text-align: justify">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JF_SPAN.jf-1-SI',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': '<span class="jf">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JF_MYJF-1-SI',
+ 'desc': 'query the "justifyfull" state',
+ 'pad': '<myjf>foo[bar]baz</myjf>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query whether the paragraph is aligned left',
+ 'qcstate': 'justifyleft',
+ 'tests': [
+ { 'id': 'JL_TEXT_SI',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'JL_DIVa:l-1_SI',
+ 'rte1-id': 'q-justifyleft-0',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': '<div align="left">foo[bar]baz</div>',
+ 'expected': True },
+
+ { 'id': 'JL_Pa:l-1_SI',
+ 'rte1-id': 'q-justifyleft-1',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': '<p align="left">foo[bar]baz</p>',
+ 'expected': True },
+
+ { 'id': 'JL_SPANs:ta:l-1_SI',
+ 'rte1-id': 'q-justifyleft-2',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': '<span style="text-align: left">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JL_SPAN.jl-1-SI',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': '<span class="jl">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JL_MYJL-1-SI',
+ 'desc': 'query the "justifyleft" state',
+ 'pad': '<myjl>foo[bar]baz</myjl>',
+ 'expected': True }
+ ]
+ },
+
+ { 'desc': 'query whether the paragraph is aligned right',
+ 'qcstate': 'justifyright',
+ 'tests': [
+ { 'id': 'JR_TEXT_SI',
+ 'rte1-id': 'q-justifyright-0',
+ 'desc': 'query the "justifyright" state',
+ 'pad': 'foo[bar]baz',
+ 'expected': False },
+
+ { 'id': 'JR_DIVa:r-1_SI',
+ 'rte1-id': 'q-justifyright-1',
+ 'desc': 'query the "justifyright" state',
+ 'pad': '<div align="right">foo[bar]baz</div>',
+ 'expected': True },
+
+ { 'id': 'JR_Pa:r-1_SI',
+ 'rte1-id': 'q-justifyright-2',
+ 'desc': 'query the "justifyright" state',
+ 'pad': '<p align="right">foo[bar]baz</p>',
+ 'expected': True },
+
+ { 'id': 'JR_SPANs:ta:r-1_SI',
+ 'rte1-id': 'q-justifyright-3',
+ 'desc': 'query the "justifyright" state',
+ 'pad': '<span style="text-align: right">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JR_SPAN.jr-1-SI',
+ 'desc': 'query the "justifyright" state',
+ 'pad': '<span class="jr">foo[bar]baz</span>',
+ 'expected': True },
+
+ { 'id': 'JR_MYJR-1-SI',
+ 'desc': 'query the "justifyright" state',
+ 'pad': '<myjr>foo[bar]baz</myjr>',
+ 'expected': True }
+ ]
+ }
+ ]
+}
+
+QUERYSTATE_TESTS_CSS = {
+ 'id': 'QSC',
+ 'caption': 'queryCommandState Tests, using styleWithCSS',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': True,
+
+ 'Proposed': QUERYSTATE_TESTS['Proposed']
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/querySupported.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/querySupported.py
new file mode 100644
index 0000000000..af23a428ce
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/querySupported.py
@@ -0,0 +1,226 @@
+
+QUERYSUPPORTED_TESTS = {
+ 'id': 'Q',
+ 'caption': 'queryCommandSupported Tests',
+ 'pad': 'foo[bar]baz',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': False,
+ 'expected': True,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'HTML5 commands',
+ 'tests': [
+ { 'id': 'SELECTALL_TEXT-1',
+ 'desc': 'check whether the "selectall" command is supported',
+ 'qcsupported': 'selectall' },
+
+ { 'id': 'UNSELECT_TEXT-1',
+ 'desc': 'check whether the "unselect" command is supported',
+ 'qcsupported': 'unselect' },
+
+ { 'id': 'UNDO_TEXT-1',
+ 'desc': 'check whether the "undo" command is supported',
+ 'qcsupported': 'undo' },
+
+ { 'id': 'REDO_TEXT-1',
+ 'desc': 'check whether the "redo" command is supported',
+ 'qcsupported': 'redo' },
+
+ { 'id': 'BOLD_TEXT-1',
+ 'desc': 'check whether the "bold" command is supported',
+ 'qcsupported': 'bold' },
+
+ { 'id': 'BOLD_B',
+ 'desc': 'check whether the "bold" command is supported',
+ 'qcsupported': 'bold',
+ 'pad': '<b>foo[bar]baz</b>' },
+
+ { 'id': 'ITALIC_TEXT-1',
+ 'desc': 'check whether the "italic" command is supported',
+ 'qcsupported': 'italic' },
+
+ { 'id': 'ITALIC_I',
+ 'desc': 'check whether the "italic" command is supported',
+ 'qcsupported': 'italic',
+ 'pad': '<i>foo[bar]baz</i>' },
+
+ { 'id': 'UNDERLINE_TEXT-1',
+ 'desc': 'check whether the "underline" command is supported',
+ 'qcsupported': 'underline' },
+
+ { 'id': 'STRIKETHROUGH_TEXT-1',
+ 'desc': 'check whether the "strikethrough" command is supported',
+ 'qcsupported': 'strikethrough' },
+
+ { 'id': 'SUBSCRIPT_TEXT-1',
+ 'desc': 'check whether the "subscript" command is supported',
+ 'qcsupported': 'subscript' },
+
+ { 'id': 'SUPERSCRIPT_TEXT-1',
+ 'desc': 'check whether the "superscript" command is supported',
+ 'qcsupported': 'superscript' },
+
+ { 'id': 'FORMATBLOCK_TEXT-1',
+ 'desc': 'check whether the "formatblock" command is supported',
+ 'qcsupported': 'formatblock' },
+
+ { 'id': 'CREATELINK_TEXT-1',
+ 'desc': 'check whether the "createlink" command is supported',
+ 'qcsupported': 'createlink' },
+
+ { 'id': 'UNLINK_TEXT-1',
+ 'desc': 'check whether the "unlink" command is supported',
+ 'qcsupported': 'unlink' },
+
+ { 'id': 'INSERTHTML_TEXT-1',
+ 'desc': 'check whether the "inserthtml" command is supported',
+ 'qcsupported': 'inserthtml' },
+
+ { 'id': 'INSERTHORIZONTALRULE_TEXT-1',
+ 'desc': 'check whether the "inserthorizontalrule" command is supported',
+ 'qcsupported': 'inserthorizontalrule' },
+
+ { 'id': 'INSERTIMAGE_TEXT-1',
+ 'desc': 'check whether the "insertimage" command is supported',
+ 'qcsupported': 'insertimage' },
+
+ { 'id': 'INSERTLINEBREAK_TEXT-1',
+ 'desc': 'check whether the "insertlinebreak" command is supported',
+ 'qcsupported': 'insertlinebreak' },
+
+ { 'id': 'INSERTPARAGRAPH_TEXT-1',
+ 'desc': 'check whether the "insertparagraph" command is supported',
+ 'qcsupported': 'insertparagraph' },
+
+ { 'id': 'INSERTORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertorderedlist" command is supported',
+ 'qcsupported': 'insertorderedlist' },
+
+ { 'id': 'INSERTUNORDEREDLIST_TEXT-1',
+ 'desc': 'check whether the "insertunorderedlist" command is supported',
+ 'qcsupported': 'insertunorderedlist' },
+
+ { 'id': 'INSERTTEXT_TEXT-1',
+ 'desc': 'check whether the "inserttext" command is supported',
+ 'qcsupported': 'inserttext' },
+
+ { 'id': 'DELETE_TEXT-1',
+ 'desc': 'check whether the "delete" command is supported',
+ 'qcsupported': 'delete' },
+
+ { 'id': 'FORWARDDELETE_TEXT-1',
+ 'desc': 'check whether the "forwarddelete" command is supported',
+ 'qcsupported': 'forwarddelete' }
+ ]
+ },
+
+ { 'desc': 'MIDAS commands',
+ 'tests': [
+ { 'id': 'STYLEWITHCSS_TEXT-1',
+ 'desc': 'check whether the "styleWithCSS" command is supported',
+ 'qcsupported': 'styleWithCSS' },
+
+ { 'id': 'CONTENTREADONLY_TEXT-1',
+ 'desc': 'check whether the "contentreadonly" command is supported',
+ 'qcsupported': 'contentreadonly' },
+
+ { 'id': 'BACKCOLOR_TEXT-1',
+ 'desc': 'check whether the "backcolor" command is supported',
+ 'qcsupported': 'backcolor' },
+
+ { 'id': 'FORECOLOR_TEXT-1',
+ 'desc': 'check whether the "forecolor" command is supported',
+ 'qcsupported': 'forecolor' },
+
+ { 'id': 'HILITECOLOR_TEXT-1',
+ 'desc': 'check whether the "hilitecolor" command is supported',
+ 'qcsupported': 'hilitecolor' },
+
+ { 'id': 'FONTNAME_TEXT-1',
+ 'desc': 'check whether the "fontname" command is supported',
+ 'qcsupported': 'fontname' },
+
+ { 'id': 'FONTSIZE_TEXT-1',
+ 'desc': 'check whether the "fontsize" command is supported',
+ 'qcsupported': 'fontsize' },
+
+ { 'id': 'INCREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "increasefontsize" command is supported',
+ 'qcsupported': 'increasefontsize' },
+
+ { 'id': 'DECREASEFONTSIZE_TEXT-1',
+ 'desc': 'check whether the "decreasefontsize" command is supported',
+ 'qcsupported': 'decreasefontsize' },
+
+ { 'id': 'HEADING_TEXT-1',
+ 'desc': 'check whether the "heading" command is supported',
+ 'qcsupported': 'heading' },
+
+ { 'id': 'INDENT_TEXT-1',
+ 'desc': 'check whether the "indent" command is supported',
+ 'qcsupported': 'indent' },
+
+ { 'id': 'OUTDENT_TEXT-1',
+ 'desc': 'check whether the "outdent" command is supported',
+ 'qcsupported': 'outdent' },
+
+ { 'id': 'CREATEBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "createbookmark" command is supported',
+ 'qcsupported': 'createbookmark' },
+
+ { 'id': 'UNBOOKMARK_TEXT-1',
+ 'desc': 'check whether the "unbookmark" command is supported',
+ 'qcsupported': 'unbookmark' },
+
+ { 'id': 'JUSTIFYCENTER_TEXT-1',
+ 'desc': 'check whether the "justifycenter" command is supported',
+ 'qcsupported': 'justifycenter' },
+
+ { 'id': 'JUSTIFYFULL_TEXT-1',
+ 'desc': 'check whether the "justifyfull" command is supported',
+ 'qcsupported': 'justifyfull' },
+
+ { 'id': 'JUSTIFYLEFT_TEXT-1',
+ 'desc': 'check whether the "justifyleft" command is supported',
+ 'qcsupported': 'justifyleft' },
+
+ { 'id': 'JUSTIFYRIGHT_TEXT-1',
+ 'desc': 'check whether the "justifyright" command is supported',
+ 'qcsupported': 'justifyright' },
+
+ { 'id': 'REMOVEFORMAT_TEXT-1',
+ 'desc': 'check whether the "removeformat" command is supported',
+ 'qcsupported': 'removeformat' },
+
+ { 'id': 'COPY_TEXT-1',
+ 'desc': 'check whether the "copy" command is supported',
+ 'qcsupported': 'copy' },
+
+ { 'id': 'CUT_TEXT-1',
+ 'desc': 'check whether the "cut" command is supported',
+ 'qcsupported': 'cut' },
+
+ { 'id': 'PASTE_TEXT-1',
+ 'desc': 'check whether the "paste" command is supported',
+ 'qcsupported': 'paste' }
+ ]
+ },
+
+ { 'desc': 'Other tests',
+ 'tests': [
+ { 'id': 'garbage-1_TEXT-1',
+ 'desc': 'check correct return value with garbage input',
+ 'qcsupported': '#!#@7',
+ 'expected': False }
+ ]
+ }
+ ]
+}
+
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryValue.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryValue.py
new file mode 100644
index 0000000000..793b7cb6cf
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/queryValue.py
@@ -0,0 +1,429 @@
+
+QUERYVALUE_TESTS = {
+ 'id': 'QV',
+ 'caption': 'queryCommandValue Tests',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': '[HTML5] query bold value',
+ 'qcvalue': 'bold',
+ 'tests': [
+ { 'id': 'B_TEXT_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'false' },
+
+ { 'id': 'B_B-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<b>foo[bar]baz</b>',
+ 'expected': 'true' },
+
+ { 'id': 'B_STRONG-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<strong>foo[bar]baz</strong>',
+ 'expected': 'true' },
+
+ { 'id': 'B_SPANs:fw:b-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
+ 'expected': 'true' },
+
+ { 'id': 'B_SPANs:fw:n-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
+ 'expected': 'false' },
+
+ { 'id': 'B_Bs:fw:n-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
+ 'expected': 'false' },
+
+ { 'id': 'B_SPAN.b-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<span class="b">foo[bar]baz</span>',
+ 'expected': 'true' },
+
+ { 'id': 'B_MYB-1-SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<myb>foo[bar]baz</myb>',
+ 'expected': 'true' }
+ ]
+ },
+
+ { 'desc': '[HTML5] query italic value',
+ 'qcvalue': 'italic',
+ 'tests': [
+ { 'id': 'I_TEXT_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': 'foo[bar]baz',
+ 'expected': 'false' },
+
+ { 'id': 'I_I-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<i>foo[bar]baz</i>',
+ 'expected': 'true' },
+
+ { 'id': 'I_EM-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<em>foo[bar]baz</em>',
+ 'expected': 'true' },
+
+ { 'id': 'I_SPANs:fs:i-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<span style="font-style: italic">foo[bar]baz</span>',
+ 'expected': 'true' },
+
+ { 'id': 'I_SPANs:fs:n-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<span style="font-style: normal">foo[bar]baz</span>',
+ 'expected': 'false' },
+
+ { 'id': 'I_I-SPANs:fs:n-1_SI',
+ 'desc': 'query the "bold" value',
+ 'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
+ 'expected': 'false' },
+
+ { 'id': 'I_SPAN.i-1_SI',
+ 'desc': 'query the "italic" value',
+ 'pad': '<span class="i">foo[bar]baz</span>',
+ 'expected': 'true' },
+
+ { 'id': 'I_MYI-1-SI',
+ 'desc': 'query the "italic" value',
+ 'pad': '<myi>foo[bar]baz</myi>',
+ 'expected': 'true' }
+ ]
+ },
+
+ { 'desc': '[HTML5] query block formatting value',
+ 'qcvalue': 'formatblock',
+ 'tests': [
+ { 'id': 'FB_TEXT-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': 'foobar^baz',
+ 'expected': '',
+ 'accept': 'normal' },
+
+ { 'id': 'FB_H1-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': '<h1>foobar^baz</h1>',
+ 'expected': 'h1' },
+
+ { 'id': 'FB_PRE-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': '<pre>foobar^baz</pre>',
+ 'expected': 'pre' },
+
+ { 'id': 'FB_BQ-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': '<blockquote>foobar^baz</blockquote>',
+ 'expected': 'blockquote' },
+
+ { 'id': 'FB_ADDRESS-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': '<address>foobar^baz</address>',
+ 'expected': 'address' },
+
+ { 'id': 'FB_H1-H2-1_SC',
+ 'desc': 'query the "formatBlock" value',
+ 'pad': '<h1>foo<h2>ba^r</h2>baz</h1>',
+ 'expected': 'h2' },
+
+ { 'id': 'FB_H1-H2-1_SL',
+ 'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
+ 'pad': '<h1>fo[o<h2>ba]r</h2>baz</h1>',
+ 'expected': 'h1' },
+
+ { 'id': 'FB_H1-H2-1_SR',
+ 'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
+ 'pad': '<h1>foo<h2>b[ar</h2>ba]z</h1>',
+ 'expected': 'h1' },
+
+ { 'id': 'FB_TEXT-ADDRESS-1_SL',
+ 'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
+ 'pad': 'fo[o<ADDRESS>ba]r</ADDRESS>baz',
+ 'expected': '',
+ 'accept': 'normal' },
+
+ { 'id': 'FB_TEXT-ADDRESS-1_SR',
+ 'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
+ 'pad': 'foo<ADDRESS>b[ar</ADDRESS>ba]z',
+ 'expected': '',
+ 'accept': 'normal' },
+
+ { 'id': 'FB_H1-H2.TEXT.H2-1_SM',
+ 'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
+ 'pad': '<h1><h2>fo[o</h2>bar<h2>b]az</h2></h1>',
+ 'expected': 'h1' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query heading type',
+ 'qcvalue': 'heading',
+ 'tests': [
+ { 'id': 'H_H1-1_SC',
+ 'desc': 'query the "heading" type',
+ 'pad': '<h1>foobar^baz</h1>',
+ 'expected': 'h1',
+ 'accept': '<h1>' },
+
+ { 'id': 'H_H3-1_SC',
+ 'desc': 'query the "heading" type',
+ 'pad': '<h3>foobar^baz</h3>',
+ 'expected': 'h3',
+ 'accept': '<h3>' },
+
+ { 'id': 'H_H1-H2-H3-H4-1_SC',
+ 'desc': 'query the "heading" type within nested heading tags',
+ 'pad': '<h1><h2><h3><h4>foobar^baz</h4></h3></h2></h1>',
+ 'expected': 'h4',
+ 'accept': '<h4>' },
+
+ { 'id': 'H_P-1_SC',
+ 'desc': 'query the "heading" type outside of a heading',
+ 'pad': '<p>foobar^baz</p>',
+ 'expected': '' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query font name',
+ 'qcvalue': 'fontname',
+ 'tests': [
+ { 'id': 'FN_FONTf:a-1_SI',
+ 'rte1-id': 'q-fontname-0',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<font face="arial">foo[bar]baz</font>',
+ 'expected': 'arial' },
+
+ { 'id': 'FN_SPANs:ff:a-1_SI',
+ 'rte1-id': 'q-fontname-1',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<span style="font-family: arial">foo[bar]baz</span>',
+ 'expected': 'arial' },
+
+ { 'id': 'FN_FONTf:a.s:ff:c-1_SI',
+ 'rte1-id': 'q-fontname-2',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<font face="arial" style="font-family: courier">foo[bar]baz</font>',
+ 'expected': 'courier' },
+
+ { 'id': 'FN_FONTf:a-FONTf:c-1_SI',
+ 'rte1-id': 'q-fontname-3',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<font face="arial"><font face="courier">foo[bar]baz</font></font>',
+ 'expected': 'courier' },
+
+ { 'id': 'FN_SPANs:ff:c-FONTf:a-1_SI',
+ 'rte1-id': 'q-fontname-4',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<span style="font-family: courier"><font face="arial">foo[bar]baz</font></span>',
+ 'expected': 'arial' },
+
+ { 'id': 'FN_SPAN.fs18px-1_SI',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<span class="courier">foo[bar]baz</span>',
+ 'expected': 'courier' },
+
+ { 'id': 'FN_MYCOURIER-1-SI',
+ 'desc': 'query the "fontname" value',
+ 'pad': '<mycourier>foo[bar]baz</mycourier>',
+ 'expected': 'courier' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query font size',
+ 'qcvalue': 'fontsize',
+ 'tests': [
+ { 'id': 'FS_FONTsz:4-1_SI',
+ 'rte1-id': 'q-fontsize-0',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<font size=4>foo[bar]baz</font>',
+ 'expected': '18px' },
+
+ { 'id': 'FS_FONTs:fs:l-1_SI',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<font style="font-size: large">foo[bar]baz</font>',
+ 'expected': '18px' },
+
+ { 'id': 'FS_FONT.ass.s:fs:l-1_SI',
+ 'rte1-id': 'q-fontsize-1',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<font class="Apple-style-span" style="font-size: large">foo[bar]baz</font>',
+ 'expected': '18px' },
+
+ { 'id': 'FS_FONTsz:1.s:fs:xl-1_SI',
+ 'rte1-id': 'q-fontsize-2',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<font size=1 style="font-size: x-large">foo[bar]baz</font>',
+ 'expected': '24px' },
+
+ { 'id': 'FS_SPAN.large-1_SI',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<span class="large">foo[bar]baz</span>',
+ 'expected': 'large' },
+
+ { 'id': 'FS_SPAN.fs18px-1_SI',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<span class="fs18px">foo[bar]baz</span>',
+ 'expected': '18px' },
+
+ { 'id': 'FA_MYLARGE-1-SI',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<mylarge>foo[bar]baz</mylarge>',
+ 'expected': 'large' },
+
+ { 'id': 'FA_MYFS18PX-1-SI',
+ 'desc': 'query the "fontsize" value',
+ 'pad': '<myfs18px>foo[bar]baz</myfs18px>',
+ 'expected': '18px' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query background color',
+ 'qcvalue': 'backcolor',
+ 'tests': [
+ { 'id': 'BC_FONTs:bc:fca-1_SI',
+ 'rte1-id': 'q-backcolor-0',
+ 'desc': 'query the "backcolor" value',
+ 'pad': '<font style="background-color: #ffccaa">foo[bar]baz</font>',
+ 'expected': '#ffccaa' },
+
+ { 'id': 'BC_SPANs:bc:abc-1_SI',
+ 'rte1-id': 'q-backcolor-2',
+ 'desc': 'query the "backcolor" value',
+ 'pad': '<span style="background-color: #aabbcc">foo[bar]baz</span>',
+ 'expected': '#aabbcc' },
+
+ { 'id': 'BC_FONTs:bc:084-SPAN-1_SI',
+ 'desc': 'query the "backcolor" value, where the color was set on an ancestor',
+ 'pad': '<font style="background-color: #008844"><span>foo[bar]baz</span></font>',
+ 'expected': '#008844' },
+
+ { 'id': 'BC_SPANs:bc:cde-SPAN-1_SI',
+ 'desc': 'query the "backcolor" value, where the color was set on an ancestor',
+ 'pad': '<span style="background-color: #ccddee"><span>foo[bar]baz</span></span>',
+ 'expected': '#ccddee' },
+
+ { 'id': 'BC_SPAN.ass.s:bc:rgb-1_SI',
+ 'rte1-id': 'q-backcolor-1',
+ 'desc': 'query the "backcolor" value',
+ 'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
+ 'expected': '#ff0000' },
+
+ { 'id': 'BC_SPAN.bcred-1_SI',
+ 'desc': 'query the "backcolor" value',
+ 'pad': '<span class="bcred">foo[bar]baz</span>',
+ 'expected': 'red' },
+
+ { 'id': 'BC_MYBCRED-1-SI',
+ 'desc': 'query the "backcolor" value',
+ 'pad': '<mybcred>foo[bar]baz</mybcred>',
+ 'expected': 'red' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query text color',
+ 'qcvalue': 'forecolor',
+ 'tests': [
+ { 'id': 'FC_FONTc:f00-1_SI',
+ 'rte1-id': 'q-forecolor-0',
+ 'desc': 'query the "forecolor" value',
+ 'pad': '<font color="#ff0000">foo[bar]baz</font>',
+ 'expected': '#ff0000' },
+
+ { 'id': 'FC_SPANs:c:0f0-1_SI',
+ 'rte1-id': 'q-forecolor-1',
+ 'desc': 'query the "forecolor" value',
+ 'pad': '<span style="color: #00ff00">foo[bar]baz</span>',
+ 'expected': '#00ff00' },
+
+ { 'id': 'FC_FONTc:333.s:c:999-1_SI',
+ 'rte1-id': 'q-forecolor-2',
+ 'desc': 'query the "forecolor" value',
+ 'pad': '<font color="#333333" style="color: #999999">foo[bar]baz</font>',
+ 'expected': '#999999' },
+
+ { 'id': 'FC_FONTc:641-SPAN-1_SI',
+ 'desc': 'query the "forecolor" value, where the color was set on an ancestor',
+ 'pad': '<font color="#664411"><span>foo[bar]baz</span></font>',
+ 'expected': '#664411' },
+
+ { 'id': 'FC_SPANs:c:d95-SPAN-1_SI',
+ 'desc': 'query the "forecolor" value, where the color was set on an ancestor',
+ 'pad': '<span style="color: #dd9955"><span>foo[bar]baz</span></span>',
+ 'expected': '#dd9955' },
+
+ { 'id': 'FC_SPAN.red-1_SI',
+ 'desc': 'query the "forecolor" value',
+ 'pad': '<span class="red">foo[bar]baz</span>',
+ 'expected': 'red' },
+
+ { 'id': 'FC_MYRED-1-SI',
+ 'desc': 'query the "forecolor" value',
+ 'pad': '<myred>foo[bar]baz</myred>',
+ 'expected': 'red' }
+ ]
+ },
+
+ { 'desc': '[MIDAS] query hilight color (same as background color)',
+ 'qcvalue': 'hilitecolor',
+ 'tests': [
+ { 'id': 'HC_FONTs:bc:fc0-1_SI',
+ 'rte1-id': 'q-hilitecolor-0',
+ 'desc': 'query the "hilitecolor" value',
+ 'pad': '<font style="background-color: #ffcc00">foo[bar]baz</font>',
+ 'expected': '#ffcc00' },
+
+ { 'id': 'HC_SPANs:bc:a0c-1_SI',
+ 'rte1-id': 'q-hilitecolor-2',
+ 'desc': 'query the "hilitecolor" value',
+ 'pad': '<span style="background-color: #aa00cc">foo[bar]baz</span>',
+ 'expected': '#aa00cc' },
+
+ { 'id': 'HC_SPAN.ass.s:bc:rgb-1_SI',
+ 'rte1-id': 'q-hilitecolor-1',
+ 'desc': 'query the "hilitecolor" value',
+ 'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
+ 'expected': '#ff0000' },
+
+ { 'id': 'HC_FONTs:bc:83e-SPAN-1_SI',
+ 'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
+ 'pad': '<font style="background-color: #8833ee"><span>foo[bar]baz</span></font>',
+ 'expected': '#8833ee' },
+
+ { 'id': 'HC_SPANs:bc:b12-SPAN-1_SI',
+ 'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
+ 'pad': '<span style="background-color: #bb1122"><span>foo[bar]baz</span></span>',
+ 'expected': '#bb1122' },
+
+ { 'id': 'HC_SPAN.bcred-1_SI',
+ 'desc': 'query the "hilitecolor" value',
+ 'pad': '<span class="bcred">foo[bar]baz</span>',
+ 'expected': 'red' },
+
+ { 'id': 'HC_MYBCRED-1-SI',
+ 'desc': 'query the "hilitecolor" value',
+ 'pad': '<mybcred>foo[bar]baz</mybcred>',
+ 'expected': 'red' }
+ ]
+ }
+ ]
+}
+
+QUERYVALUE_TESTS_CSS = {
+ 'id': 'QVC',
+ 'caption': 'queryCommandValue Tests, using styleWithCSS',
+ 'checkAttrs': False,
+ 'checkStyle': False,
+ 'styleWithCSS': True,
+
+ 'Proposed': QUERYVALUE_TESTS['Proposed']
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/selection.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/selection.py
new file mode 100644
index 0000000000..6fa7e69a66
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/selection.py
@@ -0,0 +1,801 @@
+
+SELECTION_TESTS = {
+ 'id': 'S',
+ 'caption': 'Selection Tests',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': False,
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'selectall',
+ 'command': 'selectall',
+ 'tests': [
+ { 'id': 'SELALL_TEXT-1_SI',
+ 'desc': 'select all, text only',
+ 'pad': 'foo [bar] baz',
+ 'expected': [ '[foo bar baz]',
+ '{foo bar baz}' ] },
+
+ { 'id': 'SELALL_I-1_SI',
+ 'desc': 'select all, with outer tags',
+ 'pad': '<i>foo [bar] baz</i>',
+ 'expected': '{<i>foo bar baz</i>}' }
+ ]
+ },
+
+ { 'desc': 'unselect',
+ 'command': 'unselect',
+ 'tests': [
+ { 'id': 'UNSEL_TEXT-1_SI',
+ 'desc': 'unselect',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo bar baz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify (generic)',
+ 'tests': [
+ { 'id': 'SM:m.f.c_TEXT-1_SC-1',
+ 'desc': 'move caret 1 character forward',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo b^ar baz',
+ 'expected': 'foo ba^r baz' },
+
+ { 'id': 'SM:m.b.c_TEXT-1_SC-1',
+ 'desc': 'move caret 1 character backward',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo b^ar baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:m.f.c_TEXT-1_SI-1',
+ 'desc': 'move caret forward (sollapse selection)',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo bar^ baz' },
+
+ { 'id': 'SM:m.b.c_TEXT-1_SI-1',
+ 'desc': 'move caret backward (collapse selection)',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:m.f.w_TEXT-1_SC-1',
+ 'desc': 'move caret 1 word forward',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': 'foo b^ar baz',
+ 'expected': 'foo bar^ baz' },
+
+ { 'id': 'SM:m.f.w_TEXT-1_SC-2',
+ 'desc': 'move caret 1 word forward',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': 'foo^ bar baz',
+ 'expected': 'foo bar^ baz' },
+
+ { 'id': 'SM:m.f.w_TEXT-1_SI-1',
+ 'desc': 'move caret 1 word forward from non-collapsed selection',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo bar baz^' },
+
+ { 'id': 'SM:m.b.w_TEXT-1_SC-1',
+ 'desc': 'move caret 1 word backward',
+ 'function': 'sel.modify("move", "backward", "word");',
+ 'pad': 'foo b^ar baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:m.b.w_TEXT-1_SC-3',
+ 'desc': 'move caret 1 word backward',
+ 'function': 'sel.modify("move", "backward", "word");',
+ 'pad': 'foo bar ^baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:m.b.w_TEXT-1_SI-1',
+ 'desc': 'move caret 1 word backward from non-collapsed selection',
+ 'function': 'sel.modify("move", "backward", "word");',
+ 'pad': 'foo [bar] baz',
+ 'expected': '^foo bar baz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: move forward over combining diacritics, etc.',
+ 'tests': [
+ { 'id': 'SM:m.f.c_CHAR-2_SC-1',
+ 'desc': 'move 1 character forward over combined o with diaeresis',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^&#xF6;barbaz',
+ 'expected': 'fo&#xF6;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-3_SC-1',
+ 'desc': 'move 1 character forward over character with combining diaeresis above',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^o&#x0308;barbaz',
+ 'expected': 'foo&#x0308;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-4_SC-1',
+ 'desc': 'move 1 character forward over character with combining diaeresis below',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^o&#x0324;barbaz',
+ 'expected': 'foo&#x0324;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-5_SC-1',
+ 'desc': 'move 1 character forward over character with combining diaeresis above and below',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^o&#x0308;&#x0324;barbaz',
+ 'expected': 'foo&#x0308;&#x0324;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-5_SI-1',
+ 'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis above',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo[&#x0308;]&#x0324;barbaz',
+ 'expected': 'foo&#x0308;&#x0324;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-5_SI-2',
+ 'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis below',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo&#x0308;[&#x0324;]barbaz',
+ 'expected': 'foo&#x0308;&#x0324;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-5_SL',
+ 'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo[o&#x0308;]&#x0324;barbaz',
+ 'expected': 'foo&#x0308;&#x0324;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-5_SR',
+ 'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo&#x0308;[&#x0324;bar]baz',
+ 'expected': 'foo&#x0308;&#x0324;bar^baz' },
+
+ { 'id': 'SM:m.f.c_CHAR-6_SC-1',
+ 'desc': 'move 1 character forward over character with enclosing square',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^o&#x20DE;barbaz',
+ 'expected': 'foo&#x20DE;^barbaz' },
+
+ { 'id': 'SM:m.f.c_CHAR-7_SC-1',
+ 'desc': 'move 1 character forward over character with combining long solidus overlay',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'fo^o&#x0338;barbaz',
+ 'expected': 'foo&#x0338;^barbaz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: move backward over combining diacritics, etc.',
+ 'tests': [
+ { 'id': 'SM:m.b.c_CHAR-2_SC-1',
+ 'desc': 'move 1 character backward over combined o with diaeresis',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'fo&#xF6;^barbaz',
+ 'expected': 'fo^&#xF6;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-3_SC-1',
+ 'desc': 'move 1 character backward over character with combining diaeresis above',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0308;^barbaz',
+ 'expected': 'fo^o&#x0308;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-4_SC-1',
+ 'desc': 'move 1 character backward over character with combining diaeresis below',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0324;^barbaz',
+ 'expected': 'fo^o&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-5_SC-1',
+ 'desc': 'move 1 character backward over character with combining diaeresis above and below',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0308;&#x0324;^barbaz',
+ 'expected': 'fo^o&#x0308;&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-5_SI-1',
+ 'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis above',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo[&#x0308;]&#x0324;barbaz',
+ 'expected': 'fo^o&#x0308;&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-5_SI-2',
+ 'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis below',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0308;[&#x0324;]barbaz',
+ 'expected': 'fo^o&#x0308;&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-5_SL',
+ 'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'fo[o&#x0308;]&#x0324;barbaz',
+ 'expected': 'fo^o&#x0308;&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-5_SR',
+ 'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0308;[&#x0324;bar]baz',
+ 'expected': 'fo^o&#x0308;&#x0324;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-6_SC-1',
+ 'desc': 'move 1 character backward over character with enclosing square',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x20DE;^barbaz',
+ 'expected': 'fo^o&#x20DE;barbaz' },
+
+ { 'id': 'SM:m.b.c_CHAR-7_SC-1',
+ 'desc': 'move 1 character backward over character with combining long solidus overlay',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo&#x0338;^barbaz',
+ 'expected': 'fo^o&#x0338;barbaz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: move forward/backward/left/right in RTL text',
+ 'tests': [
+ { 'id': 'SM:m.f.c_Pdir:rtl-1_SC-1',
+ 'desc': 'move caret forward 1 character in right-to-left text',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': '<p dir="rtl">foo b^ar baz</p>',
+ 'expected': '<p dir="rtl">foo ba^r baz</p>' },
+
+ { 'id': 'SM:m.b.c_Pdir:rtl-1_SC-1',
+ 'desc': 'move caret backward 1 character in right-to-left text',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': '<p dir="rtl">foo ba^r baz</p>',
+ 'expected': '<p dir="rtl">foo b^ar baz</p>' },
+
+ { 'id': 'SM:m.r.c_Pdir:rtl-1_SC-1',
+ 'desc': 'move caret 1 character to the right in LTR text within RTL context',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': '<p dir="rtl">foo b^ar baz</p>',
+ 'expected': '<p dir="rtl">foo ba^r baz</p>' },
+
+ { 'id': 'SM:m.l.c_Pdir:rtl-1_SC-1',
+ 'desc': 'move caret 1 character to the left in LTR text within RTL context',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': '<p dir="rtl">foo ba^r baz</p>',
+ 'expected': '<p dir="rtl">foo b^ar baz</p>' },
+
+
+ { 'id': 'SM:m.f.c_TEXT:ar-1_SC-1',
+ 'desc': 'move caret forward 1 character in Arabic text',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
+ 'expected': '&#1605;&#x0631;&#1581;&#1576;^&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
+
+ { 'id': 'SM:m.b.c_TEXT:ar-1_SC-1',
+ 'desc': 'move caret backward 1 character in Arabic text',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
+ 'expected': '&#1605;&#x0631;^&#1581;&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
+
+ { 'id': 'SM:m.f.c_TEXT:he-1_SC-1',
+ 'desc': 'move caret forward 1 character in Hebrew text',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
+ 'expected': '&#x05E9;&#x05DC;&#x05D5;^&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
+
+ { 'id': 'SM:m.b.c_TEXT:he-1_SC-1',
+ 'desc': 'move caret backward 1 character in Hebrew text',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
+ 'expected': '&#x05E9;^&#x05DC;&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
+
+
+ { 'id': 'SM:m.f.c_BDOdir:rtl-1_SC-1',
+ 'desc': 'move caret forward 1 character inside <bdo>',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
+ 'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
+
+ { 'id': 'SM:m.b.c_BDOdir:rtl-1_SC-1',
+ 'desc': 'move caret backward 1 character inside <bdo>',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
+ 'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
+
+ { 'id': 'SM:m.r.c_BDOdir:rtl-1_SC-1',
+ 'desc': 'move caret 1 character to the right inside <bdo>',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
+ 'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
+
+ { 'id': 'SM:m.l.c_BDOdir:rtl-1_SC-1',
+ 'desc': 'move caret 1 character to the left inside <bdo>',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
+ 'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
+
+
+ { 'id': 'SM:m.f.c_TEXTrle-1_SC-rtl-1',
+ 'desc': 'move caret forward in RTL text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.b.c_TEXTrle-1_SC-rtl-1',
+ 'desc': 'move caret backward in RTL text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.r.c_TEXTrle-1_SC-rtl-1',
+ 'desc': 'move caret 1 character to the right in RTL text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.l.c_TEXTrle-1_SC-rtl-1',
+ 'desc': 'move caret 1 character to the left in RTL text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.f.c_TEXTrle-1_SC-ltr-1',
+ 'desc': 'move caret forward in LTR text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.b.c_TEXTrle-1_SC-ltr-1',
+ 'desc': 'move caret backward in LTR text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.r.c_TEXTrle-1_SC-ltr-1',
+ 'desc': 'move caret 1 character to the right in LTR text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.l.c_TEXTrle-1_SC-ltr-1',
+ 'desc': 'move caret 1 character to the left in LTR text within RLE-PDF marks',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+
+ { 'id': 'SM:m.f.c_TEXTrlo-1_SC-rtl-1',
+ 'desc': 'move caret forward in RTL text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.b.c_TEXTrlo-1_SC-rtl-1',
+ 'desc': 'move caret backward in RTL text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.r.c_TEXTrlo-1_SC-rtl-1',
+ 'desc': 'move caret 1 character to the right in RTL text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.l.c_TEXTrlo-1_SC-rtl-1',
+ 'desc': 'move caret 1 character to the left in RTL text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.f.c_TEXTrlo-1_SC-ltr-1',
+ 'desc': 'move caret forward in Latin text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.b.c_TEXTrlo-1_SC-ltr-1',
+ 'desc': 'move caret backward in Latin text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.r.c_TEXTrlo-1_SC-ltr-1',
+ 'desc': 'move caret 1 character to the right in Latin text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+ { 'id': 'SM:m.l.c_TEXTrlo-1_SC-ltr-1',
+ 'desc': 'move caret 1 character to the left in Latin text within RLO-PDF marks',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
+ 'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
+
+
+ { 'id': 'SM:m.f.c_TEXTrlm-1_SC-1',
+ 'desc': 'move caret forward in RTL text within neutral characters followed by RLM',
+ 'function': 'sel.modify("move", "forward", "character");',
+ 'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
+ 'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' },
+
+ { 'id': 'SM:m.b.c_TEXTrlm-1_SC-1',
+ 'desc': 'move caret backward in RTL text within neutral characters followed by RLM',
+ 'function': 'sel.modify("move", "backward", "character");',
+ 'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
+ 'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
+
+ { 'id': 'SM:m.r.c_TEXTrlm-1_SC-1',
+ 'desc': 'move caret 1 character to the right in RTL text within neutral characters followed by RLM',
+ 'function': 'sel.modify("move", "right", "character");',
+ 'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
+ 'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
+
+ { 'id': 'SM:m.l.c_TEXTrlm-1_SC-1',
+ 'desc': 'move caret 1 character to the left in RTL text within neutral characters followed by RLM',
+ 'function': 'sel.modify("move", "left", "character");',
+ 'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
+ 'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: move forward/backward over words in Japanese text',
+ 'tests': [
+ { 'id': 'SM:m.f.w_TEXT-jp_SC-1',
+ 'desc': 'move caret forward 1 word in Japanese text (adjective)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '^&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ 'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
+
+ { 'id': 'SM:m.f.w_TEXT-jp_SC-2',
+ 'desc': 'move caret forward 1 word in Japanese text (in the middle of a word)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '&#x9762;^&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ 'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
+
+ { 'id': 'SM:m.f.w_TEXT-jp_SC-3',
+ 'desc': 'move caret forward 1 word in Japanese text (noun)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ 'expected': [ '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;^&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' ] },
+
+ { 'id': 'SM:m.f.w_TEXT-jp_SC-4',
+ 'desc': 'move caret forward 1 word in Japanese text (Katakana)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ 'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
+
+ { 'id': 'SM:m.f.w_TEXT-jp_SC-5',
+ 'desc': 'move caret forward 1 word in Japanese text (verb)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
+ 'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;^&#x3002;' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: move forward/backward over words in Thai text',
+ 'tests': [
+ { 'id': 'SM:m.f.w_TEXT-th_SC-1',
+ 'desc': 'move caret forward 1 word in Thai text',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '^&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;',
+ 'expected': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;^&#x0E01;&#x0E32;&#x0E23;&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;' },
+
+ { 'id': 'SM:m.f.w_TEXT-th_SC-2',
+ 'desc': 'move caret forward 1 word in Thai text (mid-word)',
+ 'function': 'sel.modify("move", "forward", "word");',
+ 'pad': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;^&#x0E32;&#x0E23;&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;',
+ 'expected': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;^&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;' },
+
+ { 'id': 'SM:m.b.w_TEXT-th_SC-1',
+ 'desc': 'move caret backward 1 word in Thai text',
+ 'function': 'sel.modify("move", "backward", "word");',
+ 'pad': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;^&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;',
+ 'expected': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;^&#x0E01;&#x0E32;&#x0E23;&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;' },
+
+ { 'id': 'SM:m.b.w_TEXT-th_SC-2',
+ 'desc': 'move caret backward 1 word in Thai text (mid-word)',
+ 'function': 'sel.modify("move", "backward", "word");',
+ 'pad': '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;&#x0E17;&#x0E33;&#x0E07;&#x0E32;^&#x0E19;',
+ 'expected': [ '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;^&#x0E17;&#x0E33;&#x0E07;&#x0E32;&#x0E19;',
+ '&#x0E17;&#x0E14;&#x0E2A;&#x0E2D;&#x0E1A;&#x0E01;&#x0E32;&#x0E23;&#x0E17;&#x0E33;^&#x0E07;&#x0E32;&#x0E19;' ] },
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection forward',
+ 'tests': [
+ { 'id': 'SM:e.f.c_TEXT-1_SC-1',
+ 'desc': 'extend selection 1 character forward',
+ 'function': 'sel.modify("extend", "forward", "character");',
+ 'pad': 'foo ^bar baz',
+ 'expected': 'foo [b]ar baz' },
+
+ { 'id': 'SM:e.f.c_TEXT-1_SI-1',
+ 'desc': 'extend selection 1 character forward',
+ 'function': 'sel.modify("extend", "forward", "character");',
+ 'pad': 'foo [b]ar baz',
+ 'expected': 'foo [ba]r baz' },
+
+ { 'id': 'SM:e.f.w_TEXT-1_SC-1',
+ 'desc': 'extend selection 1 word forward',
+ 'function': 'sel.modify("extend", "forward", "word");',
+ 'pad': 'foo ^bar baz',
+ 'expected': 'foo [bar] baz' },
+
+ { 'id': 'SM:e.f.w_TEXT-1_SI-1',
+ 'desc': 'extend selection 1 word forward',
+ 'function': 'sel.modify("extend", "forward", "word");',
+ 'pad': 'foo [b]ar baz',
+ 'expected': 'foo [bar] baz' },
+
+ { 'id': 'SM:e.f.w_TEXT-1_SI-2',
+ 'desc': 'extend selection 1 word forward',
+ 'function': 'sel.modify("extend", "forward", "word");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo [bar baz]' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection backward, shrinking it',
+ 'tests': [
+ { 'id': 'SM:e.b.c_TEXT-1_SI-2',
+ 'desc': 'extend selection 1 character backward',
+ 'function': 'sel.modify("extend", "backward", "character");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo [ba]r baz' },
+
+ { 'id': 'SM:e.b.c_TEXT-1_SI-1',
+ 'desc': 'extend selection 1 character backward',
+ 'function': 'sel.modify("extend", "backward", "character");',
+ 'pad': 'foo [b]ar baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SI-3',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo [bar baz]',
+ 'expected': 'foo [bar] baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SI-2',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo [bar] baz',
+ 'expected': 'foo ^bar baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SI-4',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo b[ar baz]',
+ 'expected': 'foo b[ar] baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SI-5',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo b[ar] baz',
+ 'expected': 'foo b^ar baz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection backward, creating or extending a reverse selections',
+ 'tests': [
+ { 'id': 'SM:e.b.c_TEXT-1_SC-1',
+ 'desc': 'extend selection 1 character backward',
+ 'function': 'sel.modify("extend", "backward", "character");',
+ 'pad': 'foo b^ar baz',
+ 'expected': 'foo ]b[ar baz' },
+
+ { 'id': 'SM:e.b.c_TEXT-1_SIR-1',
+ 'desc': 'extend selection 1 character backward',
+ 'function': 'sel.modify("extend", "backward", "character");',
+ 'pad': 'foo b]a[r baz',
+ 'expected': 'foo ]ba[r baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SIR-1',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo b]a[r baz',
+ 'expected': 'foo ]ba[r baz' },
+
+ { 'id': 'SM:e.b.w_TEXT-1_SIR-2',
+ 'desc': 'extend selection 1 word backward',
+ 'function': 'sel.modify("extend", "backward", "word");',
+ 'pad': 'foo ]ba[r baz',
+ 'expected': ']foo ba[r baz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection forward, shrinking a reverse selections',
+ 'tests': [
+ { 'id': 'SM:e.f.c_TEXT-1_SIR-1',
+ 'desc': 'extend selection 1 character forward',
+ 'function': 'sel.modify("extend", "forward", "character");',
+ 'pad': 'foo b]a[r baz',
+ 'expected': 'foo ba^r baz' },
+
+ { 'id': 'SM:e.f.c_TEXT-1_SIR-2',
+ 'desc': 'extend selection 1 character forward',
+ 'function': 'sel.modify("extend", "forward", "character");',
+ 'pad': 'foo ]ba[r baz',
+ 'expected': 'foo b]a[r baz' },
+
+ { 'id': 'SM:e.f.w_TEXT-1_SIR-1',
+ 'desc': 'extend selection 1 word forward',
+ 'function': 'sel.modify("extend", "forward", "word");',
+ 'pad': 'foo ]ba[r baz',
+ 'expected': 'foo ba^r baz' },
+
+ { 'id': 'SM:e.f.w_TEXT-1_SIR-3',
+ 'desc': 'extend selection 1 word forward',
+ 'function': 'sel.modify("extend", "forward", "word");',
+ 'pad': ']foo ba[r baz',
+ 'expected': 'foo ]ba[r baz' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection forward to line boundary',
+ 'tests': [
+ { 'id': 'SM:e.f.lb_BR.BR-1_SC-1',
+ 'desc': 'extend selection forward to line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': 'fo^o<br>bar<br>baz',
+ 'expected': 'fo[o]<br>bar<br>baz' },
+
+ { 'id': 'SM:e.f.lb_BR.BR-1_SI-1',
+ 'desc': 'extend selection forward to next line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': 'fo[o]<br>bar<br>baz',
+ 'expected': 'fo[o<br>bar]<br>baz' },
+
+ { 'id': 'SM:e.f.lb_BR.BR-1_SM-1',
+ 'desc': 'extend selection forward to line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': 'fo[o<br>b]ar<br>baz',
+ 'expected': 'fo[o<br>bar]<br>baz' },
+
+ { 'id': 'SM:e.f.lb_P.P.P-1_SC-1',
+ 'desc': 'extend selection forward to line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': '<p>fo^o</p><p>bar</p><p>baz</p>',
+ 'expected': '<p>fo[o]</p><p>bar</p><p>baz</p>' },
+
+ { 'id': 'SM:e.f.lb_P.P.P-1_SI-1',
+ 'desc': 'extend selection forward to next line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': '<p>fo[o]</p><p>bar</p><p>baz</p>',
+ 'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
+
+ { 'id': 'SM:e.f.lb_P.P.P-1_SM-1',
+ 'desc': 'extend selection forward to line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': '<p>fo[o</p><p>b]ar</p><p>baz</p>',
+ 'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
+
+ { 'id': 'SM:e.f.lb_P.P.P-1_SMR-1',
+ 'desc': 'extend selection forward to line boundary',
+ 'function': 'sel.modify("extend", "forward", "lineboundary");',
+ 'pad': '<p>foo</p><p>b]a[r</p><p>baz</p>',
+ 'expected': '<p>foo</p><p>ba[r]</p><p>baz</p>' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection backward to line boundary',
+ 'tests': [
+ { 'id': 'SM:e.b.lb_BR.BR-1_SC-2',
+ 'desc': 'extend selection backward to line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': 'foo<br>bar<br>b^az',
+ 'expected': 'foo<br>bar<br>]b[az' },
+
+ { 'id': 'SM:e.b.lb_BR.BR-1_SIR-2',
+ 'desc': 'extend selection backward to previous line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': 'foo<br>bar<br>]b[az',
+ 'expected': 'foo<br>]bar<br>b[az' },
+
+ { 'id': 'SM:e.b.lb_BR.BR-1_SMR-2',
+ 'desc': 'extend selection backward to line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': 'foo<br>ba]r<br>b[az',
+ 'expected': 'foo<br>]bar<br>b[az' },
+
+ { 'id': 'SM:e.b.lb_P.P.P-1_SC-2',
+ 'desc': 'extend selection backward to line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': '<p>foo</p><p>bar</p><p>b^az</p>',
+ 'expected': '<p>foo</p><p>bar</p><p>]b[az</p>' },
+
+ { 'id': 'SM:e.b.lb_P.P.P-1_SIR-2',
+ 'desc': 'extend selection backward to previous line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': '<p>foo</p><p>bar</p><p>]b[az</p>',
+ 'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
+
+ { 'id': 'SM:e.b.lb_P.P.P-1_SMR-2',
+ 'desc': 'extend selection backward to line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': '<p>foo</p><p>ba]r</p><p>b[az</p>',
+ 'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
+
+ { 'id': 'SM:e.b.lb_P.P.P-1_SM-2',
+ 'desc': 'extend selection backward to line boundary',
+ 'function': 'sel.modify("extend", "backward", "lineboundary");',
+ 'pad': '<p>foo</p><p>b[a]r</p><p>baz</p>',
+ 'expected': '<p>foo</p><p>]b[ar</p><p>baz</p>' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection forward to next line (NOTE: use identical text in every line!)',
+ 'tests': [
+ { 'id': 'SM:e.f.l_BR.BR-2_SC-1',
+ 'desc': 'extend selection forward to next line',
+ 'function': 'sel.modify("extend", "forward", "line");',
+ 'pad': 'fo^o<br>foo<br>foo',
+ 'expected': 'fo[o<br>fo]o<br>foo' },
+
+ { 'id': 'SM:e.f.l_BR.BR-2_SI-1',
+ 'desc': 'extend selection forward to next line',
+ 'function': 'sel.modify("extend", "forward", "line");',
+ 'pad': 'fo[o]<br>foo<br>foo',
+ 'expected': 'fo[o<br>foo]<br>foo' },
+
+ { 'id': 'SM:e.f.l_BR.BR-2_SM-1',
+ 'desc': 'extend selection forward to next line',
+ 'function': 'sel.modify("extend", "forward", "line");',
+ 'pad': 'fo[o<br>f]oo<br>foo',
+ 'expected': 'fo[o<br>foo<br>f]oo' },
+
+ { 'id': 'SM:e.f.l_P.P-1_SC-1',
+ 'desc': 'extend selection forward to next line over paragraph boundaries',
+ 'function': 'sel.modify("extend", "forward", "line");',
+ 'pad': '<p>foo^bar</p><p>foobar</p>',
+ 'expected': '<p>foo[bar</p><p>foo]bar</p>' },
+
+ { 'id': 'SM:e.f.l_P.P-1_SMR-1',
+ 'desc': 'extend selection forward to next line over paragraph boundaries',
+ 'function': 'sel.modify("extend", "forward", "line");',
+ 'pad': '<p>fo]obar</p><p>foob[ar</p>',
+ 'expected': '<p>foobar</p><p>fo]ob[ar</p>' }
+ ]
+ },
+
+ { 'desc': 'sel.modify: extend selection backward to previous line (NOTE: use identical text in every line!)',
+ 'tests': [
+ { 'id': 'SM:e.b.l_BR.BR-2_SC-2',
+ 'desc': 'extend selection backward to previous line',
+ 'function': 'sel.modify("extend", "backward", "line");',
+ 'pad': 'foo<br>foo<br>f^oo',
+ 'expected': 'foo<br>f]oo<br>f[oo' },
+
+ { 'id': 'SM:e.b.l_BR.BR-2_SIR-2',
+ 'desc': 'extend selection backward to previous line',
+ 'function': 'sel.modify("extend", "backward", "line");',
+ 'pad': 'foo<br>foo<br>]f[oo',
+ 'expected': 'foo<br>]foo<br>f[oo' },
+
+ { 'id': 'SM:e.b.l_BR.BR-2_SMR-2',
+ 'desc': 'extend selection backward to previous line',
+ 'function': 'sel.modify("extend", "backward", "line");',
+ 'pad': 'foo<br>fo]o<br>f[oo',
+ 'expected': 'fo]o<br>foo<br>f[oo' },
+
+ { 'id': 'SM:e.b.l_P.P-1_SC-2',
+ 'desc': 'extend selection backward to next line over paragraph boundaries',
+ 'function': 'sel.modify("extend", "backward", "line");',
+ 'pad': '<p>foobar</p><p>foo^bar</p>',
+ 'expected': '<p>foo]bar</p><p>foo[bar</p>' },
+
+ { 'id': 'SM:e.b.l_P.P-1_SM-1',
+ 'desc': 'extend selection backward to next line over paragraph boundaries',
+ 'function': 'sel.modify("extend", "backward", "line");',
+ 'pad': '<p>fo[obar</p><p>foob]ar</p>',
+ 'expected': '<p>fo[ob]ar</p><p>foobar</p>' }
+ ]
+ },
+
+ { 'desc': 'sel.selectAllChildren(<element>)',
+ 'function': 'sel.selectAllChildren(doc.getElementById("div"));',
+ 'tests': [
+ { 'id': 'SAC:div_DIV-1_SC-1',
+ 'desc': 'selectAllChildren(<div>)',
+ 'pad': 'foo<div id="div">bar <span>ba^z</span></div>qoz',
+ 'expected': [ 'foo<div id="div">[bar <span>baz</span>}</div>qoz',
+ 'foo<div id="div">{bar <span>baz</span>}</div>qoz' ] },
+ ]
+ }
+ ]
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapply.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapply.py
new file mode 100644
index 0000000000..adad65617e
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapply.py
@@ -0,0 +1,462 @@
+
+UNAPPLY_TESTS = {
+ 'id': 'U',
+ 'caption': 'Unapply Existing Formatting Tests',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': False,
+ 'expected': 'foo[bar]baz',
+
+ 'RFC': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'remove link',
+ 'command': 'unlink',
+ 'tests': [
+ { 'id': 'UNLINK_A-1_SO',
+ 'desc': 'unlink wrapped <a> element',
+ 'pad': 'foo[<a>bar</a>]baz' },
+
+ { 'id': 'UNLINK_A-1_SW',
+ 'desc': 'unlink <a> element where the selection wraps the full content',
+ 'pad': 'foo<a>[bar]</a>baz' },
+
+ { 'id': 'UNLINK_An:a.h:id-1_SO',
+ 'desc': 'unlink wrapped <a> element that has a name and href attribute',
+ 'pad': 'foo[<a name="A" href="#UNLINK:An:a.h:id-1_SO">bar</a>]baz' },
+
+ { 'id': 'UNLINK_A-2_SO',
+ 'desc': 'unlink contained <a> element',
+ 'pad': 'foo[b<a>a</a>r]baz' },
+
+ { 'id': 'UNLINK_A2-1_SO',
+ 'desc': 'unlink 2 contained <a> elements',
+ 'pad': 'foo[<a>b</a>a<a>r</a>]baz' }
+ ]
+ }
+ ],
+
+ 'Proposed': [
+ { 'desc': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'remove bold',
+ 'command': 'bold',
+ 'tests': [
+ { 'id': 'B_B-1_SW',
+ 'rte1-id': 'u-bold-0',
+ 'desc': 'Selection within tags; remove <b> tags',
+ 'pad': 'foo<b>[bar]</b>baz' },
+
+ { 'id': 'B_B-1_SO',
+ 'desc': 'Selection outside of tags; remove <b> tags',
+ 'pad': 'foo[<b>bar</b>]baz' },
+
+ { 'id': 'B_B-1_SL',
+ 'desc': 'Selection oblique left; remove <b> tags',
+ 'pad': 'foo[<b>bar]</b>baz' },
+
+ { 'id': 'B_B-1_SR',
+ 'desc': 'Selection oblique right; remove <b> tags',
+ 'pad': 'foo<b>[bar</b>]baz' },
+
+ { 'id': 'B_STRONG-1_SW',
+ 'rte1-id': 'u-bold-1',
+ 'desc': 'Selection within tags; remove <strong> tags',
+ 'pad': 'foo<strong>[bar]</strong>baz' },
+
+ { 'id': 'B_STRONG-1_SO',
+ 'desc': 'Selection outside of tags; remove <strong> tags',
+ 'pad': 'foo[<strong>bar</strong>]baz' },
+
+ { 'id': 'B_STRONG-1_SL',
+ 'desc': 'Selection oblique left; remove <strong> tags',
+ 'pad': 'foo[<strong>bar]</strong>baz' },
+
+ { 'id': 'B_STRONG-1_SR',
+ 'desc': 'Selection oblique right; remove <strong> tags',
+ 'pad': 'foo<strong>[bar</strong>]baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SW',
+ 'rte1-id': 'u-bold-2',
+ 'desc': 'Selection within tags; remove "font-weight: bold"',
+ 'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SO',
+ 'desc': 'Selection outside of tags; remove "font-weight: bold"',
+ 'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SL',
+ 'desc': 'Selection oblique left; remove "font-weight: bold"',
+ 'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SR',
+ 'desc': 'Selection oblique right; remove "font-weight: bold"',
+ 'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' },
+
+ { 'id': 'B_B-P3-1_SO12',
+ 'desc': 'Unbolding multiple paragraphs in inside bolded content with content-model violation',
+ 'pad': '<b>{<p>foo</p><p>bar</p>}<p>baz</p></b>',
+ 'expected': [ '<p>[foo</p><p>bar]</p><p><b>baz</b></p>',
+ '<p>[foo</p><p>bar]</p><b><p>baz</p></b>' ] },
+
+ { 'id': 'B_B-P-I..P-1_SO-I',
+ 'desc': 'Unbolding italicized content inside bolded content with content-model violation',
+ 'pad': '<b><p>foo[<i>bar</i>]</p><p>baz</p></b>',
+ 'expected': [ '<p><b>foo</b><i>[bar]</i></p><p><b>baz</b></p>',
+ '<b><p>foo</p></b><p><i>[bar]</i></p><b><p>baz</p></b>' ] },
+
+ { 'id': 'B_B-2_SL',
+ 'desc': 'Remove partially covered bold, selection extends left',
+ 'pad': 'foo [bar <b>baz] qoz</b> quz sic',
+ 'expected': 'foo [bar baz]<b> qoz</b> quz sic' },
+
+ { 'id': 'B_B-2_SR',
+ 'desc': 'Remove partially covered bold, selection extends right',
+ 'pad': 'foo bar <b>baz [qoz</b> quz] sic',
+ 'expected': 'foo bar <b>baz </b>[qoz quz] sic' }
+ ]
+ },
+
+ { 'desc': 'remove italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_I-1_SW',
+ 'rte1-id': 'u-italic-0',
+ 'desc': 'Selection within tags; remove <i> tags',
+ 'pad': 'foo<i>[bar]</i>baz' },
+
+ { 'id': 'I_I-1_SO',
+ 'desc': 'Selection outside of tags; remove <i> tags',
+ 'pad': 'foo[<i>bar</i>]baz' },
+
+ { 'id': 'I_I-1_SL',
+ 'desc': 'Selection oblique left; remove <i> tags',
+ 'pad': 'foo[<i>bar]</i>baz' },
+
+ { 'id': 'I_I-1_SR',
+ 'desc': 'Selection oblique right; remove <i> tags',
+ 'pad': 'foo<i>[bar</i>]baz' },
+
+ { 'id': 'I_EM-1_SW',
+ 'rte1-id': 'u-italic-1',
+ 'desc': 'Selection within tags; remove <em> tags',
+ 'pad': 'foo<em>[bar]</em>baz' },
+
+ { 'id': 'I_EM-1_SO',
+ 'desc': 'Selection outside of tags; remove <em> tags',
+ 'pad': 'foo[<em>bar</em>]baz' },
+
+ { 'id': 'I_EM-1_SL',
+ 'desc': 'Selection oblique left; remove <em> tags',
+ 'pad': 'foo[<em>bar]</em>baz' },
+
+ { 'id': 'I_EM-1_SR',
+ 'desc': 'Selection oblique right; remove <em> tags',
+ 'pad': 'foo<em>[bar</em>]baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SW',
+ 'rte1-id': 'u-italic-2',
+ 'desc': 'Selection within tags; remove "font-style: italic"',
+ 'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SO',
+ 'desc': 'Selection outside of tags; Italicize "font-style: italic"',
+ 'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SL',
+ 'desc': 'Selection oblique left; Italicize "font-style: italic"',
+ 'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SR',
+ 'desc': 'Selection oblique right; Italicize "font-style: italic"',
+ 'pad': 'foo<span style="font-style: italic">[bar</span>]baz' },
+
+ { 'id': 'I_I-P3-1_SO2',
+ 'desc': 'Unitalicize content with content-model violation',
+ 'pad': '<i><p>foo</p>{<p>bar</p>}<p>baz</p></i>',
+ 'expected': [ '<p><i>foo</i></p><p>[bar]</p><p><i>baz</i></p>',
+ '<i><p>foo</p></i><p>[bar]</p><i><p>baz</p></i>' ] }
+ ]
+ },
+
+ { 'desc': 'remove underline',
+ 'command': 'underline',
+ 'tests': [
+ { 'id': 'U_U-1_SW',
+ 'rte1-id': 'u-underline-0',
+ 'desc': 'Selection within tags; remove <u> tags',
+ 'pad': 'foo<u>[bar]</u>baz' },
+
+ { 'id': 'U_U-1_SO',
+ 'desc': 'Selection outside of tags; remove <u> tags',
+ 'pad': 'foo[<u>bar</u>]baz' },
+
+ { 'id': 'U_U-1_SL',
+ 'desc': 'Selection oblique left; remove <u> tags',
+ 'pad': 'foo[<u>bar]</u>baz' },
+
+ { 'id': 'U_U-1_SR',
+ 'desc': 'Selection oblique right; remove <u> tags',
+ 'pad': 'foo<u>[bar</u>]baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SW',
+ 'rte1-id': 'u-underline-1',
+ 'desc': 'Selection within tags; remove "text-decoration: underline"',
+ 'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SO',
+ 'desc': 'Selection outside of tags; remove "text-decoration: underline"',
+ 'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SL',
+ 'desc': 'Selection oblique left; remove "text-decoration: underline"',
+ 'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SR',
+ 'desc': 'Selection oblique right; remove "text-decoration: underline"',
+ 'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' },
+
+ { 'id': 'U_U-S-1_SO',
+ 'desc': 'Removing underline from underlined content with striked content',
+ 'pad': '<u>foo[bar<s>baz</s>quoz]</u>',
+ 'expected': '<u>foo</u>[bar<s>baz</s>quoz]' },
+
+ { 'id': 'U_U-S-2_SI',
+ 'desc': 'Removing underline from striked content inside underlined content',
+ 'pad': '<u><s>foo[bar]baz</s>quoz</u>',
+ 'expected': '<s><u>foo</u>[bar]<u>baz</u>quoz</s>' },
+
+ { 'id': 'U_U-P3-1_SO',
+ 'desc': 'Removing underline from underlined content with content-model violation',
+ 'pad': '<u><p>foo</p>{<p>bar</p>}<p>baz</p></u>',
+ 'expected': [ '<p><u>foo</u></p><p>[bar]</p><p><u>baz</u></p>',
+ '<u><p>foo</p></u><p>[bar]</p><u><p>baz</p></u>' ] }
+ ]
+ },
+
+ { 'desc': 'remove strike through',
+ 'command': 'strikethrough',
+ 'tests': [
+ { 'id': 'S_S-1_SW',
+ 'rte1-id': 'u-strikethrough-1',
+ 'desc': 'Selection within tags; remove <s> tags',
+ 'pad': 'foo<s>[bar]</s>baz' },
+
+ { 'id': 'S_S-1_SO',
+ 'desc': 'Selection outside of tags; remove <s> tags',
+ 'pad': 'foo[<s>bar</s>]baz' },
+
+ { 'id': 'S_S-1_SL',
+ 'desc': 'Selection oblique left; remove <s> tags',
+ 'pad': 'foo[<s>bar]</s>baz' },
+
+ { 'id': 'S_S-1_SR',
+ 'desc': 'Selection oblique right; remove <s> tags',
+ 'pad': 'foo<s>[bar</s>]baz' },
+
+ { 'id': 'S_STRIKE-1_SW',
+ 'rte1-id': 'u-strikethrough-0',
+ 'desc': 'Selection within tags; remove <strike> tags',
+ 'pad': 'foo<strike>[bar]</strike>baz' },
+
+ { 'id': 'S_STRIKE-1_SO',
+ 'desc': 'Selection outside of tags; remove <strike> tags',
+ 'pad': 'foo[<strike>bar</strike>]baz' },
+
+ { 'id': 'S_STRIKE-1_SL',
+ 'desc': 'Selection oblique left; remove <strike> tags',
+ 'pad': 'foo[<strike>bar]</strike>baz' },
+
+ { 'id': 'S_STRIKE-2_SR',
+ 'desc': 'Selection oblique right; remove <strike> tags',
+ 'pad': 'foo<strike>[bar</strike>]baz' },
+
+ { 'id': 'S_DEL-1_SW',
+ 'rte1-id': 'u-strikethrough-2',
+ 'desc': 'Selection within tags; remove <del> tags',
+ 'pad': 'foo<del>[bar]</del>baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SW',
+ 'rte1-id': 'u-strikethrough-3',
+ 'desc': 'Selection within tags; remove "text-decoration:line-through"',
+ 'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SO',
+ 'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
+ 'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SL',
+ 'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
+ 'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SR',
+ 'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
+ 'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
+
+ { 'id': 'S_S-U-1_SI',
+ 'desc': 'Removing underline from underlined content inside striked content',
+ 'pad': '<s><u>foo[bar]baz</u>quoz</s>',
+ 'expected': '<s><u>foo</u></s><u>[bar]</u><s><u>baz</u>quoz</s>' },
+
+ { 'id': 'S_U-S-1_SI',
+ 'desc': 'Removing underline from striked content inside underlined content',
+ 'pad': '<u><s>foo[bar]baz</s>quoz</u>',
+ 'expected': '<u><s>foo</s>[bar]<s>baz</s>quoz</u>' }
+ ]
+ },
+
+ { 'desc': 'remove subscript',
+ 'command': 'subscript',
+ 'tests': [
+ { 'id': 'SUB_SUB-1_SW',
+ 'rte1-id': 'u-subscript-0',
+ 'desc': 'remove subscript',
+ 'pad': 'foo<sub>[bar]</sub>baz' },
+
+ { 'id': 'SUB_SPANs:va:sub-1_SW',
+ 'rte1-id': 'u-subscript-1',
+ 'desc': 'remove subscript',
+ 'pad': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': 'remove superscript',
+ 'command': 'superscript',
+ 'tests': [
+ { 'id': 'SUP_SUP-1_SW',
+ 'rte1-id': 'u-superscript-0',
+ 'desc': 'remove superscript',
+ 'pad': 'foo<sup>[bar]</sup>baz' },
+
+ { 'id': 'SUP_SPANs:va:super-1_SW',
+ 'rte1-id': 'u-superscript-1',
+ 'desc': 'remove superscript',
+ 'pad': 'foo<span style="vertical-align: super">[bar]</span>baz' }
+ ]
+ },
+
+ { 'desc': 'remove links',
+ 'command': 'unlink',
+ 'tests': [
+ { 'id': 'UNLINK_Ahref:url-1_SW',
+ 'rte1-id': 'u-unlink-0',
+ 'desc': 'unlink an <a> element with href attribute where all children are selected',
+ 'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
+
+ { 'id': 'UNLINK_A-1_SC',
+ 'desc': 'unlink an <a> element that contains the collapsed selection',
+ 'pad': 'foo<a>ba^r</a>baz',
+ 'expected': 'fooba^rbaz' },
+
+ { 'id': 'UNLINK_A-1_SI',
+ 'desc': 'unlink an <a> element that contains the whole selection',
+ 'pad': 'foo<a>b[a]r</a>baz',
+ 'expected': 'foob[a]rbaz' },
+
+ { 'id': 'UNLINK_A-2_SL',
+ 'desc': 'unlink a partially contained <a> element',
+ 'pad': 'foo[ba<a>r]ba</a>z' },
+
+ { 'id': 'UNLINK_A-3_SR',
+ 'desc': 'unlink a partially contained <a> element',
+ 'pad': 'fo<a>o[ba</a>r]baz' },
+
+ { 'id': 'UNLINK_As:d:b.fw:b-1_SW',
+ 'desc': 'unlink, preserving styles',
+ 'pad': 'foo<a href="#" style="display: block; font-weight: bold">[bar]</a>baz',
+ 'expected': 'foo<span style="display: block; font-weight: bold">[bar]</span>baz' },
+
+ { 'id': 'UNLINK_A-IMG-1_SO',
+ 'desc': 'unlink a linked image at the start of the content',
+ 'pad': '{<a href="#"><img src="pic.jpg" align="right" height="140" width="200"></a>abc]',
+ 'expected': '{<img src="pic.jpg" align="right" height="140" width="200">abc]' }
+ ]
+ },
+
+ { 'desc': 'outdent',
+ 'command': 'outdent',
+ 'tests': [
+ { 'id': 'OUTDENT_BQ-1_SW',
+ 'rte1-id': 'u-outdent-0',
+ 'desc': 'outdent (remove) a <blockquote>',
+ 'pad': 'foo<blockquote>[bar]</blockquote>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' },
+
+ { 'id': 'OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW',
+ 'rte1-id': 'u-outdent-1',
+ 'desc': 'outdent (remove) a styled <blockquote>',
+ 'pad': 'foo<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px">[bar]</blockquote>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' },
+
+ { 'id': 'OUTDENT_OL-LI-1_SW',
+ 'rte1-id': 'u-outdent-3',
+ 'desc': 'outdent (remove) an ordered list',
+ 'pad': 'foo<ol><li>[bar]</li></ol>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' },
+
+ { 'id': 'OUTDENT_UL-LI-1_SW',
+ 'rte1-id': 'u-outdent-2',
+ 'desc': 'outdent (remove) an unordered list',
+ 'pad': 'foo<ul><li>[bar]</li></ul>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' },
+
+ { 'id': 'OUTDENT_DIV-1_SW',
+ 'rte1-id': 'u-outdent-4',
+ 'desc': 'outdent (remove) a styled <div> with margin',
+ 'pad': 'foo<div style="margin-left: 40px;">[bar]</div>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' }
+ ]
+ },
+
+ { 'desc': 'remove all formatting',
+ 'command': 'removeformat',
+ 'tests': [
+ { 'id': 'REMOVEFORMAT_B-1_SW',
+ 'rte1-id': 'u-removeformat-0',
+ 'desc': 'remove a <b> tag using "removeformat"',
+ 'pad': 'foo<b>[bar]</b>baz' },
+
+ { 'id': 'REMOVEFORMAT_Ahref:url-1_SW',
+ 'rte1-id': 'u-removeformat-0',
+ 'desc': 'remove a link using "removeformat"',
+ 'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
+
+ { 'id': 'REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW',
+ 'rte1-id': 'u-removeformat-2',
+ 'desc': 'remove a table using "removeformat"',
+ 'pad': 'foo<table><tbody><tr><td>[bar]</td></tr></tbody></table>baz',
+ 'expected': [ 'foo<p>[bar]</p>baz',
+ 'foo<div>[bar]</div>baz' ],
+ 'accept': 'foo<br>[bar]<br>baz' }
+ ]
+ },
+
+ { 'desc': 'remove bookmark',
+ 'command': 'unbookmark',
+ 'tests': [
+ { 'id': 'UNBOOKMARK_An:name-1_SW',
+ 'rte1-id': 'u-unbookmark-0',
+ 'desc': 'unlink a bookmark (a named <a> element) where all children are selected',
+ 'pad': 'foo<a name="bookmark">[bar]</a>baz' }
+ ]
+ }
+ ]
+}
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapplyCSS.py b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapplyCSS.py
new file mode 100644
index 0000000000..6f934a0f02
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/tests/unapplyCSS.py
@@ -0,0 +1,226 @@
+
+UNAPPLY_TESTS_CSS = {
+ 'id': 'UC',
+ 'caption': 'Unapply Existing Formatting Tests, using styleWithCSS',
+ 'checkAttrs': True,
+ 'checkStyle': True,
+ 'styleWithCSS': True,
+ 'expected': 'foo[bar]baz',
+
+ 'Proposed': [
+ { 'desc': '',
+ 'id': '',
+ 'command': '',
+ 'tests': [
+ ]
+ },
+
+ { 'desc': 'remove bold',
+ 'command': 'bold',
+ 'tests': [
+ { 'id': 'B_B-1_SW',
+ 'desc': 'Selection within tags; remove <b> tags',
+ 'pad': 'foo<b>[bar]</b>baz' },
+
+ { 'id': 'B_B-1_SO',
+ 'desc': 'Selection outside of tags; remove <b> tags',
+ 'pad': 'foo[<b>bar</b>]baz' },
+
+ { 'id': 'B_B-1_SL',
+ 'desc': 'Selection oblique left; remove <b> tags',
+ 'pad': 'foo[<b>bar]</b>baz' },
+
+ { 'id': 'B_B-1_SR',
+ 'desc': 'Selection oblique right; remove <b> tags',
+ 'pad': 'foo<b>[bar</b>]baz' },
+
+ { 'id': 'B_STRONG-1_SW',
+ 'desc': 'Selection within tags; remove <strong> tags',
+ 'pad': 'foo<strong>[bar]</strong>baz' },
+
+ { 'id': 'B_STRONG-1_SO',
+ 'desc': 'Selection outside of tags; remove <strong> tags',
+ 'pad': 'foo[<strong>bar</strong>]baz' },
+
+ { 'id': 'B_STRONG-1_SL',
+ 'desc': 'Selection oblique left; remove <strong> tags',
+ 'pad': 'foo[<strong>bar]</strong>baz' },
+
+ { 'id': 'B_STRONG-1_SR',
+ 'desc': 'Selection oblique right; remove <strong> tags',
+ 'pad': 'foo<strong>[bar</strong>]baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SW',
+ 'desc': 'Selection within tags; remove "font-weight: bold"',
+ 'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SO',
+ 'desc': 'Selection outside of tags; remove "font-weight: bold"',
+ 'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SL',
+ 'desc': 'Selection oblique left; remove "font-weight: bold"',
+ 'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
+
+ { 'id': 'B_SPANs:fw:b-1_SR',
+ 'desc': 'Selection oblique right; remove "font-weight: bold"',
+ 'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' }
+ ]
+ },
+
+ { 'desc': 'remove italic',
+ 'command': 'italic',
+ 'tests': [
+ { 'id': 'I_I-1_SW',
+ 'desc': 'Selection within tags; remove <i> tags',
+ 'pad': 'foo<i>[bar]</i>baz' },
+
+ { 'id': 'I_I-1_SO',
+ 'desc': 'Selection outside of tags; remove <i> tags',
+ 'pad': 'foo[<i>bar</i>]baz' },
+
+ { 'id': 'I_I-1_SL',
+ 'desc': 'Selection oblique left; remove <i> tags',
+ 'pad': 'foo[<i>bar]</i>baz' },
+
+ { 'id': 'I_I-1_SR',
+ 'desc': 'Selection oblique right; remove <i> tags',
+ 'pad': 'foo<i>[bar</i>]baz' },
+
+ { 'id': 'I_EM-1_SW',
+ 'desc': 'Selection within tags; remove <em> tags',
+ 'pad': 'foo<em>[bar]</em>baz' },
+
+ { 'id': 'I_EM-1_SO',
+ 'desc': 'Selection outside of tags; remove <em> tags',
+ 'pad': 'foo[<em>bar</em>]baz' },
+
+ { 'id': 'I_EM-1_SL',
+ 'desc': 'Selection oblique left; remove <em> tags',
+ 'pad': 'foo[<em>bar]</em>baz' },
+
+ { 'id': 'I_EM-1_SR',
+ 'desc': 'Selection oblique right; remove <em> tags',
+ 'pad': 'foo<em>[bar</em>]baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SW',
+ 'desc': 'Selection within tags; remove "font-style: italic"',
+ 'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SO',
+ 'desc': 'Selection outside of tags; Italicize "font-style: italic"',
+ 'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SL',
+ 'desc': 'Selection oblique left; Italicize "font-style: italic"',
+ 'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
+
+ { 'id': 'I_SPANs:fs:i-1_SR',
+ 'desc': 'Selection oblique right; Italicize "font-style: italic"',
+ 'pad': 'foo<span style="font-style: italic">[bar</span>]baz' }
+ ]
+ },
+
+ { 'desc': 'remove underline',
+ 'command': 'underline',
+ 'tests': [
+ { 'id': 'U_U-1_SW',
+ 'desc': 'Selection within tags; remove <u> tags',
+ 'pad': 'foo<u>[bar]</u>baz' },
+
+ { 'id': 'U_U-1_SO',
+ 'desc': 'Selection outside of tags; remove <u> tags',
+ 'pad': 'foo[<u>bar</u>]baz' },
+
+ { 'id': 'U_U-1_SL',
+ 'desc': 'Selection oblique left; remove <u> tags',
+ 'pad': 'foo[<u>bar]</u>baz' },
+
+ { 'id': 'U_U-1_SR',
+ 'desc': 'Selection oblique right; remove <u> tags',
+ 'pad': 'foo<u>[bar</u>]baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SW',
+ 'desc': 'Selection within tags; remove "text-decoration: underline"',
+ 'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SO',
+ 'desc': 'Selection outside of tags; remove "text-decoration: underline"',
+ 'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SL',
+ 'desc': 'Selection oblique left; remove "text-decoration: underline"',
+ 'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
+
+ { 'id': 'U_SPANs:td:u-1_SR',
+ 'desc': 'Selection oblique right; remove "text-decoration: underline"',
+ 'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' }
+ ]
+ },
+
+ { 'desc': 'remove strike-through',
+ 'command': 'strikethrough',
+ 'tests': [
+ { 'id': 'S_S-1_SW',
+ 'desc': 'Selection within tags; remove <s> tags',
+ 'pad': 'foo<s>[bar]</s>baz' },
+
+ { 'id': 'S_S-1_SO',
+ 'desc': 'Selection outside of tags; remove <s> tags',
+ 'pad': 'foo[<s>bar</s>]baz' },
+
+ { 'id': 'S_S-1_SL',
+ 'desc': 'Selection oblique left; remove <s> tags',
+ 'pad': 'foo[<s>bar]</s>baz' },
+
+ { 'id': 'S_S-1_SR',
+ 'desc': 'Selection oblique right; remove <s> tags',
+ 'pad': 'foo<s>[bar</s>]baz' },
+
+ { 'id': 'S_STRIKE-1_SW',
+ 'desc': 'Selection within tags; remove <strike> tags',
+ 'pad': 'foo<strike>[bar]</strike>baz' },
+
+ { 'id': 'S_STRIKE-1_SO',
+ 'desc': 'Selection outside of tags; remove <strike> tags',
+ 'pad': 'foo[<strike>bar</strike>]baz' },
+
+ { 'id': 'S_STRIKE-1_SL',
+ 'desc': 'Selection oblique left; remove <strike> tags',
+ 'pad': 'foo[<strike>bar]</strike>baz' },
+
+ { 'id': 'S_STRIKE-1_SR',
+ 'desc': 'Selection oblique right; remove <strike> tags',
+ 'pad': 'foo<strike>[bar</strike>]baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SW',
+ 'desc': 'Selection within tags; remove "text-decoration:line-through"',
+ 'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SO',
+ 'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
+ 'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SL',
+ 'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
+ 'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
+
+ { 'id': 'S_SPANs:td:lt-1_SR',
+ 'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
+ 'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
+
+ { 'id': 'S_SPANc:s-1_SW',
+ 'desc': 'Unapply "strike-through" on interited CSS style',
+ 'checkClass': True,
+ 'pad': 'foo<span class="s">[bar]</span>baz' },
+
+ { 'id': 'S_SPANc:s-2_SI',
+ 'desc': 'Unapply "strike-through" on interited CSS style',
+ 'pad': '<span class="s">foo[bar]baz</span>',
+ 'checkClass': True,
+ 'expected': '<span class="s">foo</span>[bar]<span class="s">baz</span>' }
+ ]
+ }
+ ]
+}
+
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/unittestexample.html b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/unittestexample.html
new file mode 100644
index 0000000000..4e27b05540
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/richtext2/unittestexample.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
+ <title>Rich Text 2 Unit Test Example</title>
+
+ <!-- utility scripts -->
+ <script type="text/javascript" src="static/js/variables.js"></script>
+ <script type="text/javascript" src="static/js/canonicalize.js"></script>
+ <script type="text/javascript" src="static/js/compare.js"></script>
+ <script type="text/javascript" src="static/js/pad.js"></script>
+ <script type="text/javascript" src="static/js/range.js"></script>
+ <script type="text/javascript" src="static/js/units.js"></script>
+ <script type="text/javascript" src="static/js/run.js"></script>
+ <!-- you do not need static/js/output.js -->
+
+ <!--
+ Tests - note that those have the extensions .py,
+ but can be used as JS files directly.
+ -->
+ <script type="text/javascript" src="tests/selection.py"></script>
+ <script type="text/javascript" src="tests/apply.py"></script>
+ <script type="text/javascript" src="tests/applyCSS.py"></script>
+ <script type="text/javascript" src="tests/change.py"></script>
+ <script type="text/javascript" src="tests/changeCSS.py"></script>
+ <script type="text/javascript" src="tests/unapply.py"></script>
+ <script type="text/javascript" src="tests/unapplyCSS.py"></script>
+ <script type="text/javascript" src="tests/delete.py"></script>
+ <script type="text/javascript" src="tests/forwarddelete.py"></script>
+ <script type="text/javascript" src="tests/insert.py"></script>
+ <script type="text/javascript" src="tests/querySupported.py"></script>
+ <script type="text/javascript" src="tests/queryEnabled.py"></script>
+ <script type="text/javascript" src="tests/queryIndeterm.py"></script>
+ <script type="text/javascript" src="tests/queryState.py"></script>
+ <script type="text/javascript" src="tests/queryValue.py"></script>
+
+ <!-- Do something -->
+ <script type="text/javascript">
+ function runTest() {
+ initVariables();
+ initEditorDocs();
+
+ runTestSuite(UNAPPLY_TESTS);
+
+ // Below alert is just a simple demonstration on how to access the test results.
+ // Note that we only ran UNAPPLY tests above, so we have only results from that test set.
+ //
+ // The 'results' structure is as follows:
+ //
+ // results structure containing all results
+ // [<suite ID>] structure containing the results for the given suite *)
+ // .count number of tests in the given suite
+ // .valscore sum of all test value results (HTML or query value)
+ // .selscore sum of all selection results (HTML tests only)
+ // [<class ID>] structure containing the results for the given class **)
+ // .count number of tests in the given suite
+ // .valscore sum of all test value results (HTML or query value)
+ // .selscore sum of all selection results (HTML tests only)
+ // [<test ID>] structure containing the reults for a given test ***)
+ // .valscore value score (0 or 1), minimum over all containers
+ // .selscore selection score (0 or 1), minimum over all containers (HTML tests only)
+ // .valresult worst test value result (integer, see variables.js)
+ // .selresult worst selection result (integer, see variables.js)
+ // [<cont. ID>] structure containing the results of the test for a given container ****)
+ // .valscore value score (0 or 1)
+ // .selscore selection score (0 or 1)
+ // .valresult value result (integer, see variables.js)
+ // .selresult selection result (integer, see variables.js)
+ // .output output string (mainly for use by the online version)
+ // .innerHTML inner HTML of the testing container (<div> or <body>) after the test
+ // .outerHTML outer HTML of the testing container (<div> or <body>) after the test
+ // .bodyInnerHTML inner HTML of the <body> after the test
+ // .bodyOuterHTML outer HTML of the <body> after the test
+ //
+ // *) <suite ID>: a 1-3 character ID, e.g. UNAPPLY_TESTS.id, or 'U' (both referring the same suite)
+ // **) <class ID>: one of 'Proposed', 'RFC' or 'Finalized'
+ // ***) <test ID>: the ID of the test, without the leading 'RTE2-<suite ID>_' part
+ // ****) <container ID>: one of 'div' (test within a <div contenteditable="true">)
+ // 'dM' (test with designMode = 'on')
+ // 'body' (test within a <body contenteditable="true">)
+
+ alert("Result of 'Apply' tests:\nOut of " +
+ results[UNAPPLY_TESTS.id].count + " tests\n" +
+ results[UNAPPLY_TESTS.id].valscore + " had correct HTML, and\n" +
+ results[UNAPPLY_TESTS.id].selscore + " had a correct result selection\n(in all testing containers)." +
+ "\n\n" +
+ "Test RTE2-U_B_B-1_SW results with a contenteditable <body>:\n" +
+ results['U']['Proposed']['B_B-1_SW']['body'].valscore + " points for the value result, and\n" +
+ results['U']['Proposed']['B_B-1_SW']['body'].selscore + " points for the selection" +
+ ""
+ );
+ }
+ </script>
+</head>
+
+<body onload="runTest()">
+ <iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
+ <iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
+ <iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
+</body>
+</html>
diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/update_from_upstream b/editor/libeditor/tests/browserscope/lib/richtext2/update_from_upstream
new file mode 100644
index 0000000000..baeb767454
--- /dev/null
+++ b/editor/libeditor/tests/browserscope/lib/richtext2/update_from_upstream
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -x
+
+if test -d richtext2; then
+ rm -drf richtext2;
+fi
+
+svn checkout http://browserscope.googlecode.com/svn/trunk/categories/richtext2 richtext2 | tail -1 | sed 's/[^0-9]//g' > current_revision
+
+find richtext2 -type d -name .svn -exec rm -drf \{\} \; 2> /dev/null
+
+# Remove test_set.py and other similarly named files because they confuse our mochitest runner
+find richtext2 =type f -name test_\* -exec rm -rf \{\} \; 2> /dev/null
+
+hg add current_revision richtext2
+
+hg stat .
+